* sysdeps/unix/sysv/linux/m68k/sysdep.h: Support inline syscall
[glibc.git] / sysdeps / arm / dl-machine.h
blob074762e1f004237fb3b92fb7b27519dc8f932661
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
18 02111-1307 USA. */
20 #ifndef dl_machine_h
21 #define dl_machine_h
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) \
36 { \
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" \
41 : /* no outputs */ \
42 : /* no inputs */ \
43 : "a1"); \
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
56 uses global data. */
57 static inline Elf32_Addr __attribute__ ((unused))
58 elf_machine_dynamic (void)
60 register Elf32_Addr *got asm ("r10");
61 return *got;
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)
83 Elf32_Addr *got;
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. */
98 if (got[1])
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. */
108 if (profile)
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
114 started. */
115 GL(dl_profile_map) = l;
117 else
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;
122 return lazy;
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....
128 #ifdef PIC
129 #define CALL_ROUTINE(x) "\
130 ldr sl,0f\n\
131 add sl, pc, sl\n\
132 1: ldr r2, 2f\n\
133 mov lr, pc\n\
134 add pc, sl, r2\n\
135 b 3f\n\
136 0: .word _GLOBAL_OFFSET_TABLE_ - 1b - 4\n\
137 2: .word " #x "(GOTOFF)\n\
138 3: "
139 #else
140 #define CALL_ROUTINE(x) " bl " #x
141 #endif
143 #ifndef PROF
144 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
145 .text\n\
146 .globl _dl_runtime_resolve\n\
147 .type _dl_runtime_resolve, #function\n\
148 .align 2\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\
160 sub r1, ip, lr\n\
161 sub r1, r1, #4\n\
162 add r1, r1, r1\n\
164 @ get pointer to linker struct\n\
165 ldr r0, [lr, #-4]\n\
167 @ call fixup routine\n\
168 " CALL_ROUTINE(fixup) "\n\
170 @ save the return\n\
171 mov ip, r0\n\
173 @ restore the stack\n\
174 ldmia sp!,{r0-r3,sl,fp,lr}\n\
176 @ jump to the newly found address\n\
177 mov pc, ip\n\
179 .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\
181 .globl _dl_runtime_profile\n\
182 .type _dl_runtime_profile, #function\n\
183 .align 2\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\
190 sub r1, ip, lr\n\
191 sub r1, r1, #4\n\
192 add r1, r1, r1\n\
194 @ get pointer to linker struct\n\
195 ldr r0, [lr, #-4]\n\
197 @ call profiling fixup routine\n\
198 " CALL_ROUTINE(profile_fixup) "\n\
200 @ save the return\n\
201 mov ip, r0\n\
203 @ restore the stack\n\
204 ldmia sp!,{r0-r3,sl,fp,lr}\n\
206 @ jump to the newly found address\n\
207 mov pc, ip\n\
209 .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\
210 .previous\n\
212 #else // PROF
213 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
214 .text\n\
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\
219 .align 2\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\
232 sub r1, ip, lr\n\
233 sub r1, r1, #4\n\
234 add r1, r1, r1\n\
236 @ get pointer to linker struct\n\
237 ldr r0, [lr, #-4]\n\
239 @ call profiling fixup routine\n\
240 " CALL_ROUTINE(fixup) "\n\
242 @ save the return\n\
243 mov ip, r0\n\
245 @ restore the stack\n\
246 ldmia sp!,{r0-r3,sl,fp,lr}\n\
248 @ jump to the newly found address\n\
249 mov pc, ip\n\
251 .size _dl_runtime_profile, .-_dl_runtime_profile\n\
252 .previous\n\
254 #endif //PROF
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 ("\
265 .text\n\
266 .globl _start\n\
267 .globl _dl_start_user\n\
268 _start:\n\
269 @ at start time, all the args are on the stack\n\
270 mov r0, sp\n\
271 bl _dl_start\n\
272 @ returns user entry point in r0\n\
273 _dl_start_user:\n\
274 mov r6, r0\n\
275 @ we are PIC code, so get global offset table\n\
276 ldr sl, .L_GET_GOT\n\
277 add sl, pc, sl\n\
278 .L_GOT_GOT:\n\
279 @ Store the highest stack address\n\
280 ldr r1, .L_STACK_END\n\
281 ldr r1, [sl, r1]\n\
282 str sp, [r1]\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\
286 ldr r4, [sl, r4]\n\
287 @ get the original arg count\n\
288 ldr r1, [sp]\n\
289 @ subtract _dl_skip_args from it\n\
290 sub r1, r1, r4\n\
291 @ adjust the stack pointer to skip them\n\
292 add sp, sp, r4, lsl #2\n\
293 @ get the argv address\n\
294 add r2, sp, #4\n\
295 @ store the new argc in the new stack location\n\
296 str r1, [sp]\n\
297 @ compute envp\n\
298 add r3, r2, r1, lsl #2\n\
299 add r3, r3, #4\n\
301 @ now we call _dl_init\n\
302 ldr r0, .L_LOADED\n\
303 ldr r0, [sl, r0]\n\
304 ldr r0, [r0]\n\
305 @ call _dl_init\n\
306 bl _dl_init_internal(PLT)\n\
307 @ clear the startup flag\n\
308 ldr r2, .L_STARTUP_FLAG\n\
309 ldr r1, [sl, r2]\n\
310 mov r0, #0\n\
311 str r0, [r1]\n\
312 @ load the finalizer function\n\
313 ldr r0, .L_FINI_PROC\n\
314 ldr r0, [sl, r0]\n\
315 @ jump to the user_s entry point\n\
316 mov pc, r6\n\
317 .L_GET_GOT:\n\
318 .word _GLOBAL_OFFSET_TABLE_ - .L_GOT_GOT - 4\n\
319 .L_SKIP_ARGS:\n\
320 .word _dl_skip_args(GOTOFF)\n\
321 .L_STARTUP_FLAG:\n\
322 .word _dl_starting_up(GOT)\n\
323 .L_FINI_PROC:\n\
324 .word _dl_fini(GOT)\n\
325 .L_STACK_END:\n\
326 .word __libc_stack_end(GOT)\n\
327 .L_LOADED:\n\
328 .word _rtld_local(GOT)\n\
329 .previous\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
348 _dl_sysdep_start. */
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,
370 Elf32_Addr value)
372 return value;
375 #endif /* !dl_machine_h */
377 #ifdef RESOLVE
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
383 # endif
385 /* Deal with an out-of-range PC24 reloc. */
386 static Elf32_Addr
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;
394 if (! fix_page)
396 if (! pagesize)
397 pagesize = getpagesize ();
398 fix_page = mmap (NULL, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC,
399 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
400 if (! fix_page)
401 assert (! "could not map page for fixup");
402 fix_offset = 0;
405 fix_address = (Elf32_Word *)(fix_page + fix_offset);
406 fix_address[0] = 0xe51ff004; /* ldr pc, [pc, #-4] */
407 fix_address[1] = value;
409 fix_offset += 8;
410 if (fix_offset >= pagesize)
411 fix_page = NULL;
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. */
419 static inline void
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. */
436 # ifndef SHARED
437 weak_extern (_dl_rtld_map);
438 # endif
439 if (map != &GL(dl_rtld_map)) /* Already done in rtld itself. */
440 # endif
441 *reloc_addr += map->l_addr;
443 # ifndef RTLD_BOOTSTRAP
444 else if (__builtin_expect (r_type == R_ARM_NONE, 0))
445 return;
446 # endif
447 else
448 #endif
450 const Elf32_Sym *const refsym = sym;
451 Elf32_Addr value = RESOLVE (&sym, version, r_type);
452 if (sym)
453 value += sym->st_value;
455 switch (r_type)
457 case R_ARM_COPY:
458 if (sym == NULL)
459 /* This can happen in trace mode if an object could not be
460 found. */
461 break;
462 if (sym->st_size > refsym->st_size
463 || (GL(dl_verbose) && sym->st_size < refsym->st_size))
465 const char *strtab;
467 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
468 _dl_error_printf ("\
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,
474 refsym->st_size));
475 break;
476 case R_ARM_GLOB_DAT:
477 case R_ARM_JUMP_SLOT:
478 # ifdef RTLD_BOOTSTRAP
479 /* Fix weak undefined references. */
480 if (sym != NULL && sym->st_value == 0)
481 *reloc_addr = 0;
482 else
483 # endif
484 *reloc_addr = value;
485 break;
486 case R_ARM_ABS32:
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. */
495 # ifndef SHARED
496 weak_extern (_dl_rtld_map);
497 # endif
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;
505 # endif
506 *reloc_addr += value;
507 break;
509 case R_ARM_PC24:
511 Elf32_Sword addend;
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 INTUSE (_dl_signal_error)
527 (0, map->l_name, NULL,
528 "R_ARM_PC24 relocation out of range");
531 newvalue >>= 2;
532 value = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
533 *reloc_addr = value;
535 break;
536 default:
537 _dl_reloc_bad_type (map, r_type, 0);
538 break;
543 # ifndef RTLD_BOOTSTRAP
544 static inline void
545 elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
546 const Elf32_Sym *sym, const struct r_found_version *version,
547 Elf32_Addr *const reloc_addr)
549 const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
551 if (__builtin_expect (r_type == R_ARM_RELATIVE, 0))
552 *reloc_addr = map->l_addr + reloc->r_addend;
553 else if (__builtin_expect (r_type == R_ARM_NONE, 0))
554 return;
555 else
557 # ifndef RESOLVE_CONFLICT_FIND_MAP
558 const Elf32_Sym *const refsym = sym;
559 # endif
560 Elf32_Addr value = RESOLVE (&sym, version, r_type);
561 if (sym)
562 value += sym->st_value;
564 switch (r_type)
566 # ifndef RESOLVE_CONFLICT_FIND_MAP
567 /* Not needed for dl-conflict.c. */
568 case R_ARM_COPY:
569 if (sym == NULL)
570 /* This can happen in trace mode if an object could not be
571 found. */
572 break;
573 if (sym->st_size > refsym->st_size
574 || (GL(dl_verbose) && sym->st_size < refsym->st_size))
576 const char *strtab;
578 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
579 _dl_error_printf ("\
580 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
581 rtld_progname ?: "<program name unknown>",
582 strtab + refsym->st_name);
584 memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
585 refsym->st_size));
586 break;
587 # endif /* !RESOLVE_CONFLICT_FIND_MAP */
588 case R_ARM_GLOB_DAT:
589 case R_ARM_JUMP_SLOT:
590 case R_ARM_ABS32:
591 *reloc_addr = value + reloc->r_addend;
592 break;
593 case R_ARM_PC24:
595 Elf32_Addr newvalue, topbits;
597 newvalue = value + reloc->r_addend - (Elf32_Addr)reloc_addr;
598 topbits = newvalue & 0xfe000000;
599 if (topbits != 0xfe000000 && topbits != 0x00000000)
601 newvalue = fix_bad_pc24(reloc_addr, value)
602 - (Elf32_Addr)reloc_addr + (reloc->r_addend << 2);
603 topbits = newvalue & 0xfe000000;
604 if (topbits != 0xfe000000 && topbits != 0x00000000)
606 INTUSE (_dl_signal_error)
607 (0, map->l_name, NULL,
608 "R_ARM_PC24 relocation out of range");
611 newvalue >>= 2;
612 value = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
613 *reloc_addr = value;
615 break;
616 default:
617 _dl_reloc_bad_type (map, r_type, 0);
618 break;
622 # endif
624 static inline void
625 elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc,
626 Elf32_Addr *const reloc_addr)
628 *reloc_addr += l_addr;
631 # ifndef RTLD_BOOTSTRAP
632 static inline void
633 elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc,
634 Elf32_Addr *const reloc_addr)
636 *reloc_addr = l_addr + reloc->r_addend;
638 # endif
640 static inline void
641 elf_machine_lazy_rel (struct link_map *map,
642 Elf32_Addr l_addr, const Elf32_Rel *reloc)
644 Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
645 const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
646 /* Check for unexpected PLT reloc type. */
647 if (__builtin_expect (r_type == R_ARM_JUMP_SLOT, 1))
649 if (__builtin_expect (map->l_mach.plt, 0) == 0)
650 *reloc_addr += l_addr;
651 else
652 *reloc_addr = map->l_mach.plt;
654 else
655 _dl_reloc_bad_type (map, r_type, 1);
658 #endif /* RESOLVE */