Fixed incorrect use of mvcle introduced by 2001-07-12 change.
[kopensolaris-gnu/glibc.git] / sysdeps / arm / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  ARM version.
2    Copyright (C) 1995,96,97,98,99,2000,2001 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 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.
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    Lesser General Public License for more details.
14
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.  */
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 #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])
34
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");                                            \
44 }
45
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)
49 {
50   return ehdr->e_machine == EM_ARM;
51 }
52
53
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)
59 {
60   register Elf32_Addr *got asm ("r10");
61   return *got;
62 }
63
64
65 /* Return the run-time load address of the shared object.  */
66 static inline Elf32_Addr __attribute__ ((unused))
67 elf_machine_load_address (void)
68 {
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;
74 }
75
76
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.  */
79
80 static inline int __attribute__ ((unused))
81 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
82 {
83   Elf32_Addr *got;
84   extern void _dl_runtime_resolve (Elf32_Word);
85   extern void _dl_runtime_profile (Elf32_Word);
86
87   if (l->l_info[DT_JMPREL] && lazy)
88     {
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       got[1] = (Elf32_Addr) l;  /* Identify this shared object.  */
96
97       /* The got[2] entry contains the address of a function which gets
98          called to get the address of a so far unresolved function and
99          jump to it.  The profiling extension of the dynamic linker allows
100          to intercept the calls to collect information.  In this case we
101          don't store the address in the GOT so that all future calls also
102          end in this function.  */
103       if (profile)
104         {
105           got[2] = (Elf32_Addr) &_dl_runtime_profile;
106           /* Say that we really want profiling and the timers are started.  */
107           _dl_profile_map = l;
108         }
109       else
110         /* This function will get called to fix up the GOT entry indicated by
111            the offset on the stack, and then jump to the resolved address.  */
112         got[2] = (Elf32_Addr) &_dl_runtime_resolve;
113     }
114   return lazy;
115 }
116
117 /* This code is used in dl-runtime.c to call the `fixup' function
118    and then redirect to the address it returns.  */
119    // macro for handling PIC situation....
120 #ifdef PIC
121 #define CALL_ROUTINE(x) " ldr sl,0f
122         add     sl, pc, sl
123 1:      ldr     r2, 2f
124         mov     lr, pc
125         add     pc, sl, r2
126         b       3f
127 0:      .word   _GLOBAL_OFFSET_TABLE_ - 1b - 4
128 2:      .word " #x "(GOTOFF)
129 3:      "
130 #else
131 #define CALL_ROUTINE(x) " bl " #x
132 #endif
133
134 #ifndef PROF
135 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
136         .text
137         .globl _dl_runtime_resolve
138         .type _dl_runtime_resolve, #function
139         .align 2
140 _dl_runtime_resolve:
141         @ we get called with
142         @       stack[0] contains the return address from this call
143         @       ip contains &GOT[n+3] (pointer to function)
144         @       lr points to &GOT[2]
145
146         @ save almost everything; lr is already on the stack
147         stmdb   sp!,{r0-r3,sl,fp}
148
149         @ prepare to call fixup()
150         @ change &GOT[n+3] into 8*n        NOTE: reloc are 8 bytes each
151         sub     r1, ip, lr
152         sub     r1, r1, #4
153         add     r1, r1, r1
154
155         @ get pointer to linker struct
156         ldr     r0, [lr, #-4]
157
158         @ call fixup routine
159         " CALL_ROUTINE(fixup) "
160
161         @ save the return
162         mov     ip, r0
163
164         @ restore the stack
165         ldmia   sp!,{r0-r3,sl,fp,lr}
166
167         @ jump to the newly found address
168         mov     pc, ip
169
170         .size _dl_runtime_resolve, .-_dl_runtime_resolve
171
172         .globl _dl_runtime_profile
173         .type _dl_runtime_profile, #function
174         .align 2
175 _dl_runtime_profile:
176         @ save almost everything; lr is already on the stack
177         stmdb   sp!,{r0-r3,sl,fp}
178
179         @ prepare to call fixup()
180         @ change &GOT[n+3] into 8*n        NOTE: reloc are 8 bytes each
181         sub     r1, ip, lr
182         sub     r1, r1, #4
183         add     r1, r1, r1
184
185         @ get pointer to linker struct
186         ldr     r0, [lr, #-4]
187
188         @ call profiling fixup routine
189         " CALL_ROUTINE(profile_fixup) "
190
191         @ save the return
192         mov     ip, r0
193
194         @ restore the stack
195         ldmia   sp!,{r0-r3,sl,fp,lr}
196
197         @ jump to the newly found address
198         mov     pc, ip
199
200         .size _dl_runtime_resolve, .-_dl_runtime_resolve
201         .previous
202 ");
203 #else // PROF
204 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
205         .text
206         .globl _dl_runtime_resolve
207         .globl _dl_runtime_profile
208         .type _dl_runtime_resolve, #function
209         .type _dl_runtime_profile, #function
210         .align 2
211 _dl_runtime_resolve:
212 _dl_runtime_profile:
213         @ we get called with
214         @       stack[0] contains the return address from this call
215         @       ip contains &GOT[n+3] (pointer to function)
216         @       lr points to &GOT[2]
217
218         @ save almost everything; return add is already on the stack
219         stmdb   sp!,{r0-r3,sl,fp}
220
221         @ prepare to call fixup()
222         @ change &GOT[n+3] into 8*n        NOTE: reloc are 8 bytes each
223         sub     r1, ip, lr
224         sub     r1, r1, #4
225         add     r1, r1, r1
226
227         @ get pointer to linker struct
228         ldr     r0, [lr, #-4]
229
230         @ call profiling fixup routine
231         " CALL_ROUTINE(fixup) "
232
233         @ save the return
234         mov     ip, r0
235
236         @ restore the stack
237         ldmia   sp!,{r0-r3,sl,fp,lr}
238
239         @ jump to the newly found address
240         mov     pc, ip
241
242         .size _dl_runtime_profile, .-_dl_runtime_profile
243         .previous
244 ");
245 #endif //PROF
246
247 /* Mask identifying addresses reserved for the user program,
248    where the dynamic linker should not map anything.  */
249 #define ELF_MACHINE_USER_ADDRESS_MASK   0xf8000000UL
250
251 /* Initial entry point code for the dynamic linker.
252    The C function `_dl_start' is the real entry point;
253    its return value is the user program's entry point.  */
254
255 #define RTLD_START asm ("\
256 .text
257 .globl _start
258 .globl _dl_start_user
259 _start:
260         @ at start time, all the args are on the stack
261         mov     r0, sp
262         bl      _dl_start
263         @ returns user entry point in r0
264 _dl_start_user:
265         mov     r6, r0
266         @ we are PIC code, so get global offset table
267         ldr     sl, .L_GET_GOT
268         add     sl, pc, sl
269 .L_GOT_GOT:
270         @ Store the highest stack address
271         ldr     r1, .L_STACK_END
272         ldr     r1, [sl, r1]
273         str     sp, [r1]
274         @ See if we were run as a command with the executable file
275         @ name as an extra leading argument.
276         ldr     r4, .L_SKIP_ARGS
277         ldr     r4, [sl, r4]
278         @ get the original arg count
279         ldr     r1, [sp]
280         @ subtract _dl_skip_args from it
281         sub     r1, r1, r4
282         @ adjust the stack pointer to skip them
283         add     sp, sp, r4, lsl #2
284         @ get the argv address
285         add     r2, sp, #4
286         @ store the new argc in the new stack location
287         str     r1, [sp]
288         @ compute envp
289         add     r3, r2, r1, lsl #2
290         add     r3, r3, #4
291
292         @ now we call _dl_init
293         ldr     r0, .L_LOADED
294         ldr     r0, [sl, r0]
295         ldr     r0, [r0]
296         @ call _dl_init
297         bl      _dl_init(PLT)
298         @ clear the startup flag
299         ldr     r2, .L_STARTUP_FLAG
300         ldr     r1, [sl, r2]
301         mov     r0, #0
302         str     r0, [r1]
303         @ load the finalizer function
304         ldr     r0, .L_FINI_PROC
305         ldr     r0, [sl, r0]
306         @ jump to the user_s entry point
307         mov     pc, r6
308 .L_GET_GOT:
309         .word   _GLOBAL_OFFSET_TABLE_ - .L_GOT_GOT - 4  \n\
310 .L_SKIP_ARGS:                                   \n\
311         .word   _dl_skip_args(GOTOFF)           \n\
312 .L_STARTUP_FLAG:
313         .word   _dl_starting_up(GOT)
314 .L_FINI_PROC:
315         .word   _dl_fini(GOT)
316 .L_STACK_END:
317         .word   __libc_stack_end(GOT)
318 .L_LOADED:
319         .word   _dl_loaded(GOT)
320 .previous\n\
321 ");
322
323 /* Nonzero iff TYPE should not be allowed to resolve to one of
324    the main executable's symbols, as for a COPY reloc.  */
325 #define elf_machine_lookup_noexec_p(type) ((type) == R_ARM_COPY)
326
327 /* Nonzero iff TYPE describes relocation of a PLT entry, so
328    PLT entries should not be allowed to define the value.  */
329 #define elf_machine_lookup_noplt_p(type) ((type) == R_ARM_JUMP_SLOT)
330
331 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
332 #define ELF_MACHINE_JMP_SLOT    R_ARM_JUMP_SLOT
333
334 /* The ARM never uses Elf32_Rela relocations.  */
335 #define ELF_MACHINE_NO_RELA 1
336
337 /* We define an initialization functions.  This is called very early in
338    _dl_sysdep_start.  */
339 #define DL_PLATFORM_INIT dl_platform_init ()
340
341 extern const char *_dl_platform;
342
343 static inline void __attribute__ ((unused))
344 dl_platform_init (void)
345 {
346   if (_dl_platform != NULL && *_dl_platform == '\0')
347     /* Avoid an empty string which would disturb us.  */
348     _dl_platform = NULL;
349 }
350
351 static inline Elf32_Addr
352 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
353                        const Elf32_Rel *reloc,
354                        Elf32_Addr *reloc_addr, Elf32_Addr value)
355 {
356   return *reloc_addr = value;
357 }
358
359 /* Return the final value of a plt relocation.  */
360 static inline Elf32_Addr
361 elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc,
362                        Elf32_Addr value)
363 {
364   return value;
365 }
366
367 #endif /* !dl_machine_h */
368
369 #ifdef RESOLVE
370
371 extern char **_dl_argv;
372
373 /* Deal with an out-of-range PC24 reloc.  */
374 static Elf32_Addr
375 fix_bad_pc24 (Elf32_Addr *const reloc_addr, Elf32_Addr value)
376 {
377   static void *fix_page;
378   static unsigned int fix_offset;
379   static size_t pagesize;
380   Elf32_Word *fix_address;
381
382   if (! fix_page)
383     {
384       if (! pagesize)
385         pagesize = getpagesize ();
386       fix_page = mmap (NULL, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC,
387                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
388       if (! fix_page)
389         assert (! "could not map page for fixup");
390       fix_offset = 0;
391     }
392
393   fix_address = (Elf32_Word *)(fix_page + fix_offset);
394   fix_address[0] = 0xe51ff004;  /* ldr pc, [pc, #-4] */
395   fix_address[1] = value;
396
397   fix_offset += 8;
398   if (fix_offset >= pagesize)
399     fix_page = NULL;
400
401   return (Elf32_Addr)fix_address;
402 }
403
404 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
405    MAP is the object containing the reloc.  */
406
407 static inline void
408 elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
409                  const Elf32_Sym *sym, const struct r_found_version *version,
410                  Elf32_Addr *const reloc_addr)
411 {
412   if (ELF32_R_TYPE (reloc->r_info) == R_ARM_RELATIVE)
413     {
414 #ifndef RTLD_BOOTSTRAP
415       if (map != &_dl_rtld_map) /* Already done in rtld itself.  */
416 #endif
417         *reloc_addr += map->l_addr;
418     }
419   else if (ELF32_R_TYPE (reloc->r_info) != R_ARM_NONE)
420     {
421       const Elf32_Sym *const refsym = sym;
422       Elf32_Addr value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info));
423       if (sym)
424         value += sym->st_value;
425
426       switch (ELF32_R_TYPE (reloc->r_info))
427         {
428         case R_ARM_COPY:
429           if (sym == NULL)
430             /* This can happen in trace mode if an object could not be
431                found.  */
432             break;
433           if (sym->st_size > refsym->st_size
434               || (_dl_verbose && sym->st_size < refsym->st_size))
435             {
436               const char *strtab;
437
438               strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
439               _dl_error_printf ("\
440 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
441                                 _dl_argv[0] ?: "<program name unknown>",
442                                 strtab + refsym->st_name);
443             }
444           memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
445                                                    refsym->st_size));
446           break;
447         case R_ARM_GLOB_DAT:
448         case R_ARM_JUMP_SLOT:
449 #ifdef RTLD_BOOTSTRAP
450           /* Fix weak undefined references.  */
451           if (sym != NULL && sym->st_value == 0)
452             *reloc_addr = 0;
453           else
454 #endif
455             *reloc_addr = value;
456           break;
457         case R_ARM_ABS32:
458           {
459 #ifndef RTLD_BOOTSTRAP
460            /* This is defined in rtld.c, but nowhere in the static
461               libc.a; make the reference weak so static programs can
462               still link.  This declaration cannot be done when
463               compiling rtld.c (i.e.  #ifdef RTLD_BOOTSTRAP) because
464               rtld.c contains the common defn for _dl_rtld_map, which
465               is incompatible with a weak decl in the same file.  */
466             weak_extern (_dl_rtld_map);
467             if (map == &_dl_rtld_map)
468               /* Undo the relocation done here during bootstrapping.
469                  Now we will relocate it anew, possibly using a
470                  binding found in the user program or a loaded library
471                  rather than the dynamic linker's built-in definitions
472                  used while loading those libraries.  */
473               value -= map->l_addr + refsym->st_value;
474 #endif
475             *reloc_addr += value;
476             break;
477           }
478         case R_ARM_PC24:
479           {
480              Elf32_Sword addend;
481              Elf32_Addr newvalue, topbits;
482
483              addend = *reloc_addr & 0x00ffffff;
484              if (addend & 0x00800000) addend |= 0xff000000;
485
486              newvalue = value - (Elf32_Addr)reloc_addr + (addend << 2);
487              topbits = newvalue & 0xfe000000;
488              if (topbits != 0xfe000000 && topbits != 0x00000000)
489                {
490                  newvalue = fix_bad_pc24(reloc_addr, value)
491                    - (Elf32_Addr)reloc_addr + (addend << 2);
492                  topbits = newvalue & 0xfe000000;
493                  if (topbits != 0xfe000000 && topbits != 0x00000000)
494                    {
495                      _dl_signal_error (0, map->l_name,
496                                        "R_ARM_PC24 relocation out of range");
497                    }
498                }
499              newvalue >>= 2;
500              value = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
501              *reloc_addr = value;
502           }
503         break;
504         default:
505           _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 0);
506           break;
507         }
508     }
509 }
510
511 static inline void
512 elf_machine_lazy_rel (struct link_map *map,
513                       Elf32_Addr l_addr, const Elf32_Rel *reloc)
514 {
515   Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
516   /* Check for unexpected PLT reloc type.  */
517   if (ELF32_R_TYPE (reloc->r_info) == R_ARM_JUMP_SLOT)
518     *reloc_addr += l_addr;
519   else
520     _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 1);
521 }
522
523 #endif /* RESOLVE */