1 /* vi: set sw=4 ts=4: */
2 /* i386 ELF shared library loader suppport
4 * Copyright (C) 2001-2002, David A. Schleef
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. The name of the above contributors may not be
14 * used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #ifndef VERBOSE_DLINKER
31 #define VERBOSE_DLINKER
33 #ifdef VERBOSE_DLINKER
34 static const char *_dl_reltypes[] =
35 { "R_PPC_NONE", "R_PPC_ADDR32", "R_PPC_ADDR24", "R_PPC_ADDR16",
36 "R_PPC_ADDR16_LO", "R_PPC_ADDR16_HI", "R_PPC_ADDR16_HA",
37 "R_PPC_ADDR14", "R_PPC_ADDR14_BRTAKEN", "R_PPC_ADDR14_BRNTAKEN",
38 "R_PPC_REL24", "R_PPC_REL14", "R_PPC_REL14_BRTAKEN",
39 "R_PPC_REL14_BRNTAKEN", "R_PPC_GOT16", "R_PPC_GOT16_LO",
40 "R_PPC_GOT16_HI", "R_PPC_GOT16_HA", "R_PPC_PLTREL24",
41 "R_PPC_COPY", "R_PPC_GLOB_DAT", "R_PPC_JMP_SLOT", "R_PPC_RELATIVE",
42 "R_PPC_LOCAL24PC", "R_PPC_UADDR32", "R_PPC_UADDR16", "R_PPC_REL32",
43 "R_PPC_PLT32", "R_PPC_PLTREL32", "R_PPC_PLT16_LO", "R_PPC_PLT16_HI",
44 "R_PPC_PLT16_HA", "R_PPC_SDAREL16", "R_PPC_SECTOFF",
45 "R_PPC_SECTOFF_LO", "R_PPC_SECTOFF_HI", "R_PPC_SECTOFF_HA",
47 #define N_RELTYPES (sizeof(_dl_reltypes)/sizeof(_dl_reltypes[0]))
50 /* Program to load an ELF binary on a linux system, and run it.
51 References to symbols in sharable libraries can be resolved by either
52 an ELF sharable library or a linux style of shared library. */
54 /* Disclaimer: I have never seen any AT&T source code for SVr4, nor have
55 I ever taken any courses on internals. This program was developed using
56 information available through the book "UNIX SYSTEM V RELEASE 4,
57 Programmers guide: Ansi C and Programming Support Tools", which did
58 a more than adequate job of explaining everything required to get this
62 #ifdef LD_DEBUG_SYMBOLS
63 static void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index);
64 static void debug_reloc(ELF_RELOC *rpnt);
65 #define DPRINTF(fmt,args...) _dl_dprintf(2,fmt,args)
67 #define debug_sym(a,b,c)
68 #define debug_reloc(a)
69 #define DPRINTF(fmt,args...)
72 extern int _dl_linux_resolve(void);
74 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
76 unsigned long target_addr = (unsigned long)_dl_linux_resolve;
77 unsigned int n_plt_entries;
79 unsigned long data_words;
80 unsigned int rel_offset_words;
82 DPRINTF("init_got plt=%x, tpnt=%x\n",
83 (unsigned long)plt,(unsigned long)tpnt);
85 n_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
86 DPRINTF("n_plt_entries %d\n",n_plt_entries);
88 rel_offset_words = PLT_DATA_START_WORDS(n_plt_entries);
89 DPRINTF("rel_offset_words %x\n",rel_offset_words);
90 data_words = (unsigned long)(plt + rel_offset_words);
91 DPRINTF("data_words %x\n",data_words);
93 tpnt->data_words = data_words;
95 plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
96 plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
98 plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
99 plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
104 tramp = plt + PLT_TRAMPOLINE_ENTRY_WORDS;
105 tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
106 tramp[1] = OPCODE_ADDI(11,11,-data_words);
107 tramp[2] = OPCODE_SLWI(12,11,1);
108 tramp[3] = OPCODE_ADD(11,12,11);
109 tramp[4] = OPCODE_LI(12,target_addr);
110 tramp[5] = OPCODE_ADDIS_HI(12,12,target_addr);
111 tramp[6] = OPCODE_MTCTR(12);
112 tramp[7] = OPCODE_LI(12,(unsigned long)tpnt);
113 tramp[8] = OPCODE_ADDIS_HI(12,12,(unsigned long)tpnt);
114 tramp[9] = OPCODE_BCTR();
119 /* instructions were modified */
130 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
133 ELF_RELOC *this_reloc;
138 unsigned long insn_addr;
139 unsigned long *insns;
140 unsigned long targ_addr;
143 //DPRINTF("linux_resolver tpnt=%x reloc_entry=%x\n", tpnt, reloc_entry);
145 rel_addr = (ELF_RELOC *) (tpnt->dynamic_info[DT_JMPREL] + tpnt->loadaddr);
147 this_reloc = (void *)rel_addr + reloc_entry;
148 reloc_type = ELF32_R_TYPE(this_reloc->r_info);
149 symtab_index = ELF32_R_SYM(this_reloc->r_info);
151 symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
152 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
154 //debug_reloc(this_reloc);
156 if (reloc_type != R_PPC_JMP_SLOT) {
157 _dl_dprintf(2, "%s: Incorrect relocation type [%s] in jump relocations\n",
159 (reloc_type<N_RELTYPES)?_dl_reltypes[reloc_type]:"unknown");
163 /* Address of dump instruction to fix up */
164 insn_addr = (unsigned long) tpnt->loadaddr +
165 (unsigned long) this_reloc->r_offset;
167 DPRINTF("Resolving symbol %s %x --> ",
168 strtab + symtab[symtab_index].st_name,
171 /* Get the address of the GOT entry */
172 targ_addr = (unsigned long) _dl_find_hash(
173 strtab + symtab[symtab_index].st_name,
174 tpnt->symbol_scope, tpnt, resolver);
176 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
177 _dl_progname, strtab + symtab[symtab_index].st_name);
180 DPRINTF("%x\n", targ_addr);
182 insns = (unsigned long *)insn_addr;
183 delta = targ_addr - insn_addr;
185 if(delta<<6>>6 == delta){
186 insns[0] = OPCODE_B(delta);
187 }else if (targ_addr <= 0x01fffffc || targ_addr >= 0xfe000000){
188 insns[0] = OPCODE_BA (targ_addr);
190 /* Warning: we don't handle double-sized PLT entries */
191 unsigned long plt_addr;
192 unsigned long lbranch_addr;
196 plt_addr = (unsigned long)tpnt->dynamic_info[DT_PLTGOT] +
197 (unsigned long)tpnt->loadaddr;
198 lbranch_addr = plt_addr + PLT_LONGBRANCH_ENTRY_WORDS*4;
199 delta = lbranch_addr - insn_addr;
200 index = (insn_addr - plt_addr - PLT_INITIAL_ENTRY_WORDS*4)/8;
202 ptr = (unsigned long *)tpnt->data_words;
203 DPRINTF("plt_addr=%x delta=%x index=%x ptr=%x\n",
204 plt_addr, delta, index, ptr);
205 ptr[index] = targ_addr;
206 /* icache sync is not necessary, since this will be a data load */
207 //PPC_DCBST(ptr+index);
209 //PPC_ICBI(ptr+index);
211 insns[1] = OPCODE_B(delta - 4);
214 /* instructions were modified */
215 PPC_DCBST(insn_addr);
223 void _dl_parse_lazy_relocation_information(struct elf_resolve *tpnt,
224 unsigned long rel_addr, unsigned long rel_size, int type)
232 unsigned long reloc_addr;
233 unsigned long *insns;
237 DPRINTF("_dl_parse_lazy_relocation_information(tpnt=%x, rel_addr=%x, rel_size=%x, type=%d)\n",
238 tpnt,rel_addr,rel_size,type);
240 /* Now parse the relocation information */
241 rpnt = (ELF_RELOC *) (rel_addr + tpnt->loadaddr);
242 rel_size = rel_size / sizeof(ELF_RELOC);
244 symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
245 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
246 plt = (unsigned long *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
248 for (i = 0; i < rel_size; i++, rpnt++) {
249 reloc_addr = (unsigned long)tpnt->loadaddr +
250 (unsigned long) rpnt->r_offset;
251 reloc_type = ELF32_R_TYPE(rpnt->r_info);
252 symtab_index = ELF32_R_SYM(rpnt->r_info);
254 /* When the dynamic linker bootstrapped itself, it resolved some symbols.
255 Make sure we do not do them again */
256 if (!symtab_index && tpnt->libtype == program_interpreter)
258 if (symtab_index && tpnt->libtype == program_interpreter &&
259 _dl_symbol(strtab + symtab[symtab_index].st_name))
262 DPRINTF("L %x %s %s %x %x\n",
263 reloc_addr, _dl_reltypes[reloc_type],
264 symtab_index?strtab + symtab[symtab_index].st_name:"",0,0);
266 switch (reloc_type) {
273 delta = (unsigned long)(plt+PLT_TRAMPOLINE_ENTRY_WORDS+2)
276 index = (reloc_addr -
277 (unsigned long)(plt+PLT_INITIAL_ENTRY_WORDS))
278 /sizeof(unsigned long);
280 DPRINTF(" index %x delta %x\n",index,delta);
281 insns = (unsigned long *)reloc_addr;
282 insns[0] = OPCODE_LI(11,index*4);
283 insns[1] = OPCODE_B(delta);
287 _dl_dprintf(2, "%s: (LAZY) can't handle reloc type ",
289 #ifdef VERBOSE_DLINKER
290 _dl_dprintf(2, "%s ", _dl_reltypes[reloc_type]);
293 _dl_dprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name);
297 /* instructions were modified */
298 PPC_DCBST(reloc_addr);
300 PPC_ICBI(reloc_addr);
304 int _dl_parse_relocation_information(struct elf_resolve *tpnt,
305 unsigned long rel_addr, unsigned long rel_size, int type)
313 unsigned long *reloc_addr;
314 unsigned long symbol_addr;
316 unsigned long addend;
319 DPRINTF("_dl_parse_relocation_information(tpnt=%x, rel_addr=%x, rel_size=%x, type=%d)\n",
320 tpnt,rel_addr,rel_size,type);
322 /* Now parse the relocation information */
324 rpnt = (ELF_RELOC *) (rel_addr + tpnt->loadaddr);
325 rel_size = rel_size / sizeof(ELF_RELOC);
327 symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
328 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
329 plt = (unsigned long *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
331 for (i = 0; i < rel_size; i++, rpnt++) {
334 reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
335 reloc_type = ELF32_R_TYPE(rpnt->r_info);
336 symtab_index = ELF32_R_SYM(rpnt->r_info);
337 addend = rpnt->r_addend;
340 if (!symtab_index && tpnt->libtype == program_interpreter)
345 if (tpnt->libtype == program_interpreter &&
346 _dl_symbol(strtab + symtab[symtab_index].st_name))
349 symbol_addr = (unsigned long) _dl_find_hash(strtab + symtab[symtab_index].st_name,
351 (reloc_type == R_PPC_JMP_SLOT ? tpnt : NULL), symbolrel);
354 * We want to allow undefined references to weak symbols - this might
355 * have been intentional. We should not be linking local symbols
356 * here, so all bases should be covered.
359 ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_GLOBAL) {
360 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
361 _dl_progname, strtab + symtab[symtab_index].st_name);
365 debug_sym(symtab,strtab,symtab_index);
367 switch (reloc_type) {
373 int delta = symbol_addr - (unsigned long)reloc_addr;
374 if(delta<<6>>6 != delta){
375 _dl_dprintf(2,"R_PPC_REL24: Reloc out of range\n");
378 *reloc_addr &= 0xfc000003;
379 *reloc_addr |= delta&0x03fffffc;
383 _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
387 *reloc_addr = (unsigned long)tpnt->loadaddr + addend;
390 *reloc_addr += symbol_addr;
392 case R_PPC_ADDR16_HA:
393 /* XXX is this correct? */
394 *(short *)reloc_addr += (symbol_addr+0x8000)>>16;
396 case R_PPC_ADDR16_HI:
397 *(short *)reloc_addr += symbol_addr>>16;
399 case R_PPC_ADDR16_LO:
400 *(short *)reloc_addr += symbol_addr;
404 unsigned long targ_addr = (unsigned long)_dl_linux_resolve;
405 int delta = targ_addr - (unsigned long)reloc_addr;
406 if(delta<<6>>6 == delta){
407 *reloc_addr = OPCODE_B(delta);
408 }else if (targ_addr <= 0x01fffffc || targ_addr >= 0xfe000000){
409 *reloc_addr = OPCODE_BA (targ_addr);
415 delta = (unsigned long)(plt+PLT_TRAMPOLINE_ENTRY_WORDS+2)
416 - (unsigned long)(reloc_addr+1);
418 index = ((unsigned long)reloc_addr -
419 (unsigned long)(plt+PLT_INITIAL_ENTRY_WORDS))
420 /sizeof(unsigned long);
422 DPRINTF(" index %x delta %x\n",index,delta);
423 reloc_addr[0] = OPCODE_LI(11,index*4);
424 reloc_addr[1] = OPCODE_B(delta);
430 *reloc_addr += symbol_addr;
436 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
437 #ifdef VERBOSE_DLINKER
438 _dl_dprintf(2, "%s ", _dl_reltypes[reloc_type]);
441 _dl_dprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name);
445 /* instructions were modified */
446 PPC_DCBST(reloc_addr);
448 PPC_ICBI(reloc_addr);
450 DPRINTF("reloc_addr %x: %x\n",reloc_addr,*reloc_addr);
456 /* This is done as a separate step, because there are cases where
457 information is first copied and later initialized. This results in
458 the wrong information being copied. Someone at Sun was complaining about
459 a bug in the handling of _COPY by SVr4, and this may in fact be what he
460 was talking about. Sigh. */
462 /* No, there are cases where the SVr4 linker fails to emit COPY relocs
465 int _dl_parse_copy_information(struct dyn_elf *xpnt, unsigned long rel_addr,
466 unsigned long rel_size, int type)
474 unsigned long *reloc_addr;
475 unsigned long symbol_addr;
476 struct elf_resolve *tpnt;
479 DPRINTF("parse_copy xpnt=%x rel_addr=%x rel_size=%x type=%d\n",
480 (int)xpnt,rel_addr,rel_size,type);
482 /* Now parse the relocation information */
486 rpnt = (ELF_RELOC *) (rel_addr + tpnt->loadaddr);
487 rel_size = rel_size / sizeof(ELF_RELOC);
489 symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
490 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
492 for (i = 0; i < rel_size; i++, rpnt++) {
493 reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
494 reloc_type = ELF32_R_TYPE(rpnt->r_info);
495 if (reloc_type != R_PPC_COPY)
500 symtab_index = ELF32_R_SYM(rpnt->r_info);
502 if (!symtab_index && tpnt->libtype == program_interpreter)
506 if (tpnt->libtype == program_interpreter &&
507 _dl_symbol(strtab + symtab[symtab_index].st_name))
510 symbol_addr = (unsigned long) _dl_find_hash(strtab +
511 symtab[symtab_index].st_name, xpnt->next,
514 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n",
515 _dl_progname, strtab + symtab[symtab_index].st_name);
520 debug_sym(symtab,strtab,symtab_index);
522 DPRINTF("copy: to=%x from=%x size=%x\n",
523 symtab[symtab_index].st_value,
524 symbol_addr, symtab[symtab_index].st_size);
527 _dl_memcpy((char *) symtab[symtab_index].st_value,
528 (char *) symbol_addr,
529 symtab[symtab_index].st_size);
537 static void fixup_jmpslot(unsigned long reloc_addr, unsigned long targ_addr)
539 int delta = targ_addr - reloc_addr;
542 if(delta<<6>>6 == delta){
543 *reloc_addr = OPCODE_B(delta);
544 }else if (targ_addr <= 0x01fffffc || targ_addr >= 0xfe000000){
545 *reloc_addr = OPCODE_BA (targ_addr);
547 delta = (unsigned long)(plt+PLT_TRAMPOLINE_ENTRY_WORDS+2)
548 - (unsigned long)(reloc_addr+1);
550 index = ((unsigned long)reloc_addr -
551 (unsigned long)(plt+PLT_INITIAL_ENTRY_WORDS))
552 /sizeof(unsigned long);
555 DPRINTF(" index %x delta %x\n",index,delta);
557 reloc_addr[0] = OPCODE_LI(11,index*4);
558 reloc_addr[1] = OPCODE_B(delta);
564 #ifdef LD_DEBUG_SYMBOLS
565 static void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
568 _dl_dprintf(2, "sym: name=%s value=%x size=%x info=%x other=%x shndx=%x\n",
569 strtab + symtab[symtab_index].st_name,
570 symtab[symtab_index].st_value,
571 symtab[symtab_index].st_size,
572 symtab[symtab_index].st_info,
573 symtab[symtab_index].st_other,
574 symtab[symtab_index].st_shndx);
576 _dl_dprintf(2, "sym: null\n");
580 static void debug_reloc(ELF_RELOC *rpnt)
582 _dl_dprintf(2, "reloc: offset=%x type=%x sym=%x addend=%x\n",
584 ELF32_R_TYPE(rpnt->r_info),
585 ELF32_R_SYM(rpnt->r_info),