]>
Commit | Line | Data |
---|---|---|
31161268 UD |
1 | /* Machine-dependent ELF dynamic relocation inline functions. ARM version. |
2 | Copyright (C) 1995, 1996, 1997, 1998 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 | ||
20 | #ifndef dl_machine_h | |
21 | #define dl_machine_h | |
22 | ||
23 | #define ELF_MACHINE_NAME "ARM" | |
24 | ||
25 | #include <sys/param.h> | |
26 | ||
27 | #include <assert.h> | |
28 | ||
29 | /* Return nonzero iff E_MACHINE is compatible with the running host. */ | |
30 | static inline int __attribute__ ((unused)) | |
31 | elf_machine_matches_host (Elf32_Half e_machine) | |
32 | { | |
33 | switch (e_machine) | |
34 | { | |
35 | case EM_ARM: | |
36 | return 1; | |
37 | default: | |
38 | return 0; | |
39 | } | |
40 | } | |
41 | ||
42 | ||
43 | /* Return the link-time address of _DYNAMIC. Conveniently, this is the | |
44 | first element of the GOT. This must be inlined in a function which | |
45 | uses global data. */ | |
46 | static inline Elf32_Addr __attribute__ ((unused)) | |
47 | elf_machine_dynamic (void) | |
48 | { | |
49 | register Elf32_Addr *got asm ("r10"); | |
50 | return *got; | |
51 | } | |
52 | ||
53 | ||
54 | /* Return the run-time load address of the shared object. */ | |
55 | // patb | |
56 | static inline Elf32_Addr __attribute__ ((unused)) | |
57 | elf_machine_load_address (void) | |
58 | { | |
59 | Elf32_Addr addr; | |
60 | asm (" ldr ip,.L1 | |
61 | ldr r3,.L3 | |
62 | add r3, r3, sl | |
63 | ldr ip,[sl, ip] | |
64 | sub ip, r3, ip | |
65 | b .L2 | |
66 | .L1: .word _dl_start(GOT) | |
67 | .L3: .word _dl_start(GOTOFF) | |
68 | .L2: mov %0, ip" | |
69 | : "=r" (addr) : : "ip", "r3"); | |
70 | return addr; | |
71 | } | |
72 | ||
73 | ||
74 | /* Set up the loaded object described by L so its unrelocated PLT | |
75 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
76 | ||
77 | static inline int __attribute__ ((unused)) | |
78 | elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) | |
79 | { | |
80 | Elf32_Addr *got; | |
81 | extern void _dl_runtime_resolve (Elf32_Word); | |
82 | extern void _dl_runtime_profile (Elf32_Word); | |
83 | ||
84 | if (l->l_info[DT_JMPREL] && lazy) | |
85 | { | |
86 | /* patb: this is different than i386 */ | |
87 | /* The GOT entries for functions in the PLT have not yet been filled | |
88 | in. Their initial contents will arrange when called to push an | |
89 | index into the .got section, load ip with &_GLOBAL_OFFSET_TABLE_[3], | |
90 | and then jump to _GLOBAL_OFFSET_TABLE[2]. */ | |
91 | got = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); | |
92 | got[1] = (Elf32_Addr) l; /* Identify this shared object. */ | |
93 | ||
94 | /* The got[2] entry contains the address of a function which gets | |
95 | called to get the address of a so far unresolved function and | |
96 | jump to it. The profiling extension of the dynamic linker allows | |
97 | to intercept the calls to collect information. In this case we | |
98 | don't store the address in the GOT so that all future calls also | |
99 | end in this function. */ | |
100 | if (profile) | |
101 | { | |
102 | got[2] = (Elf32_Addr) &_dl_runtime_profile; | |
103 | /* Say that we really want profiling and the timers are started. */ | |
104 | _dl_profile_map = l; | |
105 | } | |
106 | else | |
107 | /* This function will get called to fix up the GOT entry indicated by | |
108 | the offset on the stack, and then jump to the resolved address. */ | |
109 | got[2] = (Elf32_Addr) &_dl_runtime_resolve; | |
110 | } | |
111 | return lazy; | |
112 | } | |
113 | ||
114 | /* This code is used in dl-runtime.c to call the `fixup' function | |
115 | and then redirect to the address it returns. */ | |
116 | // macro for handling PIC situation.... | |
117 | #ifdef PIC | |
118 | #define CALL_ROUTINE(x) " ldr sl,0f | |
119 | add sl, pc, sl | |
120 | 1: ldr r2, 2f | |
121 | mov lr, pc | |
122 | add pc, sl, r2 | |
123 | b 3f | |
124 | 0: .word _GLOBAL_OFFSET_TABLE_ - 1b - 4 | |
125 | 2: .word " #x "(GOTOFF) | |
126 | 3: " | |
127 | #else | |
128 | #define CALL_ROUTINE(x) " bl " #x | |
129 | #endif | |
130 | ||
131 | #ifndef PROF | |
132 | # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ | |
133 | .text | |
134 | .globl _dl_runtime_resolve | |
135 | .type _dl_runtime_resolve, #function | |
136 | .align 2 | |
137 | _dl_runtime_resolve: | |
138 | @ we get called with | |
139 | @ stack[0] contains the return address from this call | |
140 | @ ip contains &GOT[n+3] (pointer to function) | |
141 | @ lr points to &GOT[2] | |
142 | ||
143 | @ save almost everything; lr is already on the stack | |
144 | stmdb sp!,{r0-r3,sl,fp} | |
145 | ||
146 | @ prepare to call fixup() | |
147 | ||
148 | @ change &GOT[n+3] into 8*n NOTE: reloc are 8 bytes each | |
149 | sub r1, ip, lr | |
150 | sub r1, r1, #4 | |
151 | add r1, r1, r1 | |
152 | ||
153 | @ get pointer to linker struct | |
154 | ldr r0, [lr, #-4] | |
155 | ||
156 | " CALL_ROUTINE(fixup) " | |
157 | ||
158 | @ save the return | |
159 | mov ip, r0 | |
160 | ||
161 | @ restore the stack | |
162 | ldmia sp!,{r0-r3,sl,fp,lr} | |
163 | ||
164 | @ jump to the newly found address | |
165 | mov pc, ip | |
166 | ||
167 | .size _dl_runtime_resolve, .-_dl_runtime_resolve | |
168 | ||
169 | .globl _dl_runtime_profile | |
170 | .type _dl_runtime_profile, #function | |
171 | .align 2 | |
172 | _dl_runtime_profile: | |
173 | @ we get caled with | |
174 | @ stack[0] contains the return address from this call | |
175 | @ ip contains &GOT[n+3] (pointer to function) | |
176 | @ lr points to &GOT[2] | |
177 | ||
178 | @ save almost everything; return add is already on the stack | |
179 | stmdb sp!,{r0-r3,fp} | |
180 | ||
181 | @ prepare to call fixup() | |
182 | ||
183 | @ change &GOT[n+3] into 8*n NOTE: reloc are 8 bytes each | |
184 | sub r1, ip, lr | |
185 | sub r1, r1, #4 | |
186 | add r1, r1, r1 | |
187 | ||
188 | @ get pointer to linker struct | |
189 | ldr r0, [lr, #-4] | |
190 | ||
191 | " CALL_ROUTINE(profile_fixup) " | |
192 | ||
193 | @ save the return | |
194 | mov ip, r0 | |
195 | ||
196 | @ restore the stack | |
197 | ldmia sp!,{r0-r3,fp,lr} | |
198 | ||
199 | @ jump to the newly found address | |
200 | mov pc, ip | |
201 | ||
202 | .size _dl_runtime_profile, .-_dl_runtime_profile | |
203 | .previous | |
204 | "); | |
205 | #else // PROF | |
206 | # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ | |
207 | .text | |
208 | .globl _dl_runtime_resolve | |
209 | .globl _dl_runtime_profile | |
210 | .type _dl_runtime_resolve, #function | |
211 | .type _dl_runtime_profile, #function | |
212 | .align 2 | |
213 | _dl_runtime_resolve: | |
214 | _dl_runtime_profile: | |
215 | stmdb sp!,{r0-r3,fp} | |
216 | ldr r1,[sp,#0x34] | |
217 | sub r1, ip, lr | |
218 | sub r1, r1, #4 | |
219 | add r1, r1, r1 | |
220 | ldr r0, [lr, #-4] | |
221 | " CALL_ROUTINE(fixup) " | |
222 | mov ip, r0 | |
223 | ldmia sp!,{r0-r3,fp,lr} | |
224 | mov pc, ip | |
225 | ||
226 | .size _dl_runtime_profile, .-_dl_runtime_profile | |
227 | .previous | |
228 | "); | |
229 | #endif //PROF | |
230 | ||
231 | /* Mask identifying addresses reserved for the user program, | |
232 | where the dynamic linker should not map anything. */ | |
233 | #define ELF_MACHINE_USER_ADDRESS_MASK 0xf8000000UL | |
234 | ||
235 | /* Initial entry point code for the dynamic linker. | |
236 | The C function `_dl_start' is the real entry point; | |
237 | its return value is the user program's entry point. */ | |
238 | ||
239 | #define RTLD_START asm ("\ | |
240 | .text | |
241 | .globl _start | |
242 | .globl _dl_start_user | |
243 | _start: | |
244 | @ at start time, all the args are on the stack | |
245 | mov r0, sp | |
246 | bl _dl_start | |
247 | @ returns user entry point in r0 | |
248 | _dl_start_user: | |
249 | mov r6, r0 | |
250 | @ we are PIC code, so get global offset table | |
251 | ldr sl, .L_GET_GOT | |
252 | add sl, pc, sl | |
253 | .L_GOT_GOT: | |
254 | @ See if we were run as a command with the executable file | |
255 | @ name as an extra leading argument. | |
256 | ldr r1, .L_SKIP_ARGS | |
257 | ldr r1, [sl, r1] | |
258 | @ get the original arg count | |
259 | ldr r0, [sp] | |
260 | @ subtract _dl_skip_args from it | |
261 | sub r0, r0, r1 | |
262 | @ adjust the stack pointer to skip them | |
263 | add sp, sp, r1, lsl #2 | |
264 | @ store the new argc in the new stack location | |
265 | str r0, [sp] | |
266 | ||
267 | @ now we enter a _dl_init_next loop | |
268 | ldr r2, .L_DEF_SCOPE | |
269 | ldr r2, [sl, r2] | |
270 | ldr r4, [r2, #8] | |
271 | @ call _dl_init_next to get the address of an initalizer | |
272 | 0: mov r0, r4 | |
273 | bl _dl_init_next(PLT) | |
274 | cmp r0, #0 | |
275 | beq 1f | |
276 | @ call the shared-object initializer | |
277 | @ during this call, the stack may get moved around | |
278 | mov lr, pc | |
279 | mov pc, r0 | |
280 | @ go back and look for another initializer | |
281 | b 0b | |
282 | 1: @ clear the startup flag | |
283 | ldr r2, .L_STARTUP_FLAG | |
284 | ldr r1, [sl, r2] | |
285 | @ we know r0==0 at this point | |
286 | str r0, [r1] | |
287 | @ load the finalizer function | |
288 | ldr r0, .L_FINI_PROC | |
289 | ldr r0, [sl, r0] | |
290 | @ jump to the user_s entry point | |
291 | mov pc, r6 | |
292 | .L_GET_GOT: | |
293 | .word _GLOBAL_OFFSET_TABLE_ - .L_GOT_GOT - 4 \n\ | |
294 | .L_SKIP_ARGS: \n\ | |
295 | .word _dl_skip_args(GOTOFF) \n\ | |
296 | .L_DEF_SCOPE: \n\ | |
297 | .word _dl_default_scope(GOT) \n\ | |
298 | .L_STARTUP_FLAG: | |
299 | .word _dl_starting_up(GOT) | |
300 | .L_FINI_PROC: | |
301 | .word _dl_fini(GOT) | |
302 | .previous\n\ | |
303 | "); | |
304 | ||
305 | /* Nonzero iff TYPE should not be allowed to resolve to one of | |
306 | the main executable's symbols, as for a COPY reloc. */ | |
307 | #define elf_machine_lookup_noexec_p(type) ((type) == R_ARM_COPY) | |
308 | ||
309 | /* Nonzero iff TYPE describes relocation of a PLT entry, so | |
310 | PLT entries should not be allowed to define the value. */ | |
bf7997b6 | 311 | #define elf_machine_lookup_noplt_p(type) ((type) == R_ARM_JUMP_SLOT) |
31161268 UD |
312 | |
313 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ | |
bf7997b6 | 314 | #define ELF_MACHINE_JMP_SLOT R_ARM_JUMP_SLOT |
31161268 | 315 | |
a9ddb793 | 316 | /* The ARM never uses Elf32_Rela relocations. */ |
31161268 UD |
317 | #define ELF_MACHINE_NO_RELA 1 |
318 | ||
319 | /* We define an initialization functions. This is called very early in | |
320 | _dl_sysdep_start. */ | |
321 | #define DL_PLATFORM_INIT dl_platform_init () | |
322 | ||
323 | extern const char *_dl_platform; | |
324 | ||
325 | static inline void __attribute__ ((unused)) | |
326 | dl_platform_init (void) | |
327 | { | |
328 | if (_dl_platform == NULL) | |
329 | /* We default to ARM | |
330 | This is where processors could be distinguished arm2, arm6, sa110, etc */ | |
331 | _dl_platform = "ARM"; | |
332 | } | |
333 | ||
334 | static inline void | |
335 | elf_machine_fixup_plt (struct link_map *map, const Elf32_Rel *reloc, | |
336 | Elf32_Addr *reloc_addr, Elf32_Addr value) | |
337 | { | |
338 | *reloc_addr = value; | |
339 | } | |
340 | ||
341 | /* Return the final value of a plt relocation. */ | |
342 | static inline Elf32_Addr | |
343 | elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc, | |
344 | Elf32_Addr value) | |
345 | { | |
346 | return value; | |
347 | } | |
348 | ||
349 | #endif /* !dl_machine_h */ | |
350 | ||
351 | #ifdef RESOLVE | |
352 | ||
353 | extern char **_dl_argv; | |
354 | ||
355 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). | |
356 | MAP is the object containing the reloc. */ | |
357 | ||
358 | static inline void | |
359 | elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc, | |
360 | const Elf32_Sym *sym, const struct r_found_version *version, | |
361 | Elf32_Addr *const reloc_addr) | |
362 | { | |
363 | if (ELF32_R_TYPE (reloc->r_info) == R_ARM_RELATIVE) | |
364 | { | |
365 | #ifndef RTLD_BOOTSTRAP | |
366 | if (map != &_dl_rtld_map) /* Already done in rtld itself. */ | |
367 | #endif | |
368 | *reloc_addr += map->l_addr; | |
369 | } | |
370 | else if (ELF32_R_TYPE (reloc->r_info) != R_ARM_NONE) | |
371 | { | |
372 | const Elf32_Sym *const refsym = sym; | |
373 | Elf32_Addr value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info)); | |
374 | if (sym) | |
375 | value += sym->st_value; | |
376 | ||
377 | switch (ELF32_R_TYPE (reloc->r_info)) | |
378 | { | |
379 | case R_ARM_COPY: | |
380 | if (sym == NULL) | |
381 | /* This can happen in trace mode if an object could not be | |
382 | found. */ | |
383 | break; | |
384 | if (sym->st_size > refsym->st_size | |
385 | || (_dl_verbose && sym->st_size < refsym->st_size)) | |
386 | { | |
387 | const char *strtab; | |
388 | ||
389 | strtab = ((const char *) map->l_addr | |
390 | + map->l_info[DT_STRTAB]->d_un.d_ptr); | |
391 | _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>", | |
392 | ": Symbol `", strtab + refsym->st_name, | |
393 | "' has different size in shared object, " | |
394 | "consider re-linking\n", NULL); | |
395 | } | |
396 | memcpy (reloc_addr, (void *) value, MIN (sym->st_size, | |
397 | refsym->st_size)); | |
398 | break; | |
399 | case R_ARM_GLOB_DAT: | |
bf7997b6 | 400 | case R_ARM_JUMP_SLOT: |
31161268 UD |
401 | |
402 | #if 0 | |
403 | #define _HEX(i) for (j=28; j>=0; j-=4) b[7-j/4]="0123456789abcdef"[((int)i>>j)&15]; | |
404 | { | |
405 | char b[10]; | |
406 | int j; | |
407 | _HEX(map->l_addr); | |
408 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #9; swi 0x00900004; " | |
409 | : : "r"(b) : "r0", "r1", "r2" ); | |
410 | _HEX(sym->st_size); | |
411 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #9; swi 0x00900004; " | |
412 | : : "r"(b) : "r0", "r1", "r2" ); | |
413 | _HEX(&sym->st_value); | |
414 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #9; swi 0x00900004; " | |
415 | : : "r"(b) : "r0", "r1", "r2" ); | |
416 | _HEX(sym->st_value); | |
417 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #9; swi 0x00900004; " | |
418 | : : "r"(b) : "r0", "r1", "r2" ); | |
419 | _HEX(sym); | |
420 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #9; swi 0x00900004; " | |
421 | : : "r"(b) : "r0", "r1", "r2" ); | |
422 | _HEX(reloc_addr); | |
423 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #9; swi 0x00900004; " | |
424 | : : "r"(b) : "r0", "r1", "r2" ); | |
425 | b[0]=' '; b[1]='\n'; | |
426 | __asm__ (" mov r0, #2; mov r1, %0; mov r2, #2; swi 0x00900004; " | |
427 | : : "r"(b) : "r0", "r1", "r2" ); | |
428 | } | |
429 | #endif | |
430 | *reloc_addr = value; | |
431 | break; | |
bf7997b6 | 432 | case R_ARM_ABS32: |
31161268 UD |
433 | { |
434 | #ifndef RTLD_BOOTSTRAP | |
435 | /* This is defined in rtld.c, but nowhere in the static | |
436 | libc.a; make the reference weak so static programs can | |
437 | still link. This declaration cannot be done when | |
438 | compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP) because | |
439 | rtld.c contains the common defn for _dl_rtld_map, which | |
440 | is incompatible with a weak decl in the same file. */ | |
441 | weak_extern (_dl_rtld_map); | |
442 | if (map == &_dl_rtld_map) | |
443 | /* Undo the relocation done here during bootstrapping. | |
444 | Now we will relocate it anew, possibly using a | |
445 | binding found in the user program or a loaded library | |
446 | rather than the dynamic linker's built-in definitions | |
447 | used while loading those libraries. */ | |
448 | value -= map->l_addr + refsym->st_value; | |
449 | #endif | |
450 | *reloc_addr += value; | |
451 | break; | |
452 | } | |
bf7997b6 | 453 | case R_ARM_PC24: |
31161268 UD |
454 | *reloc_addr += (value - (Elf32_Addr) reloc_addr); |
455 | break; | |
456 | default: | |
457 | assert (! "unexpected dynamic reloc type"); | |
458 | break; | |
459 | } | |
460 | } | |
461 | } | |
462 | ||
463 | static inline void | |
464 | elf_machine_lazy_rel (struct link_map *map, const Elf32_Rel *reloc) | |
465 | { | |
466 | Elf32_Addr *const reloc_addr = (void *) (map->l_addr + reloc->r_offset); | |
467 | switch (ELF32_R_TYPE (reloc->r_info)) | |
468 | { | |
bf7997b6 | 469 | case R_ARM_JUMP_SLOT: |
31161268 UD |
470 | *reloc_addr += map->l_addr; |
471 | break; | |
472 | default: | |
473 | assert (! "unexpected PLT reloc type"); | |
474 | break; | |
475 | } | |
476 | } | |
477 | ||
478 | #endif /* RESOLVE */ |