1 /* vi: set sw=4 ts=4: */
2 /* powerpc shared library loader suppport
4 * Copyright (C) 2001-2002 David A. Schleef
5 * Copyright (C) 2003-2004 Erik Andersen
6 * Copyright (C) 2004 Joakim Tjernlund
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the above contributors may not be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #define TLS_DTV_OFFSET 0x8000
34 #define TLS_TP_OFFSET 0x7000
36 extern int _dl_linux_resolve(void);
38 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
41 Elf32_Word num_plt_entries;
42 Elf32_Word data_words;
43 Elf32_Word rel_offset_words;
44 Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
46 if (tpnt->dynamic_info[DT_JMPREL] == 0)
48 if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
49 tpnt->dynamic_info[DT_PPC_GOT_IDX] += tpnt->loadaddr;
52 num_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
53 rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
54 data_words = (Elf32_Word) (plt + rel_offset_words);
55 tpnt->data_words = data_words;
57 plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
58 plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
60 plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
61 plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
65 tramp = (Elf32_Word *) (plt + PLT_TRAMPOLINE_ENTRY_WORDS);
67 /* For the long entries, subtract off data_words. */
68 tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
69 tramp[1] = OPCODE_ADDI(11,11,-data_words);
71 /* Multiply index of entry by 3 (in r11). */
72 tramp[2] = OPCODE_SLWI(12,11,1);
73 tramp[3] = OPCODE_ADD(11,12,11);
74 if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000) {
75 /* Load address of link map in r12. */
76 tramp[4] = OPCODE_LI (12, (Elf32_Word) tpnt);
77 tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) tpnt);
79 /* Call _dl_linux_resolve . */
80 tramp[6] = OPCODE_BA (dlrr);
82 /* Get address of _dl_linux_resolve in CTR. */
83 tramp[4] = OPCODE_LI(12,dlrr);
84 tramp[5] = OPCODE_ADDIS_HI(12,12,dlrr);
85 tramp[6] = OPCODE_MTCTR(12);
87 /* Load address of link map in r12. */
88 tramp[7] = OPCODE_LI(12,(Elf32_Word) tpnt);
89 tramp[8] = OPCODE_ADDIS_HI(12,12,(Elf32_Word) tpnt);
91 /* Call _dl_linux_resolve. */
92 tramp[9] = OPCODE_BCTR();
108 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
110 ELF_RELOC *this_reloc;
116 ElfW(Addr) *reloc_addr;
117 ElfW(Addr) finaladdr;
120 rel_addr = (ELF_RELOC *)tpnt->dynamic_info[DT_JMPREL];
122 this_reloc = (void *)rel_addr + reloc_entry;
123 symtab_index = ELF_R_SYM(this_reloc->r_info);
125 symtab = (ElfW(Sym) *)tpnt->dynamic_info[DT_SYMTAB];
126 strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
127 symname = strtab + symtab[symtab_index].st_name;
129 debug_sym(symtab,strtab,symtab_index);
130 debug_reloc(symtab,strtab,this_reloc);
132 /* Address of dump instruction to fix up */
133 reloc_addr = (ElfW(Addr) *) (tpnt->loadaddr + this_reloc->r_offset);
135 #if defined (__SUPPORT_LD_DEBUG__)
136 if (_dl_debug_reloc && _dl_debug_detail)
137 _dl_dprintf(_dl_debug_file, "\n\tResolving symbol %s %x --> ", symname, (ElfW(Addr))reloc_addr);
140 /* Get the address of the GOT entry */
141 finaladdr = (ElfW(Addr)) _dl_find_hash(symname,
142 &_dl_loaded_modules->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
143 if (unlikely(!finaladdr)) {
144 _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
147 finaladdr += this_reloc->r_addend;
148 #if defined (__SUPPORT_LD_DEBUG__)
149 if (_dl_debug_reloc && _dl_debug_detail)
150 _dl_dprintf(_dl_debug_file, "%x\n", finaladdr);
152 if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
153 *reloc_addr = finaladdr;
155 delta = finaladdr - (Elf32_Word)reloc_addr;
156 if (delta<<6>>6 == delta) {
157 *reloc_addr = OPCODE_B(delta);
158 } else if (finaladdr <= 0x01fffffc) {
159 *reloc_addr = OPCODE_BA (finaladdr);
161 /* Warning: we don't handle double-sized PLT entries */
162 Elf32_Word *plt, *data_words, idx, offset;
164 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
165 offset = reloc_addr - plt;
166 idx = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
167 data_words = (Elf32_Word *)tpnt->data_words;
170 data_words[idx] = finaladdr;
172 *reloc_addr = OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
175 /* instructions were modified */
176 PPC_DCBST(reloc_addr);
178 PPC_ICBI(reloc_addr);
184 static __inline__ int
185 _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope,
186 ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
190 struct symbol_ref sym_ref;
191 ElfW(Addr) *reloc_addr;
192 ElfW(Addr) finaladdr;
193 struct elf_resolve *tls_tpnt = NULL;
194 unsigned long symbol_addr;
196 #if defined (__SUPPORT_LD_DEBUG__)
197 unsigned long old_val;
200 symbol_addr = tpnt->loadaddr; /* For R_PPC_RELATIVE */
201 reloc_addr = (ElfW(Addr) *)(intptr_t) (symbol_addr + (unsigned long) rpnt->r_offset);
202 reloc_type = ELF_R_TYPE(rpnt->r_info);
203 symtab_index = ELF_R_SYM(rpnt->r_info);
204 sym_ref.sym = &symtab[symtab_index];
206 symname = strtab + sym_ref.sym->st_name;
208 symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
209 elf_machine_type_class(reloc_type), &sym_ref);
210 /* We want to allow undefined references to weak symbols - this might
211 * have been intentional. We should not be linking local symbols
212 * here, so all bases should be covered.
214 if (unlikely(!symbol_addr
215 && (ELF_ST_TYPE(sym_ref.sym->st_info) != STT_TLS
216 && ELF_ST_BIND(sym_ref.sym->st_info) != STB_WEAK)))
218 if (_dl_trace_prelink) {
219 _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
220 &sym_ref, elf_machine_type_class(reloc_type));
222 tls_tpnt = sym_ref.tpnt;
224 symbol_addr = sym_ref.sym->st_value;
227 #if defined (__SUPPORT_LD_DEBUG__)
228 old_val = *reloc_addr;
230 finaladdr = (ElfW(Addr)) (symbol_addr + rpnt->r_addend);
232 switch (reloc_type) {
236 *reloc_addr = finaladdr;
237 goto out_nocode; /* No code code modified */
240 if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
241 *reloc_addr = finaladdr;
242 goto out_nocode; /* No code code modified */
244 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
245 if (delta<<6>>6 == delta) {
246 *reloc_addr = OPCODE_B(delta);
247 } else if (finaladdr <= 0x01fffffc) {
248 *reloc_addr = OPCODE_BA (finaladdr);
250 /* Warning: we don't handle double-sized PLT entries */
251 Elf32_Word *plt, *data_words, idx, offset;
253 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
254 offset = reloc_addr - plt;
255 idx = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
256 data_words = (Elf32_Word *)tpnt->data_words;
258 data_words[idx] = finaladdr;
259 reloc_addr[0] = OPCODE_LI(11,idx*4);
260 reloc_addr[1] = OPCODE_B((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
262 /* instructions were modified */
263 PPC_DCBST(reloc_addr+1);
265 PPC_ICBI(reloc_addr+1);
271 #if defined (__SUPPORT_LD_DEBUG__)
273 _dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
274 symname, sym_ref.sym->st_size,
275 symbol_addr, reloc_addr);
277 _dl_memcpy((char *) reloc_addr, (char *) finaladdr, sym_ref.sym->st_size);
278 goto out_nocode; /* No code code modified */
279 case R_PPC_ADDR16_HA:
280 finaladdr += 0x8000; /* fall through. */
281 case R_PPC_ADDR16_HI:
282 finaladdr >>= 16; /* fall through. */
283 case R_PPC_ADDR16_LO:
284 *(short *)reloc_addr = finaladdr;
288 *reloc_addr = tls_tpnt->l_tls_modid;
291 /* During relocation all TLS symbols are defined and used.
292 Therefore the offset is already correct. */
293 *reloc_addr = finaladdr - TLS_DTV_OFFSET;
296 *reloc_addr = tls_tpnt->l_tls_offset + finaladdr - TLS_TP_OFFSET;
302 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
303 if (unlikely(delta<<6>>6 != delta)) {
304 _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
305 "Compile shared libraries with -fPIC!\n",
306 _dl_progname, symname);
309 *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
313 _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
317 goto out_nocode; /* No code code modified */
319 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
320 #if defined (__SUPPORT_LD_DEBUG__)
321 _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
324 _dl_dprintf(2, "'%s'\n", symname);
328 /* instructions were modified */
329 PPC_DCBST(reloc_addr);
331 PPC_ICBI(reloc_addr);
334 #if defined (__SUPPORT_LD_DEBUG__)
335 if (_dl_debug_reloc && _dl_debug_detail)
336 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr);
341 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
342 unsigned long rel_addr, unsigned long rel_size)
344 struct elf_resolve *tpnt = rpnt->dyn;
345 Elf32_Word *plt, offset, i, num_plt_entries, rel_offset_words;
347 num_plt_entries = rel_size / sizeof(ELF_RELOC);
348 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
349 if (tpnt->dynamic_info[DT_PPC_GOT_IDX] != 0) {
351 ElfW(Addr) *got = (ElfW(Addr) *)tpnt->dynamic_info[DT_PPC_GOT_IDX];
352 Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
354 got[1] = (ElfW(Addr)) dlrr;
355 got[2] = (ElfW(Addr)) tpnt;
357 /* Relocate everything in .plt by the load address offset. */
358 while (num_plt_entries-- != 0)
359 *plt++ += tpnt->loadaddr;
363 rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
365 /* Set up the lazy PLT entries. */
366 offset = PLT_INITIAL_ENTRY_WORDS;
368 /* Warning: we don't handle double-sized PLT entries */
369 while (i < num_plt_entries) {
370 plt[offset ] = OPCODE_LI(11, i * 4);
371 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
375 /* Now, we've modified code. We need to write the changes from
376 the data cache to a second-level unified cache, then make
377 sure that stale data in the instruction cache is removed.
378 (In a multiprocessor system, the effect is more complex.)
379 Most of the PLT shouldn't be in the instruction cache, but
380 there may be a little overlap at the start and the end.
382 Assumes that dcbst and icbi apply to lines of 16 bytes or
383 more. Current known line sizes are 16, 32, and 128 bytes. */
384 for (i = 0; i < rel_offset_words; i += 4)
386 PPC_DCBST (plt + rel_offset_words - 1);
389 PPC_ICBI (plt + rel_offset_words - 1);
393 static __inline__ int
394 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
395 unsigned long rel_addr, unsigned long rel_size,
396 int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *scope,
397 ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
405 /* Now parse the relocation information */
406 rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
407 rel_size = rel_size / sizeof(ELF_RELOC);
409 symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
410 strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
412 for (i = 0; i < rel_size; i++, rpnt++) {
415 symtab_index = ELF_R_SYM(rpnt->r_info);
417 debug_sym(symtab,strtab,symtab_index);
418 debug_reloc(symtab,strtab,rpnt);
420 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
422 if (res==0) continue;
424 _dl_dprintf(2, "\n%s: ",_dl_progname);
427 _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
429 if (unlikely(res <0))
431 int reloc_type = ELF_R_TYPE(rpnt->r_info);
432 #if defined (__SUPPORT_LD_DEBUG__)
433 _dl_dprintf(2, "can't handle reloc type '%s' in lib '%s'\n", _dl_reltypes(reloc_type), tpnt->libname);
435 _dl_dprintf(2, "can't handle reloc type %x in lib '%s'\n", reloc_type, tpnt->libname);
439 if (unlikely(res >0))
441 _dl_dprintf(2, "can't resolve symbol in lib '%s'.\n", tpnt->libname);
448 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
449 struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size)
451 return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);