chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / ports / sysdeps / am33 / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  AM33 version.
2    Copyright (C) 1995,96,97,98,99,2000,2001, 2004
3    Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #ifndef dl_machine_h
22 #define dl_machine_h
23
24 #define ELF_MACHINE_NAME "mn10300"
25
26 #include <sys/param.h>
27
28 /* Return nonzero iff ELF header is compatible with the running host.  */
29 static inline int __attribute__ ((unused))
30 elf_machine_matches_host (const Elf32_Ehdr *ehdr)
31 {
32   return ehdr->e_machine == EM_MN10300;
33 }
34
35
36 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
37    first element of the GOT.  This must be inlined in a function which
38    uses global data.  */
39 static inline Elf32_Addr __attribute__ ((unused))
40 elf_machine_dynamic (void)
41 {
42   register Elf32_Addr *got asm ("a2");
43   return *got;
44 }
45
46
47 /* Return the run-time load address of the shared object.  */
48 static inline Elf32_Addr __attribute__ ((unused))
49 elf_machine_load_address (void)
50 {
51   register Elf32_Addr gotaddr asm ("a2");
52   Elf32_Addr off, gotval;
53
54   asm ("mov _dl_start@GOTOFF,%0" : "=r" (off));
55   asm ("mov (_dl_start@GOT,%1),%0" : "=r" (gotval) : "r" (gotaddr));
56
57   return off + gotaddr - gotval;
58 }
59
60 #if !defined PROF && !__BOUNDED_POINTERS__
61 /* We add a declaration of this function here so that in dl-runtime.c
62    the ELF_MACHINE_RUNTIME_TRAMPOLINE macro really can pass the parameters
63    in registers.
64
65    We cannot use this scheme for profiling because the _mcount call
66    destroys the passed register information.  */
67 /* GKM FIXME: Fix trampoline to pass bounds so we can do
68    without the `__unbounded' qualifier.  */
69 static ElfW(Addr) fixup (struct link_map *__unbounded l, ElfW(Word) reloc_offset)
70      __attribute__ ((unused));
71 static ElfW(Addr) profile_fixup (struct link_map *l, ElfW(Word) reloc_offset,
72                                  ElfW(Addr) retaddr)
73      __attribute__ ((unused));
74 #endif
75
76 /* Set up the loaded object described by L so its unrelocated PLT
77    entries will jump to the on-demand fixup code in dl-runtime.c.  */
78
79 static inline int __attribute__ ((unused))
80 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
81 {
82   Elf32_Addr *got;
83   extern void _dl_runtime_resolve (Elf32_Word) attribute_hidden;
84   extern void _dl_runtime_profile (Elf32_Word) attribute_hidden;
85
86   if (l->l_info[DT_JMPREL] && lazy)
87     {
88       /* The GOT entries for functions in the PLT have not yet been filled
89          in.  Their initial contents will arrange when called to push an
90          offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1],
91          and then jump to _GLOBAL_OFFSET_TABLE[2].  */
92       got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
93       got[1] = (Elf32_Addr) l;  /* Identify this shared object.  */
94
95       /* The got[2] entry contains the address of a function which gets
96          called to get the address of a so far unresolved function and
97          jump to it.  The profiling extension of the dynamic linker allows
98          to intercept the calls to collect information.  In this case we
99          don't store the address in the GOT so that all future calls also
100          end in this function.  */
101       if (__builtin_expect (profile, 0))
102         {
103           got[2] = (Elf32_Addr) &_dl_runtime_profile;
104
105           if (_dl_name_match_p (GLRO(dl_profile), l))
106             /* This is the object we are looking for.  Say that we really
107                want profiling and the timers are started.  */
108             GL(dl_profile_map) = l;
109         }
110       else
111         /* This function will get called to fix up the GOT entry indicated by
112            the offset on the stack, and then jump to the resolved address.  */
113         got[2] = (Elf32_Addr) &_dl_runtime_resolve;
114     }
115
116   return lazy;
117 }
118
119 /* This code is used in dl-runtime.c to call the `fixup' function
120    and then redirect to the address it returns.  */
121 #if !defined PROF && !__BOUNDED_POINTERS__
122 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
123         .text\n\
124         .globl _dl_runtime_resolve\n\
125         .type _dl_runtime_resolve, @function\n\
126 _dl_runtime_resolve:\n\
127         add -12,sp              # Preserve registers otherwise clobbered.\n\
128         mov d1,(20,sp)\n\
129         mov d0,(16,sp)\n\
130         mov r1,d0\n\
131         mov r0,d1\n\
132         call fixup,[],0         # Call resolver.\n\
133         mov d0,a0\n\
134         mov (12,sp),d1          # Copy return address back to mdr,\n\
135         mov d1,mdr              # in case the callee returns with retf\n\
136         mov (16,sp),d0          # Get register content back.\n\
137         mov (20,sp),d1\n\
138         add 12,sp\n\
139         jmp (a0)\n\
140         .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\
141 \n\
142         .globl _dl_runtime_profile\n\
143         .type _dl_runtime_profile, @function\n\
144 _dl_runtime_profile:\n\
145         add -12,sp              # Preserve registers otherwise clobbered.\n\
146         mov d1,(20,sp)\n\
147         mov d0,(16,sp)\n\
148         mov r1,d0\n\
149         mov r0,d1\n\
150         call profile_fixup,[],0         # Call resolver.\n\
151         mov d0,a0\n\
152         mov (12,sp),d1          # Copy return address back to mdr,\n\
153         mov d1,mdr              # in case the callee returns with retf\n\
154         mov (16,sp),d0          # Get register content back.\n\
155         mov (20,sp),d1\n\
156         add 12,sp\n\
157         jmp (a0)\n\
158         .size _dl_runtime_profile, .-_dl_runtime_profile\n\
159         .previous\n\
160 ");
161 #else
162 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\n\
163         .text\n\
164         .globl _dl_runtime_resolve\n\
165         .globl _dl_runtime_profile\n\
166         .type _dl_runtime_resolve, @function\n\
167         .type _dl_runtime_profile, @function\n\
168 _dl_runtime_resolve:\n\
169 _dl_runtime_profile:\n\
170         add -12,sp              # Preserve registers otherwise clobbered.\n\
171         mov d1,(20,sp)\n\
172         mov d0,(16,sp)\n\
173         mov r1,d0\n\
174         mov r0,d1\n\
175         call profile_fixup,[],0         # Call resolver.\n\
176         mov d0,a0\n\
177         mov (12,sp),d1          # Copy return address back to mdr,\n\
178         mov d1,mdr              # in case the callee returns with retf\n\
179         mov (16,sp),d0          # Get register content back.\n\
180         mov (20,sp),d1\n\
181         add 12,sp\n\
182         jmp (a0)\n\
183         .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\
184         .size _dl_runtime_profile, .-_dl_runtime_profile\n\
185         .previous\n\
186 ");
187 #endif
188
189 /* Mask identifying addresses reserved for the user program,
190    where the dynamic linker should not map anything.  */
191 #define ELF_MACHINE_USER_ADDRESS_MASK   0xf8000000UL
192
193 /* Initial entry point code for the dynamic linker.
194    The C function `_dl_start' is the real entry point;
195    its return value is the user program's entry point.  */
196 #define RTLD_START asm ("\n\
197         .text\n\
198 .globl _start\n\
199 .globl _dl_start_user\n\
200 _start:\n\
201         mov 0,a3        # Mark the top of the stack\n\
202         mov sp,a1\n\
203         add -20,sp      # Prepare for function call\n\
204         mov a1,d0\n\
205         call _dl_start,[],0\n\
206 _dl_start_user:\n\
207         # Save the user entry point address in d2.\n\
208         mov d0,d2\n\
209         # Point a2 at the GOT.\n\
210 0:      mov pc,a2\n\
211         add _GLOBAL_OFFSET_TABLE_ - (0b-.),a2\n\
212         # Store the highest stack address\n\
213         mov (__libc_stack_end@GOT,a2),a0\n\
214         mov a1,(a0)\n\
215         # See if we were run as a command with the executable file\n\
216         # name as an extra leading argument.\n\
217         mov (_dl_skip_args@GOT,a2),a0\n\
218         mov (a0),d0\n\
219         # Pop the original argument count.\n\
220         mov (20,sp),d3\n\
221         # Subtract _dl_skip_args from it.\n\
222         sub d0,d3\n\
223         # Adjust the stack pointer to skip _dl_skip_args words.\n\
224         asl2 d0\n\
225         mov sp,a0\n\
226         add d0,a0\n\
227         mov a0,sp\n\
228         # Push argc back on the stack.\n\
229         mov d3,(20,sp)\n\
230         # The special initializer gets called with the stack just\n\
231         # as the application's entry point will see it; it can\n\
232         # switch stacks if it moves these contents over.\n\
233 " RTLD_START_SPECIAL_INIT "\n\
234         # Load the parameters again.\n\
235         # (d0, d1, (12,sp), (16,sp)) = (_dl_loaded, argc, argv, envp)\n\
236         add 24,a0\n\
237         mov a0,(12,sp)  # a0 is 24+sp\n\
238         mov d3,d1       # d3 contained argc\n\
239         inc d3\n\
240         asl2 d3         # d3 is now (argc+1)*4,\n\
241         add d3,a0       # the offset between argv and envp\n\
242         mov a0,(16,sp)\n\
243         mov (_rtld_local@GOTOFF,a2),d0\n\
244         # Call the function to run the initializers.\n\
245         call _dl_init@PLT,[],0\n\
246         # Pass our finalizer function to the user in d0, as per ELF ABI.\n\
247         mov (_dl_fini@GOT,a2),d0\n\
248         add 20,sp\n\
249         # Jump to the user's entry point.\n\
250         mov d2,a1\n\
251         jmp (a1)\n\
252         .previous\n\
253 ");
254
255 #ifndef RTLD_START_SPECIAL_INIT
256 #define RTLD_START_SPECIAL_INIT /* nothing */
257 #endif
258
259 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
260    PLT entries should not be allowed to define the value.
261    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
262    of the main executable's symbols, as for a COPY reloc.  */
263 #define elf_machine_type_class(type) \
264   ((((type) == R_MN10300_JMP_SLOT) * ELF_RTYPE_CLASS_PLT)       \
265    | (((type) == R_MN10300_COPY) * ELF_RTYPE_CLASS_COPY))
266
267 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
268 #define ELF_MACHINE_JMP_SLOT    R_MN10300_JMP_SLOT
269
270 static inline Elf32_Addr
271 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
272                        const Elf32_Rela *reloc,
273                        Elf32_Addr *reloc_addr, Elf32_Addr value)
274 {
275   return *reloc_addr = value;
276 }
277
278 /* Return the final value of a plt relocation.  */
279 static inline Elf32_Addr
280 elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
281                        Elf32_Addr value)
282 {
283   return value + reloc->r_addend;
284 }
285
286 #endif /* !dl_machine_h */
287
288 #ifdef RESOLVE
289
290 /* The mn10300 never uses Elf32_Rel relocations.  */
291 #define ELF_MACHINE_NO_REL 1
292
293 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
294    MAP is the object containing the reloc.  */
295
296 static inline void
297 elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
298                   const Elf32_Sym *sym, const struct r_found_version *version,
299                   void *const reloc_addr_arg)
300 {
301   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
302   Elf32_Addr value, *reloc_addr;
303
304   /* Make sure we drop any previous alignment assumptions.  */
305   asm ("" : "=r" (reloc_addr) : "0" (reloc_addr_arg));
306
307 #define COPY_UNALIGNED_WORD(sw, tw, align) \
308   { \
309     unsigned long *__sl = (void*)&(sw), *__tl = (void*)&(tw); \
310     unsigned short *__ss = (void*)&(sw), *__ts = (void*)&(tw); \
311     unsigned char *__sc = (void*)&(sw), *__tc = (void*)&(tw); \
312     switch ((align)) \
313     { \
314     case 0: \
315       *__tl = *__sl; \
316       break; \
317     case 2: \
318       *__ts++ = *__ss++; \
319       *__ts = *__ss; \
320       break; \
321     default: \
322       *__tc++ = *__sc++; \
323       *__tc++ = *__sc++; \
324       *__tc++ = *__sc++; \
325       *__tc = *__sc; \
326       break; \
327     } \
328   }
329
330 #define COPY_UNALIGNED_HALFWORD(sw, tw, align) \
331   { \
332     unsigned short *__ss = (void*)&(sw), *__ts = (void*)&(tw); \
333     unsigned char *__sc = (void*)&(sw), *__tc = (void*)&(tw); \
334     switch ((align)) \
335     { \
336     case 0: \
337       *__ts = *__ss; \
338       break; \
339     default: \
340       *__tc++ = *__sc++; \
341       *__tc = *__sc; \
342       break; \
343     } \
344   }
345
346 #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
347   if (__builtin_expect (r_type == R_MN10300_RELATIVE, 0))
348     {
349 # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
350       /* This is defined in rtld.c, but nowhere in the static libc.a;
351          make the reference weak so static programs can still link.
352          This declaration cannot be done when compiling rtld.c (i.e.
353          #ifdef RTLD_BOOTSTRAP) because rtld.c contains the common
354          defn for _dl_rtld_map, which is incompatible with a weak decl
355          in the same file.  */
356       weak_extern (_dl_rtld_map);
357       if (map != &_dl_rtld_map) /* Already done in rtld itself. */
358 # endif
359         {
360           COPY_UNALIGNED_WORD (*reloc_addr, value, (int) reloc_addr & 3);
361           value += map->l_addr;
362           COPY_UNALIGNED_WORD (value, *reloc_addr, (int) reloc_addr & 3);
363         }
364     }
365 # ifndef RTLD_BOOTSTRAP
366   else if (__builtin_expect (r_type == R_MN10300_NONE, 0))
367     return;
368 # endif
369   else
370 #endif
371     {
372 #ifndef RTLD_BOOTSTRAP
373       const Elf32_Sym *const refsym = sym;
374 #endif
375
376       value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info));
377       if (sym)
378         value += sym->st_value;
379       value += reloc->r_addend; /* Assume copy relocs have zero addend.  */
380
381       switch (r_type)
382         {
383 #ifndef RTLD_BOOTSTRAP
384         case R_MN10300_COPY:
385           if (sym == NULL)
386             /* This can happen in trace mode if an object could not be
387                found.  */
388             break;
389           if (sym->st_size > refsym->st_size
390               || (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
391             {
392               extern char **_dl_argv;
393               const char *strtab;
394
395               strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
396               _dl_error_printf ("\
397 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
398                                 _dl_argv[0] ?: "<program name unknown>",
399                                 strtab + refsym->st_name);
400             }
401           memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
402                                                    refsym->st_size));
403           break;
404 #endif
405         case R_MN10300_GLOB_DAT:
406         case R_MN10300_JMP_SLOT:
407           /* These addresses are always aligned.  */
408           *reloc_addr = value;
409           break;
410         case R_MN10300_32:
411           COPY_UNALIGNED_WORD (value, *reloc_addr, (int) reloc_addr & 3);
412           break;
413 #ifndef RTLD_BOOTSTRAP
414         case R_MN10300_16:
415           COPY_UNALIGNED_HALFWORD (value, *reloc_addr, (int) reloc_addr & 1);
416           break;
417         case R_MN10300_8:
418           *(char *) reloc_addr = value;
419           break;
420         case R_MN10300_PCREL32:
421           value -= (Elf32_Addr) reloc_addr;
422           COPY_UNALIGNED_WORD (value, *reloc_addr, (int) reloc_addr & 3);
423           break;
424         case R_MN10300_PCREL16:
425           value -= (Elf32_Addr) reloc_addr;
426           COPY_UNALIGNED_HALFWORD (value, *reloc_addr, (int) reloc_addr & 1);
427           break;
428         case R_MN10300_PCREL8:
429           value -= (Elf32_Addr) reloc_addr;
430           *(char *) reloc_addr = (value - (Elf32_Addr) reloc_addr);
431           break;
432 #endif
433         case R_MN10300_NONE:            /* Alright, Wilbur.  */
434           break;
435 #if !defined RTLD_BOOTSTRAP || defined _NDEBUG
436         default:
437           _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 0);
438           break;
439 #endif
440         }
441
442     }
443 }
444
445 static inline void
446 elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc,
447                            void *const reloc_addr_arg)
448 {
449   Elf32_Addr value, *reloc_addr;
450
451   asm ("" : "=r" (reloc_addr) : "0" (reloc_addr_arg));
452
453   COPY_UNALIGNED_WORD (*reloc_addr, value, (int)reloc_addr & 3);
454   value += l_addr;
455   COPY_UNALIGNED_WORD (value, *reloc_addr, (int)reloc_addr & 3);
456 }
457
458 static inline void
459 elf_machine_lazy_rel (struct link_map *map,
460                       Elf32_Addr l_addr, const Elf32_Rela *reloc)
461 {
462   unsigned long int const r_type = ELF32_R_TYPE (reloc->r_info);
463
464   /* Check for unexpected PLT reloc type.  */
465   if (__builtin_expect (r_type, R_MN10300_JMP_SLOT) == R_MN10300_JMP_SLOT)
466     {
467       Elf32_Addr* const reloc_addr = (void *)(l_addr + reloc->r_offset);
468       Elf32_Addr value;
469
470       /* Perform a RELATIVE reloc on the .got entry that transfers
471          to the .plt.  */
472       COPY_UNALIGNED_WORD (*reloc_addr, value, (int)reloc_addr & 3);
473       value += l_addr;
474       COPY_UNALIGNED_WORD (value, *reloc_addr, (int)reloc_addr & 3);
475     }
476   else if (__builtin_expect (r_type, R_MN10300_NONE) != R_MN10300_NONE)
477     _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 1);
478
479 }
480
481 #endif /* RESOLVE */