]>
Commit | Line | Data |
---|---|---|
cfc91acd RM |
1 | /* Machine-dependent ELF dynamic relocation inline functions. |
2 | PowerPC64 version. | |
7ba7c829 | 3 | Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 |
cfc91acd RM |
4 | Free Software Foundation, Inc. |
5 | This file is part of the GNU C Library. | |
6 | ||
7 | The GNU C Library is free software; you can redistribute it and/or | |
8 | modify it under the terms of the GNU Library General Public License as | |
9 | published by the Free Software Foundation; either version 2 of the | |
10 | License, or (at your option) any later version. | |
11 | ||
12 | The GNU C Library is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Library General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Library General Public | |
18 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
19 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 | Boston, MA 02111-1307, USA. */ | |
21 | ||
22 | #ifndef dl_machine_h | |
23 | #define dl_machine_h | |
24 | ||
25 | #define ELF_MACHINE_NAME "powerpc64" | |
26 | ||
27 | #include <assert.h> | |
28 | #include <sys/param.h> | |
29 | ||
30 | /* Translate a processor specific dynamic tag to the index | |
31 | in l_info array. */ | |
32 | #define DT_PPC64(x) (DT_PPC64_##x - DT_LOPROC + DT_NUM) | |
33 | ||
34 | /* A PowerPC64 function descriptor. The .plt (procedure linkage | |
35 | table) and .opd (official procedure descriptor) sections are | |
36 | arrays of these. */ | |
37 | typedef struct | |
38 | { | |
39 | Elf64_Addr fd_func; | |
40 | Elf64_Addr fd_toc; | |
41 | Elf64_Addr fd_aux; | |
42 | } Elf64_FuncDesc; | |
43 | ||
44 | #define ELF_MULT_MACHINES_SUPPORTED | |
45 | ||
46 | /* Return nonzero iff ELF header is compatible with the running host. */ | |
47 | static inline int | |
48 | elf_machine_matches_host (const Elf64_Ehdr *ehdr) | |
49 | { | |
50 | return ehdr->e_machine == EM_PPC64; | |
51 | } | |
52 | ||
53 | /* Return nonzero iff ELF header is compatible with the running host, | |
54 | but not this loader. */ | |
55 | static inline int | |
56 | elf_host_tolerates_machine (const Elf64_Ehdr *ehdr) | |
57 | { | |
58 | return ehdr->e_machine == EM_PPC; | |
59 | } | |
60 | ||
61 | /* Return nonzero iff ELF header is compatible with the running host, | |
62 | but not this loader. */ | |
63 | static inline int | |
64 | elf_host_tolerates_class (const Elf64_Ehdr *ehdr) | |
65 | { | |
66 | return ehdr->e_ident[EI_CLASS] == ELFCLASS32; | |
67 | } | |
68 | ||
69 | ||
70 | /* Return the run-time load address of the shared object, assuming it | |
71 | was originally linked at zero. */ | |
72 | static inline Elf64_Addr | |
73 | elf_machine_load_address (void) __attribute__ ((const)); | |
74 | ||
75 | static inline Elf64_Addr | |
76 | elf_machine_load_address (void) | |
77 | { | |
78 | Elf64_Addr ret; | |
79 | ||
80 | /* The first entry in .got (and thus the first entry in .toc) is the | |
81 | link-time TOC_base, ie. r2. So the difference between that and | |
82 | the current r2 set by the kernel is how far the shared lib has | |
83 | moved. */ | |
84 | asm ( " ld %0,-32768(2)\n" | |
85 | " subf %0,%0,2\n" | |
86 | : "=r" (ret)); | |
87 | return ret; | |
88 | } | |
89 | ||
90 | /* Return the link-time address of _DYNAMIC. */ | |
91 | static inline Elf64_Addr | |
92 | elf_machine_dynamic (void) | |
93 | { | |
94 | Elf64_Addr runtime_dynamic; | |
95 | /* It's easier to get the run-time address. */ | |
96 | asm ( " addis %0,2,_DYNAMIC@toc@ha\n" | |
97 | " addi %0,%0,_DYNAMIC@toc@l\n" | |
98 | : "=b" (runtime_dynamic)); | |
99 | /* Then subtract off the load address offset. */ | |
100 | return runtime_dynamic - elf_machine_load_address() ; | |
101 | } | |
102 | ||
103 | #define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) /* nothing */ | |
104 | ||
105 | /* The PLT uses Elf64_Rela relocs. */ | |
106 | #define elf_machine_relplt elf_machine_rela | |
107 | ||
108 | /* This code gets called via a .glink stub which loads PLT0. It is | |
109 | used in dl-runtime.c to call the `fixup' function and then redirect | |
110 | to the address `fixup' returns. | |
111 | ||
112 | Enter with r0 = plt reloc index, | |
113 | r2 = ld.so tocbase, | |
114 | r11 = ld.so link map. */ | |
115 | ||
116 | #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \ | |
117 | asm (".section \".text\"\n" \ | |
118 | " .align 2\n" \ | |
119 | " .globl ." #tramp_name "\n" \ | |
120 | " .type ." #tramp_name ",@function\n" \ | |
121 | " .section \".opd\",\"aw\"\n" \ | |
122 | " .align 3\n" \ | |
123 | " .globl " #tramp_name "\n" \ | |
124 | " .size " #tramp_name ",24\n" \ | |
125 | #tramp_name ":\n" \ | |
126 | " .quad ." #tramp_name ",.TOC.@tocbase,0\n" \ | |
127 | " .previous\n" \ | |
128 | "." #tramp_name ":\n" \ | |
129 | /* We need to save the registers used to pass parameters, ie. r3 thru \ | |
130 | r10; the registers are saved in a stack frame. */ \ | |
131 | " stdu 1,-128(1)\n" \ | |
132 | " std 3,48(1)\n" \ | |
133 | " mr 3,11\n" \ | |
134 | " std 4,56(1)\n" \ | |
135 | " sldi 4,0,1\n" \ | |
136 | " std 5,64(1)\n" \ | |
137 | " add 4,4,0\n" \ | |
138 | " std 6,72(1)\n" \ | |
139 | " sldi 4,4,3\n" \ | |
140 | " std 7,80(1)\n" \ | |
141 | " mflr 0\n" \ | |
142 | " std 8,88(1)\n" \ | |
143 | /* Store the LR in the LR Save area of the previous frame. */ \ | |
144 | " std 0,128+16(1)\n" \ | |
145 | " mfcr 0\n" \ | |
146 | " std 9,96(1)\n" \ | |
147 | " std 10,104(1)\n" \ | |
148 | /* I'm almost certain we don't have to save cr... be safe. */ \ | |
149 | " std 0,8(1)\n" \ | |
150 | " bl ." #fixup_name "\n" \ | |
151 | /* Put the registers back. */ \ | |
152 | " ld 0,128+16(1)\n" \ | |
153 | " ld 10,104(1)\n" \ | |
154 | " ld 9,96(1)\n" \ | |
155 | " ld 8,88(1)\n" \ | |
156 | " ld 7,80(1)\n" \ | |
157 | " mtlr 0\n" \ | |
158 | " ld 0,8(1)\n" \ | |
159 | " ld 6,72(1)\n" \ | |
160 | " ld 5,64(1)\n" \ | |
161 | " ld 4,56(1)\n" \ | |
162 | " mtcrf 0xFF,0\n" \ | |
163 | /* Load the target address, toc and static chain reg from the function \ | |
164 | descriptor returned by fixup. */ \ | |
165 | " ld 0,0(3)\n" \ | |
166 | " ld 2,8(3)\n" \ | |
167 | " mtctr 0\n" \ | |
168 | " ld 11,16(3)\n" \ | |
169 | " ld 3,48(1)\n" \ | |
170 | /* Unwind the stack frame, and jump. */ \ | |
171 | " addi 1,1,128\n" \ | |
172 | " bctr\n" \ | |
173 | ".LT_" #tramp_name ":\n" \ | |
174 | " .long 0\n" \ | |
175 | " .byte 0x00,0x0c,0x24,0x40,0x00,0x00,0x00,0x00\n" \ | |
176 | " .long .LT_" #tramp_name "-."#tramp_name "\n" \ | |
177 | " .short .LT_" #tramp_name "_name_end-.LT_" #tramp_name "_name_start\n" \ | |
178 | ".LT_" #tramp_name "_name_start:\n" \ | |
179 | " .ascii \"" #tramp_name "\"\n" \ | |
180 | ".LT_" #tramp_name "_name_end:\n" \ | |
181 | " .align 2\n" \ | |
182 | " .size ." #tramp_name ",. - ." #tramp_name "\n" \ | |
183 | " .previous"); | |
184 | ||
185 | #ifndef PROF | |
186 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE \ | |
187 | TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \ | |
188 | TRAMPOLINE_TEMPLATE (_dl_profile_resolve, profile_fixup); | |
189 | #else | |
190 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE \ | |
191 | TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \ | |
192 | void _dl_runtime_resolve (void); \ | |
193 | strong_alias (_dl_runtime_resolve, _dl_profile_resolve); | |
194 | #endif | |
195 | ||
196 | ||
197 | /* Initial entry point code for the dynamic linker. The C function | |
198 | `_dl_start' is the real entry point; its return value is the user | |
199 | program's entry point. */ | |
200 | #define RTLD_START \ | |
201 | asm (".section \".text\"\n" \ | |
202 | " .align 2\n" \ | |
203 | " .globl ._start\n" \ | |
204 | " .type ._start,@function\n" \ | |
205 | " .section \".opd\",\"aw\"\n" \ | |
206 | " .align 3\n" \ | |
207 | " .globl _start\n" \ | |
208 | " .size _start,24\n" \ | |
209 | "_start:\n" \ | |
210 | " .quad ._start,.TOC.@tocbase,0\n" \ | |
211 | " .previous\n" \ | |
212 | "._start:\n" \ | |
213 | /* We start with the following on the stack, from top: \ | |
214 | argc (4 bytes); \ | |
215 | arguments for program (terminated by NULL); \ | |
216 | environment variables (terminated by NULL); \ | |
217 | arguments for the program loader. */ \ | |
218 | " mr 3,1\n" \ | |
219 | " li 4,0\n" \ | |
220 | " stdu 4,-128(1)\n" \ | |
221 | /* Call _dl_start with one parameter pointing at argc. */ \ | |
222 | " bl ._dl_start\n" \ | |
223 | " nop\n" \ | |
224 | /* Transfer control to _dl_start_user! */ \ | |
225 | " b ._dl_start_user\n" \ | |
226 | ".LT__start:\n" \ | |
227 | " .long 0\n" \ | |
228 | " .byte 0x00,0x0c,0x24,0x40,0x00,0x00,0x00,0x00\n" \ | |
229 | " .long .LT__start-._start\n" \ | |
230 | " .short .LT__start_name_end-.LT__start_name_start\n" \ | |
231 | ".LT__start_name_start:\n" \ | |
232 | " .ascii \"_start\"\n" \ | |
233 | ".LT__start_name_end:\n" \ | |
234 | " .align 2\n" \ | |
235 | " .size ._start,.-._start\n" \ | |
236 | " .globl _dl_start_user\n" \ | |
237 | " .section \".opd\",\"aw\"\n" \ | |
238 | "_dl_start_user:\n" \ | |
239 | " .quad ._dl_start_user, .TOC.@tocbase, 0\n" \ | |
240 | " .previous\n" \ | |
241 | " .globl ._dl_start_user\n" \ | |
242 | " .type ._dl_start_user,@function\n" \ | |
243 | /* Now, we do our main work of calling initialisation procedures. \ | |
244 | The ELF ABI doesn't say anything about parameters for these, \ | |
245 | so we just pass argc, argv, and the environment. \ | |
246 | Changing these is strongly discouraged (not least because argc is \ | |
247 | passed by value!). */ \ | |
248 | "._dl_start_user:\n" \ | |
249 | /* the address of _start in r30. */ \ | |
250 | " mr 30,3\n" \ | |
251 | /* &_dl_argc in 29, &_dl_argv in 27, and _dl_loaded in 28. */ \ | |
252 | " ld 28,_rtld_global@got(2)\n" \ | |
253 | " ld 29,_dl_argc@got(2)\n" \ | |
254 | " ld 27,_dl_argv@got(2)\n" \ | |
255 | /* _dl_init (_dl_loaded, _dl_argc, _dl_argv, _dl_argv+_dl_argc+1). */ \ | |
256 | " ld 3,0(28)\n" \ | |
257 | " lwa 4,0(29)\n" \ | |
258 | " ld 5,0(27)\n" \ | |
259 | " sldi 6,4,3\n" \ | |
260 | " add 6,5,6\n" \ | |
261 | " addi 6,6,8\n" \ | |
262 | " bl ._dl_init\n" \ | |
263 | " nop\n" \ | |
264 | /* Now, to conform to the ELF ABI, we have to: \ | |
265 | Pass argc (actually _dl_argc) in r3; */ \ | |
266 | " lwa 3,0(29)\n" \ | |
267 | /* Pass argv (actually _dl_argv) in r4; */ \ | |
268 | " ld 4,0(27)\n" \ | |
269 | /* Pass argv+argc+1 in r5; */ \ | |
270 | " sldi 5,3,3\n" \ | |
271 | " add 6,4,5\n" \ | |
272 | " addi 5,6,8\n" \ | |
273 | /* Pass the auxilary vector in r6. This is passed to us just after \ | |
274 | _envp. */ \ | |
275 | "2: ldu 0,8(6)\n" \ | |
276 | " cmpdi 0,0\n" \ | |
277 | " bne 2b\n" \ | |
278 | " addi 6,6,8\n" \ | |
279 | /* Pass a termination function pointer (in this case _dl_fini) in \ | |
280 | r7. */ \ | |
281 | " ld 7,_dl_fini@got(2)\n" \ | |
282 | " ld 26,_dl_starting_up@got(2)\n" \ | |
283 | /* Pass the stack pointer in r1 (so far so good), pointing to a NULL \ | |
284 | value. This lets our startup code distinguish between a program \ | |
285 | linked statically, which linux will call with argc on top of the \ | |
286 | stack which will hopefully never be zero, and a dynamically linked \ | |
287 | program which will always have a NULL on the top of the stack. \ | |
288 | Take the opportunity to clear LR, so anyone who accidentally \ | |
289 | returns from _start gets SEGV. Also clear the next few words of \ | |
290 | the stack. */ \ | |
291 | " li 31,0\n" \ | |
292 | " std 31,0(1)\n" \ | |
293 | " mtlr 31\n" \ | |
294 | " std 31,8(1)\n" \ | |
295 | " std 31,16(1)\n" \ | |
296 | " std 31,24(1)\n" \ | |
297 | /* Clear _dl_starting_up. */ \ | |
298 | " stw 31,0(26)\n" \ | |
299 | /* Now, call the start function descriptor at r30... */ \ | |
300 | " .globl ._dl_main_dispatch\n" \ | |
301 | "._dl_main_dispatch:\n" \ | |
302 | " ld 0,0(30)\n" \ | |
303 | " ld 2,8(30)\n" \ | |
304 | " mtctr 0\n" \ | |
305 | " ld 11,16(30)\n" \ | |
306 | " bctr\n" \ | |
307 | ".LT__dl_start_user:\n" \ | |
308 | " .long 0\n" \ | |
309 | " .byte 0x00,0x0c,0x24,0x40,0x00,0x00,0x00,0x00\n" \ | |
310 | " .long .LT__dl_start_user-._dl_start_user\n" \ | |
311 | " .short .LT__dl_start_user_name_end-.LT__dl_start_user_name_start\n" \ | |
312 | ".LT__dl_start_user_name_start:\n" \ | |
313 | " .ascii \"_dl_start_user\"\n" \ | |
314 | ".LT__dl_start_user_name_end:\n" \ | |
315 | " .align 2\n" \ | |
316 | " .size ._dl_start_user,.-._dl_start_user\n" \ | |
317 | " .previous"); | |
318 | ||
319 | /* Nonzero iff TYPE should not be allowed to resolve to one of | |
320 | the main executable's symbols, as for a COPY reloc. */ | |
321 | #define elf_machine_lookup_noexec_p(type) ((type) == R_PPC64_COPY) | |
322 | ||
323 | /* Nonzero iff TYPE describes relocation of a PLT entry, so | |
324 | PLT entries should not be allowed to define the value. */ | |
325 | #define elf_machine_lookup_noplt_p(type) ((type) == R_PPC64_JMP_SLOT) | |
326 | ||
327 | /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so | |
328 | PLT entries should not be allowed to define the value. | |
329 | ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one | |
330 | of the main executable's symbols, as for a COPY reloc. */ | |
afafddab RM |
331 | |
332 | #if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD) | |
333 | #define elf_machine_type_class(type) \ | |
334 | ( (((type) == R_PPC64_DTPMOD64 \ | |
335 | || (type) == R_PPC64_DTPREL64 \ | |
336 | || (type) == R_PPC64_TPREL64 \ | |
337 | || (type) == R_PPC64_ADDR24) * ELF_RTYPE_CLASS_PLT) \ | |
338 | | (((type) == R_PPC64_COPY) * ELF_RTYPE_CLASS_COPY)) | |
339 | #else | |
cfc91acd RM |
340 | #define elf_machine_type_class(type) \ |
341 | ((((type) == R_PPC64_ADDR24) * ELF_RTYPE_CLASS_PLT) \ | |
342 | | (((type) == R_PPC64_COPY) * ELF_RTYPE_CLASS_COPY)) | |
afafddab | 343 | #endif |
cfc91acd RM |
344 | |
345 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ | |
346 | #define ELF_MACHINE_JMP_SLOT R_PPC64_JMP_SLOT | |
347 | ||
348 | /* The PowerPC never uses REL relocations. */ | |
349 | #define ELF_MACHINE_NO_REL 1 | |
350 | ||
351 | /* Stuff for the PLT. */ | |
352 | #define PLT_INITIAL_ENTRY_WORDS 3 | |
353 | #define GLINK_INITIAL_ENTRY_WORDS 8 | |
354 | ||
355 | #define PPC_DCBST(where) asm volatile ("dcbst 0,%0" : : "r"(where) : "memory") | |
356 | #define PPC_SYNC asm volatile ("sync" : : : "memory") | |
357 | #define PPC_ISYNC asm volatile ("sync; isync" : : : "memory") | |
358 | #define PPC_ICBI(where) asm volatile ("icbi 0,%0" : : "r"(where) : "memory") | |
359 | #define PPC_DIE asm volatile ("tweq 0,0") | |
360 | /* Use this when you've modified some code, but it won't be in the | |
361 | instruction fetch queue (or when it doesn't matter if it is). */ | |
362 | #define MODIFIED_CODE_NOQUEUE(where) \ | |
363 | do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); } while (0) | |
364 | /* Use this when it might be in the instruction queue. */ | |
365 | #define MODIFIED_CODE(where) \ | |
366 | do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); PPC_ISYNC; } while (0) | |
367 | ||
368 | /* Set up the loaded object described by MAP so its unrelocated PLT | |
369 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
370 | static inline int | |
371 | elf_machine_runtime_setup (struct link_map *map, int lazy, int profile) | |
372 | { | |
373 | if (map->l_info[DT_JMPREL]) | |
374 | { | |
375 | Elf64_Word i; | |
376 | Elf64_Word *glink = NULL; | |
377 | Elf64_Xword *plt = (Elf64_Xword *) D_PTR (map, l_info[DT_PLTGOT]); | |
378 | Elf64_Word num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val | |
379 | / sizeof (Elf64_Rela)); | |
380 | Elf64_Addr l_addr = map->l_addr; | |
381 | Elf64_Dyn **info = map->l_info; | |
382 | char *p; | |
383 | ||
384 | extern void _dl_runtime_resolve (void); | |
385 | extern void _dl_profile_resolve (void); | |
386 | ||
387 | /* Relocate the DT_PPC64_GLINK entry in the _DYNAMIC section. | |
388 | elf_get_dynamic_info takes care of the standard entries but | |
389 | doesn't know exactly what to do with processor specific | |
390 | entires. */ | |
391 | if (info[DT_PPC64(GLINK)] != NULL) | |
392 | info[DT_PPC64(GLINK)]->d_un.d_ptr += l_addr; | |
393 | ||
394 | if (lazy) | |
395 | { | |
396 | /* The function descriptor of the appropriate trampline | |
397 | routine is used to set the 1st and 2nd doubleword of the | |
398 | plt_reserve. */ | |
399 | Elf64_FuncDesc *resolve_fd; | |
400 | Elf64_Word glink_offset; | |
401 | /* the plt_reserve area is the 1st 3 doublewords of the PLT */ | |
402 | Elf64_FuncDesc *plt_reserve = (Elf64_FuncDesc *) plt; | |
403 | Elf64_Word offset; | |
404 | ||
405 | resolve_fd = (Elf64_FuncDesc *) (profile ? _dl_profile_resolve | |
406 | : _dl_runtime_resolve); | |
407 | if (profile && _dl_name_match_p (GL(dl_profile), map)) | |
408 | /* This is the object we are looking for. Say that we really | |
409 | want profiling and the timers are started. */ | |
410 | GL(dl_profile_map) = map; | |
411 | ||
412 | ||
413 | /* We need to stuff the address/TOC of _dl_runtime_resolve | |
414 | into doublewords 0 and 1 of plt_reserve. Then we need to | |
415 | stuff the map address into doubleword 2 of plt_reserve. | |
416 | This allows the GLINK0 code to transfer control to the | |
417 | correct trampoline which will transfer control to fixup | |
418 | in dl-machine.c. */ | |
419 | plt_reserve->fd_func = resolve_fd->fd_func; | |
420 | plt_reserve->fd_toc = resolve_fd->fd_toc; | |
421 | plt_reserve->fd_aux = (Elf64_Addr) map; | |
422 | #ifdef RTLD_BOOTSTRAP | |
423 | /* When we're bootstrapping, the opd entry will not have | |
424 | been relocated yet. */ | |
425 | plt_reserve->fd_func += l_addr; | |
426 | plt_reserve->fd_toc += l_addr; | |
427 | #endif | |
428 | ||
429 | /* Set up the lazy PLT entries. */ | |
430 | glink = (Elf64_Word *) D_PTR (map, l_info[DT_PPC64(GLINK)]); | |
431 | offset = PLT_INITIAL_ENTRY_WORDS; | |
432 | glink_offset = GLINK_INITIAL_ENTRY_WORDS; | |
433 | for (i = 0; i < num_plt_entries; i++) | |
434 | { | |
435 | ||
436 | plt[offset] = (Elf64_Xword) &glink[glink_offset]; | |
437 | offset += 3; | |
438 | /* The first 32k entries of glink can set an index and | |
439 | branch using two instructions; Past that point, | |
440 | glink uses three instructions. */ | |
441 | if (i < 0x8000) | |
442 | glink_offset += 2; | |
443 | else | |
444 | glink_offset += 3; | |
445 | } | |
446 | ||
447 | /* Now, we've modified data. We need to write the changes from | |
448 | the data cache to a second-level unified cache, then make | |
449 | sure that stale data in the instruction cache is removed. | |
450 | (In a multiprocessor system, the effect is more complex.) | |
451 | Most of the PLT shouldn't be in the instruction cache, but | |
452 | there may be a little overlap at the start and the end. | |
453 | ||
454 | Assumes that dcbst and icbi apply to lines of 16 bytes or | |
455 | more. Current known line sizes are 16, 32, and 128 bytes. */ | |
456 | ||
457 | for (p = (char *) plt; p < (char *) &plt[offset]; p += 16) | |
458 | PPC_DCBST (p); | |
459 | PPC_SYNC; | |
460 | } | |
461 | } | |
462 | return lazy; | |
463 | } | |
464 | ||
cfc91acd RM |
465 | /* Change the PLT entry whose reloc is 'reloc' to call the actual |
466 | routine. */ | |
467 | static inline Elf64_Addr | |
468 | elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map, | |
469 | const Elf64_Rela *reloc, | |
470 | Elf64_Addr *reloc_addr, Elf64_Addr finaladdr) | |
471 | { | |
472 | Elf64_FuncDesc *plt = (Elf64_FuncDesc *) reloc_addr; | |
473 | Elf64_FuncDesc *rel = (Elf64_FuncDesc *) finaladdr; | |
474 | Elf64_Addr offset = 0; | |
475 | #ifndef RTLD_BOOTSTRAP | |
476 | weak_extern (GL(dl_rtld_map)); | |
477 | #endif | |
478 | ||
479 | /* If sym_map is NULL, it's a weak undefined sym; Leave the plt zero. */ | |
480 | if (sym_map == NULL) | |
481 | return 0; | |
482 | ||
483 | /* If the opd entry is not yet relocated (because it's from a shared | |
484 | object that hasn't been processed yet), then manually reloc it. */ | |
485 | if (map != sym_map && !sym_map->l_relocated | |
486 | #ifndef RTLD_BOOTSTRAP | |
487 | /* Bootstrap map doesn't have l_relocated set for it. */ | |
488 | && sym_map != &GL(dl_rtld_map) | |
489 | #endif | |
490 | ) | |
491 | offset = sym_map->l_addr; | |
492 | ||
493 | /* For PPC64, fixup_plt copies the function descriptor from opd | |
494 | over the corresponding PLT entry. | |
495 | Initially, PLT Entry[i] is set up for lazy linking, or is zero. | |
496 | For lazy linking, the fd_toc and fd_aux entries are irrelevant, | |
497 | so for thread safety we write them before changing fd_func. */ | |
498 | ||
499 | plt->fd_aux = rel->fd_aux + offset; | |
500 | plt->fd_toc = rel->fd_toc + offset; | |
501 | PPC_DCBST (&plt->fd_aux); | |
502 | PPC_DCBST (&plt->fd_toc); | |
503 | PPC_SYNC; | |
504 | ||
505 | plt->fd_func = rel->fd_func + offset; | |
506 | PPC_DCBST (&plt->fd_func); | |
507 | PPC_SYNC; | |
508 | ||
509 | return finaladdr; | |
510 | } | |
511 | ||
512 | /* Return the final value of a plt relocation. */ | |
513 | static inline Elf64_Addr | |
514 | elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc, | |
515 | Elf64_Addr value) | |
516 | { | |
517 | return value + reloc->r_addend; | |
518 | } | |
519 | ||
520 | #endif /* dl_machine_h */ | |
521 | ||
522 | #ifdef RESOLVE_MAP | |
523 | ||
524 | #define PPC_LO(v) ((v) & 0xffff) | |
525 | #define PPC_HI(v) (((v) >> 16) & 0xffff) | |
526 | #define PPC_HA(v) PPC_HI ((v) + 0x8000) | |
527 | #define PPC_HIGHER(v) (((v) >> 32) & 0xffff) | |
528 | #define PPC_HIGHERA(v) PPC_HIGHER ((v) + 0x8000) | |
529 | #define PPC_HIGHEST(v) (((v) >> 48) & 0xffff) | |
530 | #define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000) | |
531 | #define BIT_INSERT(old, val, mask) ((old & ~(Elf64_Addr) mask) | (val & mask)) | |
532 | ||
533 | #define dont_expect(X) __builtin_expect ((X), 0) | |
534 | ||
535 | extern void _dl_reloc_overflow (struct link_map *map, | |
536 | const char *name, | |
537 | Elf64_Addr *const reloc_addr, | |
538 | const Elf64_Sym *sym, | |
539 | const Elf64_Sym *refsym) | |
540 | attribute_hidden; | |
541 | ||
542 | static inline void | |
543 | elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc, | |
544 | Elf64_Addr *const reloc_addr) | |
545 | { | |
546 | *reloc_addr = l_addr + reloc->r_addend; | |
afafddab | 547 | } |
cfc91acd RM |
548 | |
549 | /* Perform the relocation specified by RELOC and SYM (which is fully | |
550 | resolved). MAP is the object containing the reloc. */ | |
551 | static inline void | |
552 | elf_machine_rela (struct link_map *map, | |
553 | const Elf64_Rela *reloc, | |
554 | const Elf64_Sym *sym, | |
555 | const struct r_found_version *version, | |
556 | Elf64_Addr *const reloc_addr) | |
557 | { | |
558 | int r_type = ELF64_R_TYPE (reloc->r_info); | |
559 | struct link_map *sym_map; | |
afafddab | 560 | Elf64_Addr value, raw_value; |
cfc91acd RM |
561 | #ifndef RTLD_BOOTSTRAP |
562 | const Elf64_Sym *const refsym = sym; | |
563 | /* This is defined in rtld.c, but nowhere in the static libc.a; make the | |
564 | reference weak so static programs can still link. This declaration | |
565 | cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP) | |
566 | because rtld.c contains the common defn for _dl_rtld_map, which is | |
567 | incompatible with a weak decl in the same file. */ | |
568 | weak_extern (GL(dl_rtld_map)); | |
569 | #endif | |
570 | ||
571 | if (r_type == R_PPC64_RELATIVE) | |
572 | { | |
573 | #ifndef RTLD_BOOTSTRAP | |
574 | /* Already done in dynamic linker. */ | |
575 | if (map != &GL(dl_rtld_map)) | |
576 | #endif | |
afafddab | 577 | *reloc_addr = map->l_addr + reloc->r_addend; |
cfc91acd RM |
578 | return; |
579 | } | |
580 | ||
581 | if (r_type == R_PPC64_NONE) | |
582 | return; | |
583 | ||
4e5b051f | 584 | value = 0; |
afafddab RM |
585 | raw_value = 0; |
586 | ||
587 | #if defined USE_TLS && !defined RTLD_BOOTSTRAP | |
588 | sym_map = RESOLVE_MAP (&sym, version, r_type); | |
589 | raw_value = value = reloc->r_addend; | |
1d02f71f | 590 | if (sym_map) |
afafddab | 591 | if (sym) |
1d02f71f | 592 | { |
afafddab RM |
593 | raw_value += sym->st_value; |
594 | value = raw_value + sym_map->l_addr; | |
1d02f71f | 595 | } |
afafddab RM |
596 | #else |
597 | sym_map = RESOLVE_MAP (&sym, version, r_type); | |
598 | if (sym_map) | |
599 | { | |
600 | if (sym) | |
601 | { | |
602 | raw_value = sym->st_value + sym_map->l_addr; | |
603 | } | |
604 | value = raw_value + reloc->r_addend; | |
605 | } | |
606 | #endif | |
cfc91acd RM |
607 | |
608 | switch (r_type) | |
609 | { | |
610 | case R_PPC64_ADDR64: | |
611 | case R_PPC64_GLOB_DAT: | |
afafddab | 612 | *reloc_addr = value; |
cfc91acd | 613 | return; |
afafddab RM |
614 | |
615 | #if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD) | |
616 | ||
617 | case R_PPC64_DTPMOD64: | |
618 | #ifdef RTLD_BOOTSTRAP | |
619 | /* During startup the dynamic linker is always index 1. */ | |
620 | *reloc_addr = 1; | |
621 | #else | |
622 | /* Get the information from the link map returned by the | |
623 | resolv function. */ | |
624 | if (sym_map != NULL) | |
625 | { | |
626 | *reloc_addr = sym_map->l_tls_modid; | |
627 | } | |
628 | #endif | |
629 | return; | |
630 | ||
631 | case R_PPC64_TPREL64: | |
632 | #ifdef RTLD_BOOTSTRAP | |
633 | *reloc_addr = raw_value + map->l_tls_offset - 0x7010; | |
634 | #else | |
635 | if (sym_map) | |
636 | { | |
637 | CHECK_STATIC_TLS (map, sym_map); | |
638 | *reloc_addr = raw_value + sym_map->l_tls_offset - 0x7010; | |
639 | } | |
640 | #endif | |
641 | return; | |
642 | ||
643 | case R_PPC64_DTPREL64: | |
644 | #ifndef RTLD_BOOTSTRAP | |
645 | /* During relocation all TLS symbols are defined and used. | |
646 | Therefore the offset is already correct. */ | |
647 | *reloc_addr = raw_value - 0x8000; | |
648 | #endif | |
649 | return; | |
650 | #endif | |
cfc91acd RM |
651 | |
652 | case R_PPC64_JMP_SLOT: | |
653 | ||
654 | elf_machine_fixup_plt (map, sym_map, reloc, reloc_addr, value); | |
655 | return; | |
656 | ||
657 | #ifndef RTLD_BOOTSTRAP /* None of the following appear in ld.so */ | |
658 | case R_PPC64_ADDR16_LO_DS: | |
659 | if (dont_expect ((value & 3) != 0)) | |
660 | _dl_reloc_overflow (map, "R_PPC64_ADDR16_LO_DS", | |
661 | reloc_addr, sym, refsym); | |
662 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
663 | value, 0xfffc); | |
664 | break; | |
665 | ||
afafddab RM |
666 | case R_PPC64_TPREL16_LO_DS: |
667 | if (dont_expect ((value & 3) != 0)) | |
668 | _dl_reloc_overflow (map, "R_PPC64_TPREL16_LO_DS", | |
669 | reloc_addr, sym, refsym); | |
670 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
671 | value, 0xfffc); | |
672 | break; | |
673 | ||
674 | case R_PPC64_DTPREL16_LO_DS: | |
675 | if (dont_expect ((value & 3) != 0)) | |
676 | _dl_reloc_overflow (map, "R_PPC64_DTPREL16_LO_DS", | |
677 | reloc_addr, sym, refsym); | |
678 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
679 | value, 0xfffc); | |
680 | break; | |
681 | ||
682 | case R_PPC64_GOT_TPREL16_LO_DS: | |
683 | if (dont_expect ((value & 3) != 0)) | |
684 | _dl_reloc_overflow (map, "R_PPC64_GOT_TPREL16_LO_DS", | |
685 | reloc_addr, sym, refsym); | |
686 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
687 | value, 0xfffc); | |
688 | break; | |
689 | ||
690 | case R_PPC64_GOT_DTPREL16_LO_DS: | |
691 | if (dont_expect ((value & 3) != 0)) | |
692 | _dl_reloc_overflow (map, "R_PPC64_GOT_DTPREL16_LO_DS", | |
693 | reloc_addr, sym, refsym); | |
694 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
695 | value, 0xfffc); | |
696 | break; | |
697 | ||
cfc91acd | 698 | case R_PPC64_ADDR16_LO: |
afafddab RM |
699 | case R_PPC64_TPREL16_LO: |
700 | case R_PPC64_DTPREL16_LO: | |
701 | case R_PPC64_GOT_TLSGD16_LO: | |
702 | case R_PPC64_GOT_TLSLD16_LO: | |
cfc91acd RM |
703 | *(Elf64_Half *) reloc_addr = PPC_LO (value); |
704 | break; | |
705 | ||
706 | case R_PPC64_ADDR16_HI: | |
afafddab RM |
707 | case R_PPC64_TPREL16_HI: |
708 | case R_PPC64_DTPREL16_HI: | |
709 | case R_PPC64_GOT_TPREL16_HI: | |
710 | case R_PPC64_GOT_DTPREL16_HI: | |
711 | case R_PPC64_GOT_TLSGD16_HI: | |
712 | case R_PPC64_GOT_TLSLD16_HI: | |
cfc91acd RM |
713 | *(Elf64_Half *) reloc_addr = PPC_HI (value); |
714 | break; | |
715 | ||
716 | case R_PPC64_ADDR16_HA: | |
afafddab RM |
717 | case R_PPC64_TPREL16_HA: |
718 | case R_PPC64_DTPREL16_HA: | |
719 | case R_PPC64_GOT_TLSGD16_HA: | |
720 | case R_PPC64_GOT_TLSLD16_HA: | |
cfc91acd RM |
721 | *(Elf64_Half *) reloc_addr = PPC_HA (value); |
722 | break; | |
723 | ||
724 | case R_PPC64_REL24: | |
725 | { | |
726 | Elf64_Addr delta = value - (Elf64_Xword) reloc_addr; | |
727 | if (dont_expect ((delta + 0x2000000) >= 0x4000000 || (delta & 3) != 0)) | |
728 | _dl_reloc_overflow (map, "R_PPC64_REL24", reloc_addr, sym, refsym); | |
729 | *(Elf64_Word *) reloc_addr = BIT_INSERT (*(Elf64_Word *) reloc_addr, | |
730 | delta, 0x3fffffc); | |
731 | } | |
732 | break; | |
733 | ||
734 | case R_PPC64_COPY: | |
735 | if (dont_expect (sym == NULL)) | |
736 | /* This can happen in trace mode when an object could not be found. */ | |
737 | return; | |
738 | if (dont_expect (sym->st_size > refsym->st_size | |
739 | || (GL(dl_verbose) && sym->st_size < refsym->st_size))) | |
740 | { | |
741 | const char *strtab; | |
742 | ||
743 | strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); | |
744 | _dl_error_printf ("%s: Symbol `%s' has different size" \ | |
745 | " in shared object," \ | |
746 | " consider re-linking\n", | |
747 | _dl_argv[0] ?: "<program name unknown>", | |
748 | strtab + refsym->st_name); | |
749 | } | |
750 | memcpy (reloc_addr, (char *) value, MIN (sym->st_size, refsym->st_size)); | |
751 | return; | |
752 | ||
753 | case R_PPC64_UADDR64: | |
754 | /* We are big-endian. */ | |
755 | ((char *) reloc_addr)[0] = (value >> 56) & 0xff; | |
756 | ((char *) reloc_addr)[1] = (value >> 48) & 0xff; | |
757 | ((char *) reloc_addr)[2] = (value >> 40) & 0xff; | |
758 | ((char *) reloc_addr)[3] = (value >> 32) & 0xff; | |
759 | ((char *) reloc_addr)[4] = (value >> 24) & 0xff; | |
760 | ((char *) reloc_addr)[5] = (value >> 16) & 0xff; | |
761 | ((char *) reloc_addr)[6] = (value >> 8) & 0xff; | |
762 | ((char *) reloc_addr)[7] = (value >> 0) & 0xff; | |
763 | return; | |
764 | ||
765 | case R_PPC64_UADDR32: | |
766 | /* We are big-endian. */ | |
767 | ((char *) reloc_addr)[0] = (value >> 24) & 0xff; | |
768 | ((char *) reloc_addr)[1] = (value >> 16) & 0xff; | |
769 | ((char *) reloc_addr)[2] = (value >> 8) & 0xff; | |
770 | ((char *) reloc_addr)[3] = (value >> 0) & 0xff; | |
771 | return; | |
772 | ||
773 | case R_PPC64_ADDR24: | |
774 | if (dont_expect ((value + 0x2000000) >= 0x4000000 || (value & 3) != 0)) | |
775 | _dl_reloc_overflow (map, "R_PPC64_ADDR24", reloc_addr, sym, refsym); | |
776 | *(Elf64_Word *) reloc_addr = BIT_INSERT (*(Elf64_Word *) reloc_addr, | |
777 | value, 0x3fffffc); | |
778 | break; | |
779 | ||
780 | case R_PPC64_ADDR16: | |
781 | if (dont_expect ((value + 0x8000) >= 0x10000)) | |
782 | _dl_reloc_overflow (map, "R_PPC64_ADDR16", reloc_addr, sym, refsym); | |
783 | *(Elf64_Half *) reloc_addr = value; | |
784 | break; | |
785 | ||
786 | case R_PPC64_UADDR16: | |
787 | if (dont_expect ((value + 0x8000) >= 0x10000)) | |
788 | _dl_reloc_overflow (map, "R_PPC64_UADDR16", reloc_addr, sym, refsym); | |
789 | /* We are big-endian. */ | |
790 | ((char *) reloc_addr)[0] = (value >> 8) & 0xff; | |
791 | ((char *) reloc_addr)[1] = (value >> 0) & 0xff; | |
792 | break; | |
793 | ||
794 | case R_PPC64_ADDR16_DS: | |
795 | if (dont_expect ((value + 0x8000) >= 0x10000 || (value & 3) != 0)) | |
796 | _dl_reloc_overflow (map, "R_PPC64_ADDR16_DS", reloc_addr, sym, refsym); | |
797 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
798 | value, 0xfffc); | |
799 | break; | |
800 | ||
afafddab RM |
801 | case R_PPC64_TPREL16_DS: |
802 | if (dont_expect ((value + 0x8000) >= 0x10000 || (value & 3) != 0)) | |
803 | _dl_reloc_overflow (map, "R_PPC64_TPREL16_DS", reloc_addr, | |
804 | sym, refsym); | |
805 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
806 | value, 0xfffc); | |
807 | break; | |
808 | ||
809 | case R_PPC64_DTPREL16_DS: | |
810 | if (dont_expect ((value + 0x8000) >= 0x10000 || (value & 3) != 0)) | |
811 | _dl_reloc_overflow (map, "R_PPC64_DTPREL16_DS", reloc_addr, | |
812 | sym, refsym); | |
813 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
814 | value, 0xfffc); | |
815 | break; | |
816 | ||
817 | case R_PPC64_GOT_TPREL16_DS: | |
818 | if (dont_expect ((value + 0x8000) >= 0x10000 || (value & 3) != 0)) | |
819 | _dl_reloc_overflow (map, "R_PPC64_GOT_TPREL16_DS", reloc_addr, | |
820 | sym, refsym); | |
821 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
822 | value, 0xfffc); | |
823 | break; | |
824 | ||
825 | case R_PPC64_GOT_DTPREL16_DS: | |
826 | if (dont_expect ((value + 0x8000) >= 0x10000 || (value & 3) != 0)) | |
827 | _dl_reloc_overflow (map, "R_PPC64_GOT_DTPREL16_DS", | |
828 | reloc_addr, sym, refsym); | |
829 | *(Elf64_Half *) reloc_addr = BIT_INSERT (*(Elf64_Half *) reloc_addr, | |
830 | value, 0xfffc); | |
831 | break; | |
832 | ||
cfc91acd | 833 | case R_PPC64_ADDR16_HIGHER: |
afafddab RM |
834 | case R_PPC64_TPREL16_HIGHER: |
835 | case R_PPC64_DTPREL16_HIGHER: | |
cfc91acd RM |
836 | *(Elf64_Half *) reloc_addr = PPC_HIGHER (value); |
837 | break; | |
838 | ||
839 | case R_PPC64_ADDR16_HIGHEST: | |
afafddab RM |
840 | case R_PPC64_TPREL16_HIGHEST: |
841 | case R_PPC64_DTPREL16_HIGHEST: | |
cfc91acd RM |
842 | *(Elf64_Half *) reloc_addr = PPC_HIGHEST (value); |
843 | break; | |
844 | ||
845 | case R_PPC64_ADDR16_HIGHERA: | |
afafddab RM |
846 | case R_PPC64_TPREL16_HIGHERA: |
847 | case R_PPC64_DTPREL16_HIGHERA: | |
cfc91acd RM |
848 | *(Elf64_Half *) reloc_addr = PPC_HIGHERA (value); |
849 | break; | |
850 | ||
851 | case R_PPC64_ADDR16_HIGHESTA: | |
afafddab RM |
852 | case R_PPC64_TPREL16_HIGHESTA: |
853 | case R_PPC64_DTPREL16_HIGHESTA: | |
cfc91acd RM |
854 | *(Elf64_Half *) reloc_addr = PPC_HIGHESTA (value); |
855 | break; | |
856 | ||
857 | case R_PPC64_ADDR14: | |
858 | case R_PPC64_ADDR14_BRTAKEN: | |
859 | case R_PPC64_ADDR14_BRNTAKEN: | |
860 | { | |
861 | Elf64_Word insn; | |
862 | if (dont_expect ((value + 0x8000) >= 0x10000 || (value & 3) != 0)) | |
863 | _dl_reloc_overflow (map, "R_PPC64_ADDR14", reloc_addr, sym, refsym); | |
864 | insn = BIT_INSERT (*(Elf64_Word *) reloc_addr, value, 0xfffc); | |
865 | if (r_type != R_PPC64_ADDR14) | |
866 | { | |
867 | insn &= ~(1 << 21); | |
868 | if (r_type == R_PPC64_ADDR14_BRTAKEN) | |
869 | insn |= 1 << 21; | |
870 | if ((insn & (0x14 << 21)) == (0x04 << 21)) | |
871 | insn |= 0x02 << 21; | |
872 | else if ((insn & (0x14 << 21)) == (0x10 << 21)) | |
873 | insn |= 0x08 << 21; | |
874 | } | |
875 | *(Elf64_Word *) reloc_addr = insn; | |
876 | } | |
877 | break; | |
878 | ||
879 | case R_PPC64_REL32: | |
880 | *(Elf64_Word *) reloc_addr = value - (Elf64_Xword) reloc_addr; | |
881 | return; | |
882 | #endif /* !RTLD_BOOTSTRAP */ | |
883 | ||
884 | default: | |
885 | _dl_reloc_bad_type (map, r_type, 0); | |
886 | return; | |
887 | } | |
888 | MODIFIED_CODE_NOQUEUE (reloc_addr); | |
889 | } | |
890 | ||
7ba7c829 UD |
891 | static inline void |
892 | elf_machine_lazy_rel (struct link_map *map, | |
893 | Elf64_Addr l_addr, const Elf64_Rela *reloc) | |
894 | { | |
895 | /* elf_machine_runtime_setup handles this. */ | |
896 | } | |
897 | ||
cfc91acd | 898 | #endif /* RESOLVE */ |