]>
Commit | Line | Data |
---|---|---|
4cca6b86 | 1 | /* Machine-dependent ELF dynamic relocation inline functions. PowerPC version. |
b0cf070b | 2 | Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. |
4cca6b86 UD |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
17 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 | Boston, MA 02111-1307, USA. */ | |
19 | ||
1f205a47 UD |
20 | #ifndef dl_machine_h |
21 | #define dl_machine_h | |
22 | ||
4cca6b86 UD |
23 | #define ELF_MACHINE_NAME "powerpc" |
24 | ||
25 | #include <assert.h> | |
26 | #include <string.h> | |
27 | #include <link.h> | |
1f07e617 UD |
28 | #include <sys/param.h> |
29 | ||
4cca6b86 UD |
30 | |
31 | /* stuff for the PLT */ | |
32 | #define PLT_INITIAL_ENTRY_WORDS 18 | |
33 | #define PLT_LONGBRANCH_ENTRY_WORDS 10 | |
e61abf83 UD |
34 | #define PLT_DOUBLE_SIZE (1<<13) |
35 | #define PLT_ENTRY_START_WORDS(entry_number) \ | |
36 | (PLT_INITIAL_ENTRY_WORDS + (entry_number)*2 + \ | |
37 | ((entry_number) > PLT_DOUBLE_SIZE ? \ | |
38 | ((entry_number) - PLT_DOUBLE_SIZE)*2 : \ | |
39 | 0)) | |
40 | #define PLT_DATA_START_WORDS(num_entries) PLT_ENTRY_START_WORDS(num_entries) | |
41 | ||
4cca6b86 UD |
42 | #define OPCODE_ADDI(rd,ra,simm) \ |
43 | (0x38000000 | (rd) << 21 | (ra) << 16 | (simm) & 0xffff) | |
44 | #define OPCODE_ADDIS(rd,ra,simm) \ | |
45 | (0x3c000000 | (rd) << 21 | (ra) << 16 | (simm) & 0xffff) | |
46 | #define OPCODE_ADD(rd,ra,rb) \ | |
47 | (0x7c000214 | (rd) << 21 | (ra) << 16 | (rb) << 11) | |
48 | #define OPCODE_B(target) (0x48000000 | (target) & 0x03fffffc) | |
49 | #define OPCODE_BA(target) (0x48000002 | (target) & 0x03fffffc) | |
50 | #define OPCODE_BCTR() 0x4e800420 | |
51 | #define OPCODE_LWZ(rd,d,ra) \ | |
52 | (0x80000000 | (rd) << 21 | (ra) << 16 | (d) & 0xffff) | |
53 | #define OPCODE_MTCTR(rd) (0x7C0903A6 | (rd) << 21) | |
54 | #define OPCODE_RLWINM(ra,rs,sh,mb,me) \ | |
55 | (0x54000000 | (rs) << 21 | (ra) << 16 | (sh) << 11 | (mb) << 6 | (me) << 1) | |
56 | ||
57 | #define OPCODE_LI(rd,simm) OPCODE_ADDI(rd,0,simm) | |
58 | #define OPCODE_SLWI(ra,rs,sh) OPCODE_RLWINM(ra,rs,sh,0,31-sh) | |
59 | ||
0413b54c UD |
60 | #define PPC_DCBST(where) asm volatile ("dcbst 0,%0" : : "r"(where)) |
61 | #define PPC_SYNC asm volatile ("sync") | |
62 | #define PPC_ISYNC asm volatile ("sync; isync") | |
63 | #define PPC_ICBI(where) asm volatile ("icbi 0,%0" : : "r"(where)) | |
64 | #define PPC_DIE asm volatile ("tweq 0,0") | |
e61abf83 UD |
65 | |
66 | /* Use this when you've modified some code, but it won't be in the | |
67 | instruction fetch queue (or when it doesn't matter if it is). */ | |
68 | #define MODIFIED_CODE_NOQUEUE(where) \ | |
69 | do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); } while (0) | |
70 | /* Use this when it might be in the instruction queue. */ | |
71 | #define MODIFIED_CODE(where) \ | |
72 | do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); PPC_ISYNC; } while (0) | |
73 | ||
4cca6b86 UD |
74 | |
75 | /* Return nonzero iff E_MACHINE is compatible with the running host. */ | |
76 | static inline int | |
77 | elf_machine_matches_host (Elf32_Half e_machine) | |
78 | { | |
79 | return e_machine == EM_PPC; | |
80 | } | |
81 | ||
82 | ||
e61abf83 UD |
83 | /* Return the link-time address of _DYNAMIC, stored as |
84 | the first value in the GOT. */ | |
4cca6b86 UD |
85 | static inline Elf32_Addr |
86 | elf_machine_dynamic (void) | |
87 | { | |
88 | Elf32_Addr *got; | |
89 | asm (" bl _GLOBAL_OFFSET_TABLE_-4@local" | |
90 | : "=l"(got)); | |
91 | return *got; | |
92 | } | |
93 | ||
94 | /* Return the run-time load address of the shared object. */ | |
95 | static inline Elf32_Addr | |
96 | elf_machine_load_address (void) | |
97 | { | |
98 | unsigned *got; | |
99 | unsigned *branchaddr; | |
100 | ||
101 | /* This is much harder than you'd expect. Possibly I'm missing something. | |
102 | The 'obvious' way: | |
103 | ||
104 | Apparently, "bcl 20,31,$+4" is what should be used to load LR | |
105 | with the address of the next instruction. | |
106 | I think this is so that machines that do bl/blr pairing don't | |
107 | get confused. | |
108 | ||
109 | asm ("bcl 20,31,0f ;" | |
e61abf83 UD |
110 | "0: mflr 0 ;" |
111 | "lis %0,0b@ha;" | |
112 | "addi %0,%0,0b@l;" | |
113 | "subf %0,%0,0" | |
114 | : "=b" (addr) : : "r0", "lr"); | |
4cca6b86 UD |
115 | |
116 | doesn't work, because the linker doesn't have to (and in fact doesn't) | |
117 | update the @ha and @l references; the loader (which runs after this | |
118 | code) will do that. | |
119 | ||
120 | Instead, we use the following trick: | |
121 | ||
122 | The linker puts the _link-time_ address of _DYNAMIC at the first | |
123 | word in the GOT. We could branch to that address, if we wanted, | |
124 | by using an @local reloc; the linker works this out, so it's safe | |
125 | to use now. We can't, of course, actually branch there, because | |
126 | we'd cause an illegal instruction exception; so we need to compute | |
127 | the address ourselves. That gives us the following code: */ | |
128 | ||
129 | /* Get address of the 'b _DYNAMIC@local'... */ | |
130 | asm ("bl 0f ;" | |
131 | "b _DYNAMIC@local;" | |
132 | "0:" | |
133 | : "=l"(branchaddr)); | |
134 | ||
135 | /* ... and the address of the GOT. */ | |
136 | asm (" bl _GLOBAL_OFFSET_TABLE_-4@local" | |
137 | : "=l"(got)); | |
138 | ||
139 | /* So now work out the difference between where the branch actually points, | |
140 | and the offset of that location in memory from the start of the file. */ | |
1f205a47 UD |
141 | return ((Elf32_Addr)branchaddr - *got |
142 | + (*branchaddr & 0x3fffffc | |
143 | | (int)(*branchaddr << 6 & 0x80000000) >> 6)); | |
4cca6b86 UD |
144 | } |
145 | ||
146 | #define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) /* nothing */ | |
147 | ||
1f205a47 UD |
148 | /* The PLT uses Elf32_Rela relocs. */ |
149 | #define elf_machine_relplt elf_machine_rela | |
4cca6b86 | 150 | |
0413b54c | 151 | /* This code is used in dl-runtime.c to call the `fixup' function |
9a0a462c UD |
152 | and then redirect to the address it returns. It is called |
153 | from code built in the PLT by elf_machine_runtime_setup. */ | |
1f205a47 UD |
154 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ |
155 | .section \".text\" | |
156 | .align 2 | |
157 | .globl _dl_runtime_resolve | |
158 | .type _dl_runtime_resolve,@function | |
159 | _dl_runtime_resolve: | |
9a0a462c UD |
160 | # We need to save the registers used to pass parameters, and register 0, |
161 | # which is used by _mcount; the registers are saved in a stack frame. | |
1f205a47 | 162 | stwu 1,-48(1) |
9a0a462c | 163 | stw 0,12(1) |
1f205a47 UD |
164 | stw 3,16(1) |
165 | stw 4,20(1) | |
9a0a462c UD |
166 | # The code that calls this has put parameters for `fixup' in r12 and r11. |
167 | mr 3,12 | |
1f205a47 | 168 | stw 5,24(1) |
9a0a462c | 169 | mr 4,11 |
1f205a47 | 170 | stw 6,28(1) |
9a0a462c UD |
171 | mflr 0 |
172 | # We also need to save some of the condition register fields. | |
1f205a47 | 173 | stw 7,32(1) |
9a0a462c | 174 | stw 0,52(1) |
1f205a47 | 175 | stw 8,36(1) |
9a0a462c | 176 | mfcr 0 |
1f205a47 UD |
177 | stw 9,40(1) |
178 | stw 10,44(1) | |
9a0a462c | 179 | stw 0,8(1) |
1f205a47 UD |
180 | bl fixup@local |
181 | # 'fixup' returns the address we want to branch to. | |
182 | mtctr 3 | |
183 | # Put the registers back... | |
184 | lwz 0,52(1) | |
185 | lwz 10,44(1) | |
186 | lwz 9,40(1) | |
187 | mtlr 0 | |
1f205a47 | 188 | lwz 8,36(1) |
9a0a462c | 189 | lwz 0,8(1) |
1f205a47 UD |
190 | lwz 7,32(1) |
191 | lwz 6,28(1) | |
192 | mtcrf 0xFF,0 | |
193 | lwz 5,24(1) | |
194 | lwz 4,20(1) | |
195 | lwz 3,16(1) | |
9a0a462c | 196 | lwz 0,12(1) |
1f205a47 UD |
197 | # ...unwind the stack frame, and jump to the PLT entry we updated. |
198 | addi 1,1,48 | |
199 | bctr | |
a2b08ee5 | 200 | .size _dl_runtime_resolve,.-_dl_runtime_resolve |
650425ce UD |
201 | |
202 | .align 2 | |
203 | .globl _dl_prof_resolve | |
204 | .type _dl_prof_resolve,@function | |
205 | _dl_prof_resolve: | |
206 | # We need to save the registers used to pass parameters, and register 0, | |
207 | # which is used by _mcount; the registers are saved in a stack frame. | |
208 | stwu 1,-48(1) | |
209 | stw 0,12(1) | |
210 | stw 3,16(1) | |
211 | stw 4,20(1) | |
212 | # The code that calls this has put parameters for `fixup' in r12 and r11. | |
213 | mr 3,12 | |
214 | stw 5,24(1) | |
215 | mr 4,11 | |
216 | stw 6,28(1) | |
217 | mflr 5 | |
218 | # We also need to save some of the condition register fields. | |
219 | stw 7,32(1) | |
220 | stw 5,52(1) | |
221 | stw 8,36(1) | |
222 | mfcr 0 | |
223 | stw 9,40(1) | |
224 | stw 10,44(1) | |
225 | stw 0,8(1) | |
226 | bl profile_fixup@local | |
227 | # 'fixup' returns the address we want to branch to. | |
228 | mtctr 3 | |
229 | # Put the registers back... | |
230 | lwz 0,52(1) | |
231 | lwz 10,44(1) | |
232 | lwz 9,40(1) | |
233 | mtlr 0 | |
234 | lwz 8,36(1) | |
235 | lwz 0,8(1) | |
236 | lwz 7,32(1) | |
237 | lwz 6,28(1) | |
238 | mtcrf 0xFF,0 | |
239 | lwz 5,24(1) | |
240 | lwz 4,20(1) | |
241 | lwz 3,16(1) | |
242 | lwz 0,12(1) | |
243 | # ...unwind the stack frame, and jump to the PLT entry we updated. | |
244 | addi 1,1,48 | |
245 | bctr | |
af6f3906 | 246 | .size _dl_prof_resolve,.-_dl_prof_resolve |
9a0a462c | 247 | # Undo '.section text'. |
1f205a47 UD |
248 | .previous |
249 | "); | |
4cca6b86 | 250 | |
1f205a47 UD |
251 | /* Initial entry point code for the dynamic linker. |
252 | The C function `_dl_start' is the real entry point; | |
253 | its return value is the user program's entry point. */ | |
254 | #define RTLD_START \ | |
255 | static ElfW(Addr) _dl_start (void *arg) __attribute__((unused)); \ | |
256 | asm ("\ | |
257 | .section \".text\" | |
258 | .align 2 | |
259 | .globl _start | |
260 | .type _start,@function | |
261 | _start: | |
262 | # We start with the following on the stack, from top: | |
9a0a462c UD |
263 | # argc (4 bytes); |
264 | # arguments for program (terminated by NULL); | |
265 | # environment variables (terminated by NULL); | |
266 | # arguments for the program loader. | |
1f205a47 | 267 | # FIXME: perhaps this should do the same trick as elf/start.c? |
4cca6b86 | 268 | |
1f205a47 | 269 | # Call _dl_start with one parameter pointing at argc |
9a0a462c | 270 | mr 3,1 |
1f205a47 UD |
271 | # (we have to frob the stack pointer a bit to allow room for |
272 | # _dl_start to save the link register) | |
9a0a462c | 273 | li 4,0 |
1f205a47 | 274 | addi 1,1,-16 |
9a0a462c UD |
275 | stw 4,0(1) |
276 | bl _dl_start@local | |
4cca6b86 | 277 | |
1f205a47 UD |
278 | # Now, we do our main work of calling initialisation procedures. |
279 | # The ELF ABI doesn't say anything about parameters for these, | |
280 | # so we just pass argc, argv, and the environment. | |
281 | # Changing these is strongly discouraged (not least because argc is | |
282 | # passed by value!). | |
e61abf83 | 283 | |
9a0a462c UD |
284 | # Put our GOT pointer in r31, |
285 | bl _GLOBAL_OFFSET_TABLE_-4@local | |
1f205a47 | 286 | mflr 31 |
9a0a462c UD |
287 | # the address of _start in r30, |
288 | mr 30,3 | |
289 | # &_dl_argc in 29, &_dl_argv in 27, and _dl_default_scope in 28. | |
290 | lwz 28,_dl_default_scope@got(31) | |
291 | lwz 29,_dl_argc@got(31) | |
292 | lwz 27,_dl_argv@got(31) | |
1f205a47 | 293 | 0: |
9a0a462c UD |
294 | # Set initfunc = _dl_init_next(_dl_default_scope[2]) |
295 | lwz 3,8(28) | |
296 | bl _dl_init_next@plt | |
297 | # If initfunc is NULL, we exit the loop; otherwise, | |
298 | cmpwi 3,0 | |
299 | beq 1f | |
1f205a47 | 300 | # call initfunc(_dl_argc, _dl_argv, _dl_argv+_dl_argc+1) |
9a0a462c UD |
301 | mtlr 3 |
302 | lwz 3,0(29) | |
303 | lwz 4,0(27) | |
1f205a47 | 304 | slwi 5,3,2 |
9a0a462c | 305 | add 5,4,5 |
1f205a47 UD |
306 | addi 5,5,4 |
307 | blrl | |
308 | # and loop. | |
9a0a462c | 309 | b 0b |
1f205a47 UD |
310 | 1: |
311 | # Now, to conform to the ELF ABI, we have to: | |
9a0a462c UD |
312 | # Pass argc (actually _dl_argc) in r3; |
313 | lwz 3,0(29) | |
314 | # pass argv (actually _dl_argv) in r4; | |
315 | lwz 4,0(27) | |
316 | # pass envp (actually _dl_argv+_dl_argc+1) in r5; | |
1f205a47 | 317 | slwi 5,3,2 |
9a0a462c UD |
318 | add 6,4,5 |
319 | addi 5,6,4 | |
320 | # pass the auxilary vector in r6. This is passed to us just after _envp. | |
1f205a47 | 321 | 2: lwzu 0,4(6) |
9a0a462c UD |
322 | cmpwi 0,0,0 |
323 | bne 2b | |
1f205a47 | 324 | addi 6,6,4 |
9a0a462c UD |
325 | # Pass a termination function pointer (in this case _dl_fini) in r7. |
326 | lwz 7,_dl_fini@got(31) | |
327 | # Now, call the start function in r30... | |
1f205a47 | 328 | mtctr 30 |
9a0a462c UD |
329 | lwz 26,_dl_starting_up@got(31) |
330 | # Pass the stack pointer in r1 (so far so good), pointing to a NULL value. | |
331 | # (This lets our startup code distinguish between a program linked statically, | |
1f205a47 UD |
332 | # which linux will call with argc on top of the stack which will hopefully |
333 | # never be zero, and a dynamically linked program which will always have | |
334 | # a NULL on the top of the stack). | |
335 | # Take the opportunity to clear LR, so anyone who accidentally returns | |
9a0a462c UD |
336 | # from _start gets SEGV. Also clear the next few words of the stack. |
337 | li 31,0 | |
338 | stw 31,0(1) | |
339 | mtlr 31 | |
340 | stw 31,4(1) | |
341 | stw 31,8(1) | |
342 | stw 31,12(1) | |
343 | # Clear _dl_starting_up. | |
344 | stw 31,0(26) | |
345 | # Go do it! | |
1f205a47 UD |
346 | bctr |
347 | 0: | |
348 | .size _start,0b-_start | |
9a0a462c | 349 | # Undo '.section text'. |
1f205a47 UD |
350 | .previous |
351 | "); | |
4cca6b86 | 352 | |
1f205a47 UD |
353 | /* The idea here is that to conform to the ABI, we are supposed to try |
354 | to load dynamic objects between 0x10000 (we actually use 0x40000 as | |
355 | the lower bound, to increase the chance of a memory reference from | |
356 | a null pointer giving a segfault) and the program's load address. | |
357 | Regrettably, in this code we can't find the program's load address, | |
358 | so we punt and choose 0x01800000, which is below the ABI's | |
359 | recommended default, and what GNU ld currently chooses. We only use | |
360 | the address as a preference for mmap, so if we get it wrong the | |
361 | worst that happens is that it gets mapped somewhere else. | |
1f07e617 | 362 | |
1f205a47 UD |
363 | FIXME: Unfortunately, 'somewhere else' is probably right after the |
364 | program's break, which causes malloc to fail. We really need more | |
365 | information here about the way memory is mapped. */ | |
5929563f | 366 | |
1f205a47 UD |
367 | #define ELF_PREFERRED_ADDRESS_DATA \ |
368 | static ElfW(Addr) _dl_preferred_address = 1 | |
e61abf83 | 369 | |
1f205a47 UD |
370 | #define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) \ |
371 | ( { \ | |
372 | ElfW(Addr) prefd; \ | |
373 | if (mapstartpref != 0 && _dl_preferred_address == 1) \ | |
374 | _dl_preferred_address = mapstartpref; \ | |
375 | if (mapstartpref != 0) \ | |
376 | prefd = mapstartpref; \ | |
377 | else if (_dl_preferred_address == 1) \ | |
378 | prefd = _dl_preferred_address = \ | |
379 | (0x01800000 - maplength - 0x10000) & \ | |
380 | ~(_dl_pagesize - 1); \ | |
381 | else if (_dl_preferred_address < maplength + 0x50000) \ | |
382 | prefd = 0; \ | |
383 | else \ | |
384 | prefd = _dl_preferred_address = \ | |
385 | ((_dl_preferred_address - maplength - 0x10000) \ | |
386 | & ~(_dl_pagesize - 1)); \ | |
387 | prefd; \ | |
388 | } ) | |
e61abf83 | 389 | |
1f205a47 UD |
390 | #define ELF_FIXED_ADDRESS(loader, mapstart) \ |
391 | ( { \ | |
392 | if (mapstart != 0 && _dl_preferred_address == 1) \ | |
393 | _dl_preferred_address = mapstart; \ | |
394 | } ) | |
e61abf83 | 395 | |
1f205a47 UD |
396 | /* Nonzero iff TYPE should not be allowed to resolve to one of |
397 | the main executable's symbols, as for a COPY reloc. */ | |
398 | #define elf_machine_lookup_noexec_p(type) ((type) == R_PPC_COPY) | |
4cca6b86 | 399 | |
1f205a47 UD |
400 | /* Nonzero iff TYPE describes relocation of a PLT entry, so |
401 | PLT entries should not be allowed to define the value. */ | |
402 | /* We never want to use a PLT entry as the destination of a | |
403 | reloc, when what is being relocated is a branch. This is | |
404 | partly for efficiency, but mostly so we avoid loops. */ | |
405 | #define elf_machine_lookup_noplt_p(type) ((type) == R_PPC_REL24 || \ | |
406 | (type) == R_PPC_ADDR24 || \ | |
407 | (type) == R_PPC_JMP_SLOT) | |
4cca6b86 | 408 | |
1f205a47 | 409 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ |
a2b08ee5 | 410 | #define ELF_MACHINE_JMP_SLOT R_PPC_JMP_SLOT |
4cca6b86 UD |
411 | |
412 | /* Nonzero iff TYPE describes relocation of a PLT entry, so | |
413 | PLT entries should not be allowed to define the value. */ | |
414 | #define elf_machine_pltrel_p(type) ((type) == R_PPC_JMP_SLOT) | |
415 | ||
416 | /* Set up the loaded object described by L so its unrelocated PLT | |
5929563f UD |
417 | entries will jump to the on-demand fixup code in dl-runtime.c. |
418 | Also install a small trampoline to be used by entries that have | |
419 | been relocated to an address too far away for a single branch. */ | |
420 | ||
421 | /* A PLT entry does one of three things: | |
422 | (i) Jumps to the actual routine. Such entries are set up above, in | |
423 | elf_machine_rela. | |
424 | ||
425 | (ii) Jumps to the actual routine via glue at the start of the PLT. | |
426 | We do this by putting the address of the routine in space | |
427 | allocated at the end of the PLT, and when the PLT entry is | |
428 | called we load the offset of that word (from the start of the | |
429 | space) into r11, then call the glue, which loads the word and | |
430 | branches to that address. These entries are set up in | |
431 | elf_machine_rela, but the glue is set up here. | |
432 | ||
433 | (iii) Loads the index of this PLT entry (we count the double-size | |
434 | entries as one entry for this purpose) into r11, then | |
435 | branches to code at the start of the PLT. This code then | |
436 | calls `fixup', in dl-runtime.c, via the glue in the macro | |
437 | ELF_MACHINE_RUNTIME_TRAMPOLINE, which resets the PLT entry to | |
438 | be one of the above two types. These entries are set up here. */ | |
0501d603 | 439 | static inline int |
3996f34b | 440 | elf_machine_runtime_setup (struct link_map *map, int lazy, int profile) |
4cca6b86 | 441 | { |
4cca6b86 UD |
442 | if (map->l_info[DT_JMPREL]) |
443 | { | |
9a0a462c | 444 | Elf32_Word i; |
4cca6b86 UD |
445 | /* Fill in the PLT. Its initial contents are directed to a |
446 | function earlier in the PLT which arranges for the dynamic | |
447 | linker to be called back. */ | |
e61abf83 UD |
448 | Elf32_Word *plt = (Elf32_Word *) ((char *) map->l_addr |
449 | + map->l_info[DT_PLTGOT]->d_un.d_val); | |
450 | Elf32_Word num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val | |
451 | / sizeof (Elf32_Rela)); | |
452 | Elf32_Word rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries); | |
e61abf83 | 453 | Elf32_Word size_modified; |
650425ce UD |
454 | extern void _dl_runtime_resolve (void); |
455 | extern void _dl_prof_resolve (void); | |
456 | Elf32_Word dlrr; | |
457 | ||
458 | dlrr = (Elf32_Word)(char *)(profile | |
459 | ? _dl_prof_resolve | |
460 | : _dl_runtime_resolve); | |
4cca6b86 UD |
461 | |
462 | if (lazy) | |
463 | for (i = 0; i < num_plt_entries; i++) | |
4cca6b86 | 464 | { |
5929563f | 465 | Elf32_Word offset = PLT_ENTRY_START_WORDS (i); |
e61abf83 UD |
466 | |
467 | if (i >= PLT_DOUBLE_SIZE) | |
468 | { | |
5929563f UD |
469 | plt[offset ] = OPCODE_LI (11, i * 4); |
470 | plt[offset+1] = OPCODE_ADDIS (11, 11, (i * 4 + 0x8000) >> 16); | |
e61abf83 UD |
471 | plt[offset+2] = OPCODE_B (-(4 * (offset + 2))); |
472 | } | |
473 | else | |
474 | { | |
5929563f UD |
475 | plt[offset ] = OPCODE_LI (11, i * 4); |
476 | plt[offset+1] = OPCODE_B (-(4 * (offset + 1))); | |
e61abf83 | 477 | } |
5929563f | 478 | } |
e61abf83 | 479 | |
5929563f UD |
480 | /* Multiply index of entry by 3 (in r11). */ |
481 | plt[0] = OPCODE_SLWI (12, 11, 1); | |
482 | plt[1] = OPCODE_ADD (11, 12, 11); | |
650425ce | 483 | if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000) |
5929563f UD |
484 | { |
485 | /* Load address of link map in r12. */ | |
486 | plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) map); | |
487 | plt[3] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map | |
488 | + 0x8000) >> 16)); | |
489 | ||
490 | /* Call _dl_runtime_resolve. */ | |
650425ce | 491 | plt[4] = OPCODE_BA (dlrr); |
4cca6b86 UD |
492 | } |
493 | else | |
494 | { | |
5929563f | 495 | /* Get address of _dl_runtime_resolve in CTR. */ |
650425ce UD |
496 | plt[2] = OPCODE_LI (12, dlrr); |
497 | plt[3] = OPCODE_ADDIS (12, 12, (dlrr + 0x8000) >> 16); | |
4cca6b86 | 498 | plt[4] = OPCODE_MTCTR (12); |
5929563f UD |
499 | |
500 | /* Load address of link map in r12. */ | |
4cca6b86 | 501 | plt[5] = OPCODE_LI (12, (Elf32_Word) (char *) map); |
5929563f UD |
502 | plt[6] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map |
503 | + 0x8000) >> 16)); | |
504 | ||
505 | /* Call _dl_runtime_resolve. */ | |
4cca6b86 UD |
506 | plt[7] = OPCODE_BCTR (); |
507 | } | |
5929563f UD |
508 | |
509 | ||
510 | /* Convert the index in r11 into an actual address, and get the | |
511 | word at that address. */ | |
4cca6b86 | 512 | plt[PLT_LONGBRANCH_ENTRY_WORDS] = |
5929563f UD |
513 | OPCODE_ADDIS (11, 11, (((Elf32_Word) (char*) (plt + rel_offset_words) |
514 | + 0x8000) >> 16)); | |
4cca6b86 | 515 | plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = |
5929563f UD |
516 | OPCODE_LWZ (11, (Elf32_Word) (char*) (plt+rel_offset_words), 11); |
517 | ||
518 | /* Call the procedure at that address. */ | |
4cca6b86 UD |
519 | plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR (11); |
520 | plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR (); | |
e61abf83 | 521 | |
5929563f UD |
522 | |
523 | /* Now, we've modified code (quite a lot of code, possibly). We | |
524 | need to write the changes from the data cache to a | |
525 | second-level unified cache, then make sure that stale data in | |
526 | the instruction cache is removed. (In a multiprocessor | |
527 | system, the effect is more complex.) | |
528 | ||
529 | Assumes the cache line size is at least 32 bytes, or at least | |
530 | that dcbst and icbi apply to 32-byte lines. At present, all | |
531 | PowerPC processors have line sizes of exactly 32 bytes. */ | |
532 | ||
e61abf83 | 533 | size_modified = lazy ? rel_offset_words : PLT_INITIAL_ENTRY_WORDS; |
e61abf83 UD |
534 | for (i = 0; i < size_modified; i+=8) |
535 | PPC_DCBST (plt + i); | |
536 | PPC_SYNC; | |
537 | for (i = 0; i < size_modified; i+=8) | |
538 | PPC_ICBI (plt + i); | |
539 | PPC_ISYNC; | |
4cca6b86 | 540 | } |
0501d603 UD |
541 | |
542 | return lazy; | |
4cca6b86 UD |
543 | } |
544 | ||
545 | static inline void | |
b0cf070b | 546 | elf_machine_lazy_rel (Elf32_Addr l_addr, const Elf32_Rela *reloc) |
4cca6b86 | 547 | { |
4cca6b86 UD |
548 | /* elf_machine_runtime_setup handles this. */ |
549 | } | |
550 | ||
a2b08ee5 UD |
551 | static inline void |
552 | elf_machine_fixup_plt(struct link_map *map, const Elf32_Rela *reloc, | |
553 | Elf32_Addr *reloc_addr, Elf32_Addr finaladdr) | |
554 | { | |
555 | Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr; | |
556 | if (delta << 6 >> 6 == delta) | |
557 | *reloc_addr = OPCODE_B (delta); | |
558 | else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000) | |
559 | *reloc_addr = OPCODE_BA (finaladdr); | |
560 | else | |
561 | { | |
562 | Elf32_Word *plt; | |
563 | Elf32_Word index; | |
564 | ||
565 | plt = (Elf32_Word *)((char *)map->l_addr | |
566 | + map->l_info[DT_PLTGOT]->d_un.d_val); | |
567 | index = (reloc_addr - plt - PLT_INITIAL_ENTRY_WORDS)/2; | |
568 | if (index >= PLT_DOUBLE_SIZE) | |
569 | { | |
570 | /* Slots greater than or equal to 2^13 have 4 words available | |
571 | instead of two. */ | |
572 | /* FIXME: There are some possible race conditions in this code, | |
573 | when called from 'fixup'. | |
574 | ||
575 | 1) Suppose that a lazy PLT entry is executing, a context switch | |
576 | between threads (or a signal) occurs, and the new thread or | |
577 | signal handler calls the same lazy PLT entry. Then the PLT entry | |
578 | would be changed while it's being run, which will cause a segfault | |
579 | (almost always). | |
580 | ||
581 | 2) Suppose the reverse: that a lazy PLT entry is being updated, | |
582 | a context switch occurs, and the new code calls the lazy PLT | |
583 | entry that is being updated. Then the half-fixed PLT entry will | |
584 | be executed, which will also almost always cause a segfault. | |
585 | ||
586 | These problems don't happen with the 2-word entries, because | |
587 | only one of the two instructions are changed when a lazy entry | |
588 | is retargeted at the actual PLT entry; the li instruction stays | |
589 | the same (we have to update it anyway, because we might not be | |
590 | updating a lazy PLT entry). */ | |
591 | ||
592 | reloc_addr[0] = OPCODE_LI (11, finaladdr); | |
593 | reloc_addr[1] = OPCODE_ADDIS (11, 11, finaladdr + 0x8000 >> 16); | |
594 | reloc_addr[2] = OPCODE_MTCTR (11); | |
595 | reloc_addr[3] = OPCODE_BCTR (); | |
596 | } | |
597 | else | |
598 | { | |
599 | Elf32_Word num_plt_entries; | |
600 | ||
601 | num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val | |
602 | / sizeof(Elf32_Rela)); | |
603 | ||
604 | plt[index+PLT_DATA_START_WORDS (num_plt_entries)] = finaladdr; | |
605 | reloc_addr[0] = OPCODE_LI (11, index*4); | |
606 | reloc_addr[1] = OPCODE_B (-(4*(index*2 | |
607 | + 1 | |
608 | - PLT_LONGBRANCH_ENTRY_WORDS | |
609 | + PLT_INITIAL_ENTRY_WORDS))); | |
610 | } | |
611 | } | |
612 | MODIFIED_CODE (reloc_addr); | |
613 | } | |
614 | ||
dfd2257a UD |
615 | /* Return the final value of a plt relocation. */ |
616 | static inline Elf32_Addr | |
617 | elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc, | |
618 | Elf32_Addr value) | |
619 | { | |
620 | return value + reloc->r_addend; | |
621 | } | |
622 | ||
1f205a47 | 623 | #endif /* dl_machine_h */ |
4cca6b86 | 624 | |
1f205a47 | 625 | #ifdef RESOLVE |
4cca6b86 | 626 | |
1f205a47 UD |
627 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). |
628 | LOADADDR is the load address of the object; INFO is an array indexed | |
629 | by DT_* of the .dynamic section info. */ | |
4cca6b86 | 630 | |
650425ce | 631 | static void |
1f205a47 | 632 | elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, |
3996f34b | 633 | const Elf32_Sym *sym, const struct r_found_version *version, |
0413b54c | 634 | Elf32_Addr *const reloc_addr) |
1f205a47 | 635 | { |
0413b54c | 636 | #ifndef RTLD_BOOTSTRAP |
1f205a47 | 637 | const Elf32_Sym *const refsym = sym; |
9a0a462c | 638 | extern char **_dl_argv; |
0413b54c | 639 | #endif |
1f205a47 UD |
640 | Elf32_Word loadbase, finaladdr; |
641 | const int rinfo = ELF32_R_TYPE (reloc->r_info); | |
4cca6b86 | 642 | |
1f205a47 UD |
643 | if (rinfo == R_PPC_NONE) |
644 | return; | |
4cca6b86 | 645 | |
1f205a47 UD |
646 | assert (sym != NULL); |
647 | /* The condition on the next two lines is a hack around a bug in Solaris | |
648 | tools on Sparc. It's not clear whether it should really be here at all, | |
649 | but if not the binutils need to be changed. */ | |
650 | if ((sym->st_shndx != SHN_UNDEF | |
651 | && ELF32_ST_BIND (sym->st_info) == STB_LOCAL) | |
652 | || rinfo == R_PPC_RELATIVE) | |
653 | { | |
654 | /* Has already been relocated. */ | |
655 | loadbase = map->l_addr; | |
656 | finaladdr = loadbase + reloc->r_addend; | |
657 | } | |
658 | else | |
659 | { | |
660 | loadbase = (Elf32_Word) (char *) (RESOLVE (&sym, version, | |
661 | ELF32_R_TYPE(reloc->r_info))); | |
662 | if (sym == NULL) | |
663 | { | |
664 | /* Weak symbol that wasn't actually defined anywhere. */ | |
665 | assert(loadbase == 0); | |
666 | finaladdr = reloc->r_addend; | |
667 | } | |
668 | else | |
669 | finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value | |
670 | + reloc->r_addend); | |
671 | } | |
4cca6b86 | 672 | |
9a0a462c UD |
673 | /* This is still an if/else if chain because GCC uses the GOT to find |
674 | the table for table-based switch statements, and we haven't set it | |
675 | up yet. */ | |
1f205a47 UD |
676 | if (rinfo == R_PPC_UADDR32 || |
677 | rinfo == R_PPC_GLOB_DAT || | |
678 | rinfo == R_PPC_ADDR32 || | |
679 | rinfo == R_PPC_RELATIVE) | |
680 | { | |
681 | *reloc_addr = finaladdr; | |
682 | } | |
9a0a462c | 683 | #ifndef RTLD_BOOTSTRAP |
1f205a47 UD |
684 | else if (rinfo == R_PPC_ADDR16_LO) |
685 | { | |
686 | *(Elf32_Half*) reloc_addr = finaladdr; | |
687 | } | |
688 | else if (rinfo == R_PPC_ADDR16_HI) | |
689 | { | |
690 | *(Elf32_Half*) reloc_addr = finaladdr >> 16; | |
691 | } | |
692 | else if (rinfo == R_PPC_ADDR16_HA) | |
693 | { | |
694 | *(Elf32_Half*) reloc_addr = (finaladdr + 0x8000) >> 16; | |
695 | } | |
1f205a47 UD |
696 | else if (rinfo == R_PPC_REL24) |
697 | { | |
698 | Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr; | |
699 | if (delta << 6 >> 6 != delta) | |
700 | { | |
701 | _dl_signal_error(0, map->l_name, | |
702 | "R_PPC_REL24 relocation out of range"); | |
703 | } | |
704 | *reloc_addr = *reloc_addr & 0xfc000003 | delta & 0x3fffffc; | |
705 | } | |
706 | else if (rinfo == R_PPC_ADDR24) | |
707 | { | |
708 | if (finaladdr << 6 >> 6 != finaladdr) | |
709 | { | |
710 | _dl_signal_error(0, map->l_name, | |
711 | "R_PPC_ADDR24 relocation out of range"); | |
712 | } | |
713 | *reloc_addr = *reloc_addr & 0xfc000003 | finaladdr & 0x3fffffc; | |
714 | } | |
715 | else if (rinfo == R_PPC_COPY) | |
716 | { | |
5107cf1d UD |
717 | if (sym == NULL) |
718 | /* This can happen in trace mode when an object could not be | |
719 | found. */ | |
720 | return; | |
cf29ffbe UD |
721 | if (sym->st_size > refsym->st_size |
722 | || (_dl_verbose && sym->st_size < refsym->st_size)) | |
1f205a47 UD |
723 | { |
724 | const char *strtab; | |
5929563f | 725 | |
1f205a47 UD |
726 | strtab = ((void *) map->l_addr |
727 | + map->l_info[DT_STRTAB]->d_un.d_ptr); | |
60c96635 UD |
728 | _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>", |
729 | ": Symbol `", strtab + refsym->st_name, | |
1f205a47 UD |
730 | "' has different size in shared object, " |
731 | "consider re-linking\n", NULL); | |
732 | } | |
733 | memcpy (reloc_addr, (char *) finaladdr, MIN (sym->st_size, | |
734 | refsym->st_size)); | |
735 | } | |
736 | #endif | |
737 | else if (rinfo == R_PPC_REL32) | |
738 | { | |
739 | *reloc_addr = finaladdr - (Elf32_Word) (char *) reloc_addr; | |
740 | } | |
741 | else if (rinfo == R_PPC_JMP_SLOT) | |
742 | { | |
af6f3906 | 743 | elf_machine_fixup_plt (map, reloc, reloc_addr, finaladdr); |
1f205a47 UD |
744 | } |
745 | else | |
0413b54c UD |
746 | { |
747 | #ifdef RTLD_BOOTSTRAP | |
748 | PPC_DIE; /* There is no point calling _dl_sysdep_error, it | |
749 | almost certainly hasn't been relocated properly. */ | |
750 | #else | |
751 | _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>", | |
752 | ": Unknown relocation type\n", NULL); | |
753 | #endif | |
754 | } | |
1f205a47 | 755 | |
9a0a462c | 756 | #ifndef RTLD_BOOTSTRAP |
1f205a47 UD |
757 | if (rinfo == R_PPC_ADDR16_LO || |
758 | rinfo == R_PPC_ADDR16_HI || | |
759 | rinfo == R_PPC_ADDR16_HA || | |
760 | rinfo == R_PPC_REL24 || | |
761 | rinfo == R_PPC_ADDR24) | |
762 | MODIFIED_CODE_NOQUEUE (reloc_addr); | |
9a0a462c | 763 | #endif |
1f205a47 UD |
764 | } |
765 | ||
766 | #define ELF_MACHINE_NO_REL 1 | |
767 | ||
650425ce | 768 | #endif /* RESOLVE */ |