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