1 /* Machine-dependent ELF dynamic relocation inline functions. ARM version.
2 Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 #define ELF_MACHINE_NAME "ARM"
25 #include <sys/param.h>
27 #define VALID_ELF_ABIVERSION(ver) (ver == 0)
28 #define VALID_ELF_OSABI(osabi) \
29 (osabi == ELFOSABI_SYSV || osabi == ELFOSABI_ARM)
30 #define VALID_ELF_HEADER(hdr,exp,size) \
31 memcmp (hdr,exp,size-2) == 0 \
32 && VALID_ELF_OSABI (hdr[EI_OSABI]) \
33 && VALID_ELF_ABIVERSION (hdr[EI_ABIVERSION])
35 #define CLEAR_CACHE(BEG,END) \
37 register unsigned long _beg __asm ("a1") = (unsigned long)(BEG); \
38 register unsigned long _end __asm ("a2") = (unsigned long)(END); \
39 register unsigned long _flg __asm ("a3") = 0; \
40 __asm __volatile ("swi 0x9f0002 @ sys_cacheflush" \
46 /* Return nonzero iff ELF header is compatible with the running host. */
47 static inline int __attribute__ ((unused
))
48 elf_machine_matches_host (const Elf32_Ehdr
*ehdr
)
50 return ehdr
->e_machine
== EM_ARM
;
54 /* Return the link-time address of _DYNAMIC. Conveniently, this is the
55 first element of the GOT. This must be inlined in a function which
57 static inline Elf32_Addr
__attribute__ ((unused
))
58 elf_machine_dynamic (void)
60 register Elf32_Addr
*got
asm ("r10");
65 /* Return the run-time load address of the shared object. */
66 static inline Elf32_Addr
__attribute__ ((unused
))
67 elf_machine_load_address (void)
69 extern void __dl_start
asm ("_dl_start");
70 Elf32_Addr got_addr
= (Elf32_Addr
) &__dl_start
;
71 Elf32_Addr pcrel_addr
;
72 asm ("adr %0, _dl_start" : "=r" (pcrel_addr
));
73 return pcrel_addr
- got_addr
;
77 /* Set up the loaded object described by L so its unrelocated PLT
78 entries will jump to the on-demand fixup code in dl-runtime.c. */
80 static inline int __attribute__ ((unused
))
81 elf_machine_runtime_setup (struct link_map
*l
, int lazy
, int profile
)
84 extern void _dl_runtime_resolve (Elf32_Word
);
85 extern void _dl_runtime_profile (Elf32_Word
);
87 if (l
->l_info
[DT_JMPREL
] && lazy
)
89 /* patb: this is different than i386 */
90 /* The GOT entries for functions in the PLT have not yet been filled
91 in. Their initial contents will arrange when called to push an
92 index into the .got section, load ip with &_GLOBAL_OFFSET_TABLE_[3],
93 and then jump to _GLOBAL_OFFSET_TABLE[2]. */
94 got
= (Elf32_Addr
*) D_PTR (l
, l_info
[DT_PLTGOT
]);
95 /* If a library is prelinked but we have to relocate anyway,
96 we have to be able to undo the prelinking of .got.plt.
97 The prelinker saved us here address of .plt. */
99 l
->l_mach
.plt
= got
[1] + l
->l_addr
;
100 got
[1] = (Elf32_Addr
) l
; /* Identify this shared object. */
102 /* The got[2] entry contains the address of a function which gets
103 called to get the address of a so far unresolved function and
104 jump to it. The profiling extension of the dynamic linker allows
105 to intercept the calls to collect information. In this case we
106 don't store the address in the GOT so that all future calls also
107 end in this function. */
110 got
[2] = (Elf32_Addr
) &_dl_runtime_profile
;
112 if (_dl_name_match_p (GL(dl_profile
), l
))
113 /* Say that we really want profiling and the timers are
115 GL(dl_profile_map
) = l
;
118 /* This function will get called to fix up the GOT entry indicated by
119 the offset on the stack, and then jump to the resolved address. */
120 got
[2] = (Elf32_Addr
) &_dl_runtime_resolve
;
125 /* This code is used in dl-runtime.c to call the `fixup' function
126 and then redirect to the address it returns. */
127 // macro for handling PIC situation....
129 #define CALL_ROUTINE(x) "\
136 0: .word _GLOBAL_OFFSET_TABLE_ - 1b - 4\n\
137 2: .word " #x "(GOTOFF)\n\
140 #define CALL_ROUTINE(x) " bl " #x
144 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
146 .globl _dl_runtime_resolve\n\
147 .type _dl_runtime_resolve, #function\n\
149 _dl_runtime_resolve:\n\
150 @ we get called with\n\
151 @ stack[0] contains the return address from this call\n\
152 @ ip contains &GOT[n+3] (pointer to function)\n\
153 @ lr points to &GOT[2]\n\
155 @ save almost everything; lr is already on the stack\n\
156 stmdb sp!,{r0-r3,sl,fp}\n\
158 @ prepare to call fixup()\n\
159 @ change &GOT[n+3] into 8*n NOTE: reloc are 8 bytes each\n\
164 @ get pointer to linker struct\n\
167 @ call fixup routine\n\
168 " CALL_ROUTINE(fixup) "\n\
173 @ restore the stack\n\
174 ldmia sp!,{r0-r3,sl,fp,lr}\n\
176 @ jump to the newly found address\n\
179 .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\
181 .globl _dl_runtime_profile\n\
182 .type _dl_runtime_profile, #function\n\
184 _dl_runtime_profile:\n\
185 @ save almost everything; lr is already on the stack\n\
186 stmdb sp!,{r0-r3,sl,fp}\n\
188 @ prepare to call fixup()\n\
189 @ change &GOT[n+3] into 8*n NOTE: reloc are 8 bytes each\n\
194 @ get pointer to linker struct\n\
197 @ call profiling fixup routine\n\
198 " CALL_ROUTINE(profile_fixup) "\n\
203 @ restore the stack\n\
204 ldmia sp!,{r0-r3,sl,fp,lr}\n\
206 @ jump to the newly found address\n\
209 .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\
213 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
215 .globl _dl_runtime_resolve\n\
216 .globl _dl_runtime_profile\n\
217 .type _dl_runtime_resolve, #function\n\
218 .type _dl_runtime_profile, #function\n\
220 _dl_runtime_resolve:\n\
221 _dl_runtime_profile:\n\
222 @ we get called with\n\
223 @ stack[0] contains the return address from this call\n\
224 @ ip contains &GOT[n+3] (pointer to function)\n\
225 @ lr points to &GOT[2]\n\
227 @ save almost everything; return add is already on the stack\n\
228 stmdb sp!,{r0-r3,sl,fp}\n\
230 @ prepare to call fixup()\n\
231 @ change &GOT[n+3] into 8*n NOTE: reloc are 8 bytes each\n\
236 @ get pointer to linker struct\n\
239 @ call profiling fixup routine\n\
240 " CALL_ROUTINE(fixup) "\n\
245 @ restore the stack\n\
246 ldmia sp!,{r0-r3,sl,fp,lr}\n\
248 @ jump to the newly found address\n\
251 .size _dl_runtime_profile, .-_dl_runtime_profile\n\
256 /* Mask identifying addresses reserved for the user program,
257 where the dynamic linker should not map anything. */
258 #define ELF_MACHINE_USER_ADDRESS_MASK 0xf8000000UL
260 /* Initial entry point code for the dynamic linker.
261 The C function `_dl_start' is the real entry point;
262 its return value is the user program's entry point. */
264 #define RTLD_START asm ("\
267 .globl _dl_start_user\n\
269 @ at start time, all the args are on the stack\n\
272 @ returns user entry point in r0\n\
275 @ we are PIC code, so get global offset table\n\
276 ldr sl, .L_GET_GOT\n\
279 @ Store the highest stack address\n\
280 ldr r1, .L_STACK_END\n\
283 @ See if we were run as a command with the executable file\n\
284 @ name as an extra leading argument.\n\
285 ldr r4, .L_SKIP_ARGS\n\
287 @ get the original arg count\n\
289 @ subtract _dl_skip_args from it\n\
291 @ adjust the stack pointer to skip them\n\
292 add sp, sp, r4, lsl #2\n\
293 @ get the argv address\n\
295 @ store the new argc in the new stack location\n\
298 add r3, r2, r1, lsl #2\n\
301 @ now we call _dl_init\n\
306 bl _dl_init_internal(PLT)\n\
307 @ clear the startup flag\n\
308 ldr r2, .L_STARTUP_FLAG\n\
312 @ load the finalizer function\n\
313 ldr r0, .L_FINI_PROC\n\
315 @ jump to the user_s entry point\n\
318 .word _GLOBAL_OFFSET_TABLE_ - .L_GOT_GOT - 4\n\
320 .word _dl_skip_args(GOTOFF)\n\
322 .word _dl_starting_up(GOT)\n\
324 .word _dl_fini(GOT)\n\
326 .word __libc_stack_end(GOT)\n\
328 .word _rtld_local(GOT)\n\
332 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
333 PLT entries should not be allowed to define the value.
334 ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
335 of the main executable's symbols, as for a COPY reloc. */
336 #define elf_machine_type_class(type) \
337 ((((type) == R_ARM_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT) \
338 | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY))
340 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
341 #define ELF_MACHINE_JMP_SLOT R_ARM_JUMP_SLOT
343 /* ARM never uses Elf32_Rela relocations for the dynamic linker.
344 Prelinked libraries may use Elf32_Rela though. */
345 #define ELF_MACHINE_PLT_REL 1
347 /* We define an initialization functions. This is called very early in
349 #define DL_PLATFORM_INIT dl_platform_init ()
351 static inline void __attribute__ ((unused
))
352 dl_platform_init (void)
354 if (GL(dl_platform
) != NULL
&& *GL(dl_platform
) == '\0')
355 /* Avoid an empty string which would disturb us. */
356 GL(dl_platform
) = NULL
;
359 static inline Elf32_Addr
360 elf_machine_fixup_plt (struct link_map
*map
, lookup_t t
,
361 const Elf32_Rel
*reloc
,
362 Elf32_Addr
*reloc_addr
, Elf32_Addr value
)
364 return *reloc_addr
= value
;
367 /* Return the final value of a plt relocation. */
368 static inline Elf32_Addr
369 elf_machine_plt_value (struct link_map
*map
, const Elf32_Rel
*reloc
,
375 #endif /* !dl_machine_h */
379 /* ARM never uses Elf32_Rela relocations for the dynamic linker.
380 Prelinked libraries may use Elf32_Rela though. */
381 # ifdef RTLD_BOOTSTRAP
382 # define ELF_MACHINE_NO_RELA 1
385 /* Deal with an out-of-range PC24 reloc. */
387 fix_bad_pc24 (Elf32_Addr
*const reloc_addr
, Elf32_Addr value
)
389 static void *fix_page
;
390 static unsigned int fix_offset
;
391 static size_t pagesize
;
392 Elf32_Word
*fix_address
;
397 pagesize
= getpagesize ();
398 fix_page
= mmap (NULL
, pagesize
, PROT_READ
| PROT_WRITE
| PROT_EXEC
,
399 MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
401 assert (! "could not map page for fixup");
405 fix_address
= (Elf32_Word
*)(fix_page
+ fix_offset
);
406 fix_address
[0] = 0xe51ff004; /* ldr pc, [pc, #-4] */
407 fix_address
[1] = value
;
410 if (fix_offset
>= pagesize
)
413 return (Elf32_Addr
)fix_address
;
416 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
417 MAP is the object containing the reloc. */
420 elf_machine_rel (struct link_map
*map
, const Elf32_Rel
*reloc
,
421 const Elf32_Sym
*sym
, const struct r_found_version
*version
,
422 Elf32_Addr
*const reloc_addr
)
424 const unsigned int r_type
= ELF32_R_TYPE (reloc
->r_info
);
426 #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
427 if (__builtin_expect (r_type
== R_ARM_RELATIVE
, 0))
429 # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
430 /* This is defined in rtld.c, but nowhere in the static libc.a;
431 make the reference weak so static programs can still link.
432 This declaration cannot be done when compiling rtld.c
433 (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
434 common defn for _dl_rtld_map, which is incompatible with a
435 weak decl in the same file. */
437 weak_extern (_dl_rtld_map
);
439 if (map
!= &GL(dl_rtld_map
)) /* Already done in rtld itself. */
441 *reloc_addr
+= map
->l_addr
;
443 # ifndef RTLD_BOOTSTRAP
444 else if (__builtin_expect (r_type
== R_ARM_NONE
, 0))
450 const Elf32_Sym
*const refsym
= sym
;
451 Elf32_Addr value
= RESOLVE (&sym
, version
, r_type
);
453 value
+= sym
->st_value
;
459 /* This can happen in trace mode if an object could not be
462 if (sym
->st_size
> refsym
->st_size
463 || (GL(dl_verbose
) && sym
->st_size
< refsym
->st_size
))
467 strtab
= (const void *) D_PTR (map
, l_info
[DT_STRTAB
]);
469 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
470 rtld_progname
?: "<program name unknown>",
471 strtab
+ refsym
->st_name
);
473 memcpy (reloc_addr
, (void *) value
, MIN (sym
->st_size
,
477 case R_ARM_JUMP_SLOT
:
478 # ifdef RTLD_BOOTSTRAP
479 /* Fix weak undefined references. */
480 if (sym
!= NULL
&& sym
->st_value
== 0)
488 # ifndef RTLD_BOOTSTRAP
489 /* This is defined in rtld.c, but nowhere in the static
490 libc.a; make the reference weak so static programs can
491 still link. This declaration cannot be done when
492 compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP) because
493 rtld.c contains the common defn for _dl_rtld_map, which
494 is incompatible with a weak decl in the same file. */
496 weak_extern (_dl_rtld_map
);
498 if (map
== &GL(dl_rtld_map
))
499 /* Undo the relocation done here during bootstrapping.
500 Now we will relocate it anew, possibly using a
501 binding found in the user program or a loaded library
502 rather than the dynamic linker's built-in definitions
503 used while loading those libraries. */
504 value
-= map
->l_addr
+ refsym
->st_value
;
506 *reloc_addr
+= value
;
512 Elf32_Addr newvalue
, topbits
;
514 addend
= *reloc_addr
& 0x00ffffff;
515 if (addend
& 0x00800000) addend
|= 0xff000000;
517 newvalue
= value
- (Elf32_Addr
)reloc_addr
+ (addend
<< 2);
518 topbits
= newvalue
& 0xfe000000;
519 if (topbits
!= 0xfe000000 && topbits
!= 0x00000000)
521 newvalue
= fix_bad_pc24(reloc_addr
, value
)
522 - (Elf32_Addr
)reloc_addr
+ (addend
<< 2);
523 topbits
= newvalue
& 0xfe000000;
524 if (topbits
!= 0xfe000000 && topbits
!= 0x00000000)
526 _dl_signal_error (0, map
->l_name
, NULL
,
527 "R_ARM_PC24 relocation out of range");
531 value
= (*reloc_addr
& 0xff000000) | (newvalue
& 0x00ffffff);
536 _dl_reloc_bad_type (map
, r_type
, 0);
542 # ifndef RTLD_BOOTSTRAP
544 elf_machine_rela (struct link_map
*map
, const Elf32_Rela
*reloc
,
545 const Elf32_Sym
*sym
, const struct r_found_version
*version
,
546 Elf32_Addr
*const reloc_addr
)
548 const unsigned int r_type
= ELF32_R_TYPE (reloc
->r_info
);
550 if (__builtin_expect (r_type
== R_ARM_RELATIVE
, 0))
551 *reloc_addr
= map
->l_addr
+ reloc
->r_addend
;
552 else if (__builtin_expect (r_type
== R_ARM_NONE
, 0))
556 Elf32_Addr value
= RESOLVE (&sym
, version
, r_type
);
558 value
+= sym
->st_value
;
563 case R_ARM_JUMP_SLOT
:
565 *reloc_addr
= value
+ reloc
->r_addend
;
569 Elf32_Addr newvalue
, topbits
;
571 newvalue
= value
+ reloc
->r_addend
- (Elf32_Addr
)reloc_addr
;
572 topbits
= newvalue
& 0xfe000000;
573 if (topbits
!= 0xfe000000 && topbits
!= 0x00000000)
575 newvalue
= fix_bad_pc24(reloc_addr
, value
)
576 - (Elf32_Addr
)reloc_addr
+ (reloc
->r_addend
<< 2);
577 topbits
= newvalue
& 0xfe000000;
578 if (topbits
!= 0xfe000000 && topbits
!= 0x00000000)
580 _dl_signal_error (0, map
->l_name
, NULL
,
581 "R_ARM_PC24 relocation out of range");
585 value
= (*reloc_addr
& 0xff000000) | (newvalue
& 0x00ffffff);
590 _dl_reloc_bad_type (map
, r_type
, 0);
598 elf_machine_rel_relative (Elf32_Addr l_addr
, const Elf32_Rel
*reloc
,
599 Elf32_Addr
*const reloc_addr
)
601 *reloc_addr
+= l_addr
;
604 # ifndef RTLD_BOOTSTRAP
606 elf_machine_rela_relative (Elf32_Addr l_addr
, const Elf32_Rela
*reloc
,
607 Elf32_Addr
*const reloc_addr
)
609 *reloc_addr
= l_addr
+ reloc
->r_addend
;
614 elf_machine_lazy_rel (struct link_map
*map
,
615 Elf32_Addr l_addr
, const Elf32_Rel
*reloc
)
617 Elf32_Addr
*const reloc_addr
= (void *) (l_addr
+ reloc
->r_offset
);
618 const unsigned int r_type
= ELF32_R_TYPE (reloc
->r_info
);
619 /* Check for unexpected PLT reloc type. */
620 if (__builtin_expect (r_type
== R_ARM_JUMP_SLOT
, 1))
622 if (__builtin_expect (map
->l_mach
.plt
, 0) == 0)
623 *reloc_addr
+= l_addr
;
625 *reloc_addr
= map
->l_mach
.plt
;
628 _dl_reloc_bad_type (map
, r_type
, 1);