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