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
32 extern int _dl_linux_resolve(void);
34 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
37 Elf32_Word num_plt_entries;
38 Elf32_Word data_words;
39 Elf32_Word rel_offset_words;
40 Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
42 num_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
43 rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
44 data_words = (Elf32_Word) (plt + rel_offset_words);
45 tpnt->data_words = data_words;
47 plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
48 plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
50 plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
51 plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
55 tramp = (Elf32_Word *) (plt + PLT_TRAMPOLINE_ENTRY_WORDS);
57 /* For the long entries, subtract off data_words. */
58 tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
59 tramp[1] = OPCODE_ADDI(11,11,-data_words);
61 /* Multiply index of entry by 3 (in r11). */
62 tramp[2] = OPCODE_SLWI(12,11,1);
63 tramp[3] = OPCODE_ADD(11,12,11);
64 if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000) {
65 /* Load address of link map in r12. */
66 tramp[4] = OPCODE_LI (12, (Elf32_Word) tpnt);
67 tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) tpnt);
69 /* Call _dl_linux_resolve . */
70 tramp[6] = OPCODE_BA (dlrr);
72 /* Get address of _dl_linux_resolve in CTR. */
73 tramp[4] = OPCODE_LI(12,dlrr);
74 tramp[5] = OPCODE_ADDIS_HI(12,12,dlrr);
75 tramp[6] = OPCODE_MTCTR(12);
77 /* Load address of link map in r12. */
78 tramp[7] = OPCODE_LI(12,(Elf32_Word) tpnt);
79 tramp[8] = OPCODE_ADDIS_HI(12,12,(Elf32_Word) tpnt);
81 /* Call _dl_linux_resolve. */
82 tramp[9] = OPCODE_BCTR();
98 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
100 ELF_RELOC *this_reloc;
106 Elf32_Addr *reloc_addr;
107 Elf32_Addr finaladdr;
110 rel_addr = (ELF_RELOC *)tpnt->dynamic_info[DT_JMPREL];
112 this_reloc = (void *)rel_addr + reloc_entry;
113 symtab_index = ELF32_R_SYM(this_reloc->r_info);
115 symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB];
116 strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
117 symname = strtab + symtab[symtab_index].st_name;
119 debug_sym(symtab,strtab,symtab_index);
120 debug_reloc(symtab,strtab,this_reloc);
122 #if defined (__SUPPORT_LD_DEBUG__)
123 if (unlikely(ELF32_R_TYPE(this_reloc->r_info) != R_PPC_JMP_SLOT)) {
124 _dl_dprintf(2, "%s: Incorrect relocation type in jump relocation\n", _dl_progname);
129 /* Address of dump instruction to fix up */
130 reloc_addr = (Elf32_Addr *) (tpnt->loadaddr + this_reloc->r_offset);
132 #if defined (__SUPPORT_LD_DEBUG__)
133 if(_dl_debug_reloc && _dl_debug_detail)
134 _dl_dprintf(_dl_debug_file, "\n\tResolving symbol %s %x --> ", symname, (Elf32_Addr)reloc_addr);
137 /* Get the address of the GOT entry */
138 finaladdr = (Elf32_Addr) _dl_find_hash(symname,
139 tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT);
140 if (unlikely(!finaladdr)) {
141 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
144 finaladdr += this_reloc->r_addend;
145 #if defined (__SUPPORT_LD_DEBUG__)
146 if(_dl_debug_reloc && _dl_debug_detail)
147 _dl_dprintf(_dl_debug_file, "%x\n", finaladdr);
149 delta = finaladdr - (Elf32_Word)reloc_addr;
150 if (delta<<6>>6 == delta) {
151 *reloc_addr = OPCODE_B(delta);
152 } else if (finaladdr <= 0x01fffffc) {
153 *reloc_addr = OPCODE_BA (finaladdr);
155 /* Warning: we don't handle double-sized PLT entries */
156 Elf32_Word *plt, *data_words, index, offset;
158 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
159 offset = reloc_addr - plt;
160 index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
161 data_words = (Elf32_Word *)tpnt->data_words;
164 data_words[index] = finaladdr;
166 *reloc_addr = OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
169 /* instructions were modified */
170 PPC_DCBST(reloc_addr);
172 PPC_ICBI(reloc_addr);
179 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
180 ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
185 Elf32_Addr *reloc_addr;
186 Elf32_Addr finaladdr;
188 unsigned long symbol_addr;
189 #if defined (__SUPPORT_LD_DEBUG__)
190 unsigned long old_val;
192 reloc_addr = (Elf32_Addr *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
193 reloc_type = ELF32_R_TYPE(rpnt->r_info);
194 symbol_addr = tpnt->loadaddr; /* For R_PPC_RELATIVE */
195 symtab_index = ELF32_R_SYM(rpnt->r_info);
196 symname = strtab + symtab[symtab_index].st_name;
198 symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
199 elf_machine_type_class(reloc_type));
200 /* We want to allow undefined references to weak symbols - this might
201 * have been intentional. We should not be linking local symbols
202 * here, so all bases should be covered.
204 if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK))
207 #if defined (__SUPPORT_LD_DEBUG__)
208 old_val = *reloc_addr;
210 finaladdr = (Elf32_Addr) (symbol_addr + rpnt->r_addend);
212 switch (reloc_type) {
216 *reloc_addr = finaladdr;
217 goto out_nocode; /* No code code modified */
220 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
221 if (delta<<6>>6 == delta) {
222 *reloc_addr = OPCODE_B(delta);
223 } else if (finaladdr <= 0x01fffffc) {
224 *reloc_addr = OPCODE_BA (finaladdr);
226 /* Warning: we don't handle double-sized PLT entries */
227 Elf32_Word *plt, *data_words, index, offset;
229 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
230 offset = reloc_addr - plt;
231 index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
232 data_words = (Elf32_Word *)tpnt->data_words;
234 data_words[index] = finaladdr;
235 reloc_addr[0] = OPCODE_LI(11,index*4);
236 reloc_addr[1] = OPCODE_B((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1)) * 4);
238 /* instructions were modified */
239 PPC_DCBST(reloc_addr+1);
241 PPC_ICBI(reloc_addr+1);
246 #if defined (__SUPPORT_LD_DEBUG__)
248 _dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
249 symname, symtab[symtab_index].st_size,
250 symbol_addr, reloc_addr);
252 _dl_memcpy((char *) reloc_addr, (char *) finaladdr, symtab[symtab_index].st_size);
253 goto out_nocode; /* No code code modified */
254 case R_PPC_ADDR16_HA:
255 finaladdr += 0x8000; /* fall through. */
256 case R_PPC_ADDR16_HI:
257 finaladdr >>= 16; /* fall through. */
258 case R_PPC_ADDR16_LO:
259 *(short *)reloc_addr = finaladdr;
264 Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
265 if(unlikely(delta<<6>>6 != delta)) {
266 _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
267 "Compile shared libraries with -fPIC!\n",
268 _dl_progname, symname);
271 *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
275 _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
279 goto out_nocode; /* No code code modified */
281 _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
282 #if defined (__SUPPORT_LD_DEBUG__)
283 _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
286 _dl_dprintf(2, "'%s'\n", symname);
290 /* instructions were modified */
291 PPC_DCBST(reloc_addr);
293 PPC_ICBI(reloc_addr);
296 #if defined (__SUPPORT_LD_DEBUG__)
297 if(_dl_debug_reloc && _dl_debug_detail)
298 _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
303 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
304 unsigned long rel_addr, unsigned long rel_size)
306 struct elf_resolve *tpnt = rpnt->dyn;
307 Elf32_Word *plt, offset, i, num_plt_entries, rel_offset_words;
309 num_plt_entries = rel_size / sizeof(ELF_RELOC);
311 rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
312 plt = (Elf32_Word *)tpnt->dynamic_info[DT_PLTGOT];
314 /* Set up the lazy PLT entries. */
315 offset = PLT_INITIAL_ENTRY_WORDS;
317 /* Warning: we don't handle double-sized PLT entries */
318 while (i < num_plt_entries) {
319 plt[offset ] = OPCODE_LI(11, i * 4);
320 plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
324 /* Now, we've modified code. We need to write the changes from
325 the data cache to a second-level unified cache, then make
326 sure that stale data in the instruction cache is removed.
327 (In a multiprocessor system, the effect is more complex.)
328 Most of the PLT shouldn't be in the instruction cache, but
329 there may be a little overlap at the start and the end.
331 Assumes that dcbst and icbi apply to lines of 16 bytes or
332 more. Current known line sizes are 16, 32, and 128 bytes. */
333 for (i = 0; i < rel_offset_words; i += 4)
335 PPC_DCBST (plt + rel_offset_words - 1);
338 PPC_ICBI (plt + rel_offset_words - 1);
343 _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
344 unsigned long rel_addr, unsigned long rel_size,
345 int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
346 ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
354 /* Now parse the relocation information */
355 rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
356 rel_size = rel_size / sizeof(ELF_RELOC);
358 symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
359 strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
361 for (i = 0; i < rel_size; i++, rpnt++) {
364 symtab_index = ELF32_R_SYM(rpnt->r_info);
366 debug_sym(symtab,strtab,symtab_index);
367 debug_reloc(symtab,strtab,rpnt);
369 res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
371 if (res==0) continue;
373 _dl_dprintf(2, "\n%s: ",_dl_progname);
376 _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
378 if (unlikely(res <0))
380 int reloc_type = ELF32_R_TYPE(rpnt->r_info);
381 #if defined (__SUPPORT_LD_DEBUG__)
382 _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
384 _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
388 if (unlikely(res >0))
390 _dl_dprintf(2, "can't resolve symbol\n");
397 int _dl_parse_relocation_information(struct dyn_elf *rpnt,
398 unsigned long rel_addr, unsigned long rel_size)
400 return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);