Update copyright notices with scripts/update-copyrights
[jlayton/glibc.git] / ports / sysdeps / aarch64 / dl-machine.h
1 /* Copyright (C) 1995-2014 Free Software Foundation, Inc.
2
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 License as
7    published by the Free Software Foundation; either version 2.1 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    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, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #ifndef dl_machine_h
20 #define dl_machine_h
21
22 #define ELF_MACHINE_NAME "aarch64"
23
24 #include <tls.h>
25 #include <dl-tlsdesc.h>
26 #include <dl-irel.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 ElfW(Ehdr) *ehdr)
31 {
32   return ehdr->e_machine == EM_AARCH64;
33 }
34
35 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
36    first element of the GOT. */
37 static inline ElfW(Addr) __attribute__ ((unused))
38 elf_machine_dynamic (void)
39 {
40   extern const ElfW(Addr) _GLOBAL_OFFSET_TABLE_[] attribute_hidden;
41   return _GLOBAL_OFFSET_TABLE_[0];
42 }
43
44 /* Return the run-time load address of the shared object.  */
45
46 static inline ElfW(Addr) __attribute__ ((unused))
47 elf_machine_load_address (void)
48 {
49   /* To figure out the load address we use the definition that for any symbol:
50      dynamic_addr(symbol) = static_addr(symbol) + load_addr
51
52      The choice of symbol is arbitrary. The static address we obtain
53      by constructing a non GOT reference to the symbol, the dynamic
54      address of the symbol we compute using adrp/add to compute the
55      symbol's address relative to the PC. */
56
57   ElfW(Addr) static_addr;
58   ElfW(Addr) dynamic_addr;
59
60   asm ("                                        \n\
61         adrp    %1, _dl_start;                  \n\
62         add     %1, %1, #:lo12:_dl_start        \n\
63         ldr     %w0, 1f                         \n\
64         b       2f                              \n\
65 1:      .word   _dl_start                       \n\
66 2:                                              \n\
67        " : "=r" (static_addr),  "=r" (dynamic_addr));
68   return dynamic_addr - static_addr;
69 }
70
71 /* Set up the loaded object described by L so its unrelocated PLT
72    entries will jump to the on-demand fixup code in dl-runtime.c.  */
73
74 static inline int __attribute__ ((unused))
75 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
76 {
77   if (l->l_info[DT_JMPREL] && lazy)
78     {
79       ElfW(Addr) *got;
80       extern void _dl_runtime_resolve (ElfW(Word));
81       extern void _dl_runtime_profile (ElfW(Word));
82
83       got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
84       if (got[1])
85         {
86           l->l_mach.plt = got[1] + l->l_addr;
87         }
88       got[1] = (ElfW(Addr)) l;
89
90       /* The got[2] entry contains the address of a function which gets
91          called to get the address of a so far unresolved function and
92          jump to it.  The profiling extension of the dynamic linker allows
93          to intercept the calls to collect information.  In this case we
94          don't store the address in the GOT so that all future calls also
95          end in this function.  */
96       if ( profile)
97         {
98            got[2] = (ElfW(Addr)) &_dl_runtime_profile;
99
100           if (GLRO(dl_profile) != NULL
101               && _dl_name_match_p (GLRO(dl_profile), l))
102             /* Say that we really want profiling and the timers are
103                started.  */
104             GL(dl_profile_map) = l;
105         }
106       else
107         {
108           /* This function will get called to fix up the GOT entry
109              indicated by the offset on the stack, and then jump to
110              the resolved address.  */
111           got[2] = (ElfW(Addr)) &_dl_runtime_resolve;
112         }
113     }
114
115   if (l->l_info[ADDRIDX (DT_TLSDESC_GOT)] && lazy)
116     *(Elf64_Addr*)(D_PTR (l, l_info[ADDRIDX (DT_TLSDESC_GOT)]) + l->l_addr)
117       = (Elf64_Addr) &_dl_tlsdesc_resolve_rela;
118
119   return lazy;
120 }
121
122 /* Initial entry point for the dynamic linker. The C function
123    _dl_start is the real entry point, its return value is the user
124    program's entry point */
125
126 #define RTLD_START asm ("\
127 .text                                                           \n\
128 .globl _start                                                   \n\
129 .type _start, %function                                         \n\
130 .globl _dl_start_user                                           \n\
131 .type _dl_start_user, %function                                 \n\
132 _start:                                                         \n\
133         mov     x0,     sp                                      \n\
134         bl      _dl_start                                       \n\
135         // returns user entry point in x0                       \n\
136         mov     x21, x0                                         \n\
137 _dl_start_user:                                                 \n\
138         // get the original arg count                           \n\
139         ldr     x1, [sp]                                        \n\
140         // get the argv address                                 \n\
141         add     x2, sp, #8                                      \n\
142         // get _dl_skip_args to see if we were                  \n\
143         // invoked as an executable                             \n\
144         adrp    x4, _dl_skip_args                               \n\
145         ldr     w4, [x4, #:lo12:_dl_skip_args]                  \n\
146         // do we need to adjust argc/argv                       \n\
147         cmp     w4, 0                                           \n\
148         beq     .L_done_stack_adjust                            \n\
149         // subtract _dl_skip_args from original arg count       \n\
150         sub     x1, x1, x4                                      \n\
151         // store adjusted argc back to stack                    \n\
152         str     x1, [sp]                                        \n\
153         // find the first unskipped argument                    \n\
154         mov     x3, x2                                          \n\
155         add     x4, x2, x4, lsl #3                              \n\
156         // shuffle argv down                                    \n\
157 1:      ldr     x5, [x4], #8                                    \n\
158         str     x5, [x3], #8                                    \n\
159         cmp     x5, #0                                          \n\
160         bne     1b                                              \n\
161         // shuffle envp down                                    \n\
162 1:      ldr     x5, [x4], #8                                    \n\
163         str     x5, [x3], #8                                    \n\
164         cmp     x5, #0                                          \n\
165         bne     1b                                              \n\
166         // shuffle auxv down                                    \n\
167 1:      ldp     x0, x5, [x4, #16]!                              \n\
168         stp     x0, x5, [x3], #16                               \n\
169         cmp     x0, #0                                          \n\
170         bne     1b                                              \n\
171         // Update _dl_argv                                      \n\
172         adrp    x3, _dl_argv                                    \n\
173         str     x2, [x3, #:lo12:_dl_argv]                       \n\
174 .L_done_stack_adjust:                                           \n\
175         // compute envp                                         \n\
176         add     x3, x2, x1, lsl #3                              \n\
177         add     x3, x3, #8                                      \n\
178         adrp    x16, _rtld_local                                \n\
179         add     x16, x16, #:lo12:_rtld_local                    \n\
180         ldr     x0, [x16]                                       \n\
181         bl      _dl_init_internal                               \n\
182         // load the finalizer function                          \n\
183         adrp    x0, _dl_fini                                    \n\
184         add     x0, x0, #:lo12:_dl_fini                         \n\
185         // jump to the user_s entry point                       \n\
186         br      x21                                             \n\
187 ");
188
189 #define elf_machine_type_class(type)                                    \
190   ((((type) == R_AARCH64_JUMP_SLOT ||                                   \
191      (type) == R_AARCH64_TLS_DTPMOD64 ||                                \
192      (type) == R_AARCH64_TLS_DTPREL64 ||                                \
193      (type) == R_AARCH64_TLS_TPREL64 ||                                 \
194      (type) == R_AARCH64_TLSDESC) * ELF_RTYPE_CLASS_PLT)                \
195    | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY))
196
197 #define ELF_MACHINE_JMP_SLOT    R_AARCH64_JUMP_SLOT
198
199 /* AArch64 uses RELA not REL */
200 #define ELF_MACHINE_NO_REL 1
201
202 static inline ElfW(Addr)
203 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
204                        const ElfW(Rela) *reloc,
205                        ElfW(Addr) *reloc_addr,
206                        ElfW(Addr) value)
207 {
208   return *reloc_addr = value;
209 }
210
211 /* Return the final value of a plt relocation.  */
212 static inline ElfW(Addr)
213 elf_machine_plt_value (struct link_map *map,
214                        const ElfW(Rela) *reloc,
215                        ElfW(Addr) value)
216 {
217   return value;
218 }
219
220 #endif
221
222 /* Names of the architecture-specific auditing callback functions.  */
223 #define ARCH_LA_PLTENTER aarch64_gnu_pltenter
224 #define ARCH_LA_PLTEXIT  aarch64_gnu_pltexit
225
226 #ifdef RESOLVE_MAP
227
228 auto inline void
229 __attribute__ ((always_inline))
230 elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
231                   const ElfW(Sym) *sym, const struct r_found_version *version,
232                   void *const reloc_addr_arg, int skip_ifunc)
233 {
234   ElfW(Addr) *const reloc_addr = reloc_addr_arg;
235   const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
236
237   if (__builtin_expect (r_type == R_AARCH64_RELATIVE, 0))
238       *reloc_addr = map->l_addr + reloc->r_addend;
239   else if (__builtin_expect (r_type == R_AARCH64_NONE, 0))
240       return;
241   else
242     {
243       const ElfW(Sym) *const refsym = sym;
244       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
245       ElfW(Addr) value = sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value;
246
247       if (sym != NULL
248           && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
249           && __glibc_likely (sym->st_shndx != SHN_UNDEF)
250           && __glibc_likely (!skip_ifunc))
251         value = elf_ifunc_invoke (value);
252
253       switch (r_type)
254         {
255         case R_AARCH64_COPY:
256           if (sym == NULL)
257               break;
258
259           if (sym->st_size > refsym->st_size
260               || (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
261             {
262               const char *strtab;
263
264               strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
265               _dl_error_printf ("\
266 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
267                                 RTLD_PROGNAME, strtab + refsym->st_name);
268             }
269           memcpy (reloc_addr_arg, (void *) value,
270                   MIN (sym->st_size, refsym->st_size));
271           break;
272
273         case R_AARCH64_RELATIVE:
274         case R_AARCH64_GLOB_DAT:
275         case R_AARCH64_JUMP_SLOT:
276         case R_AARCH64_ABS32:
277         case R_AARCH64_ABS64:
278           *reloc_addr = value + reloc->r_addend;
279           break;
280
281         case R_AARCH64_TLSDESC:
282           {
283             struct tlsdesc volatile *td =
284               (struct tlsdesc volatile *)reloc_addr;
285 #ifndef RTLD_BOOTSTRAP
286             if (! sym)
287               {
288                 td->arg = (void*)reloc->r_addend;
289                 td->entry = _dl_tlsdesc_undefweak;
290               }
291             else
292 #endif
293               {
294 #ifndef RTLD_BOOTSTRAP
295 # ifndef SHARED
296                 CHECK_STATIC_TLS (map, sym_map);
297 # else
298                 if (!TRY_STATIC_TLS (map, sym_map))
299                   {
300                     td->arg = _dl_make_tlsdesc_dynamic
301                       (sym_map, sym->st_value + reloc->r_addend);
302                     td->entry = _dl_tlsdesc_dynamic;
303                   }
304                 else
305 # endif
306 #endif
307                   {
308                     td->arg = (void*)(sym->st_value + sym_map->l_tls_offset
309                                       + reloc->r_addend);
310                     td->entry = _dl_tlsdesc_return;
311                   }
312               }
313             break;
314           }
315
316         case R_AARCH64_TLS_DTPMOD64:
317 #ifdef RTLD_BOOTSTRAP
318           *reloc_addr = 1;
319 #else
320           if (sym_map != NULL)
321             {
322               *reloc_addr = sym_map->l_tls_modid;
323             }
324 #endif
325           break;
326
327         case R_AARCH64_TLS_DTPREL64:
328           if (sym)
329             *reloc_addr = sym->st_value + reloc->r_addend;
330           break;
331
332         case R_AARCH64_TLS_TPREL64:
333           if (sym)
334             {
335               CHECK_STATIC_TLS (map, sym_map);
336               *reloc_addr =
337                 sym->st_value + reloc->r_addend + sym_map->l_tls_offset;
338             }
339           break;
340
341         case R_AARCH64_IRELATIVE:
342           value = map->l_addr + reloc->r_addend;
343           value = elf_ifunc_invoke (value);
344           *reloc_addr = value;
345           break;
346
347         default:
348           _dl_reloc_bad_type (map, r_type, 0);
349           break;
350         }
351     }
352 }
353
354 inline void
355 __attribute__ ((always_inline))
356 elf_machine_rela_relative (ElfW(Addr) l_addr,
357                            const ElfW(Rela) *reloc,
358                            void *const reloc_addr_arg)
359 {
360   ElfW(Addr) *const reloc_addr = reloc_addr_arg;
361   *reloc_addr = l_addr + reloc->r_addend;
362 }
363
364 inline void
365 __attribute__ ((always_inline))
366 elf_machine_lazy_rel (struct link_map *map,
367                       ElfW(Addr) l_addr,
368                       const ElfW(Rela) *reloc,
369                       int skip_ifunc)
370 {
371   ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
372   const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
373   /* Check for unexpected PLT reloc type.  */
374   if (__builtin_expect (r_type == R_AARCH64_JUMP_SLOT, 1))
375     {
376       if (__builtin_expect (map->l_mach.plt, 0) == 0)
377         *reloc_addr += l_addr;
378       else
379         *reloc_addr = map->l_mach.plt;
380     }
381   else if (__builtin_expect (r_type == R_AARCH64_TLSDESC, 1))
382     {
383       struct tlsdesc volatile *td =
384         (struct tlsdesc volatile *)reloc_addr;
385
386       td->arg = (void*)reloc;
387       td->entry = (void*)(D_PTR (map, l_info[ADDRIDX (DT_TLSDESC_PLT)])
388                           + map->l_addr);
389     }
390   else if (__glibc_unlikely (r_type == R_AARCH64_IRELATIVE))
391     {
392       ElfW(Addr) value = map->l_addr + reloc->r_addend;
393       if (__glibc_likely (!skip_ifunc))
394         value = elf_ifunc_invoke (value);
395       *reloc_addr = value;
396     }
397   else
398     _dl_reloc_bad_type (map, r_type, 1);
399 }
400
401 #endif