]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/arm/dl-machine.h
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / sysdeps / arm / dl-machine.h
CommitLineData
04637865 1/* Machine-dependent ELF dynamic relocation inline functions. ARM version.
6d7e8eda 2 Copyright (C) 1995-2023 Free Software Foundation, Inc.
04637865
UD
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
3214b89b
AJ
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.
04637865
UD
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
3214b89b 13 Lesser General Public License for more details.
04637865 14
3214b89b 15 You should have received a copy of the GNU Lesser General Public
ab84e3ff 16 License along with the GNU C Library. If not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
04637865
UD
18
19#ifndef dl_machine_h
20#define dl_machine_h
21
22#define ELF_MACHINE_NAME "ARM"
23
4af6982e 24#include <assert.h>
04637865 25#include <sys/param.h>
485a9bb9 26#include <tls.h>
3447f0d7 27#include <dl-tlsdesc.h>
73da6bac 28#include <dl-irel.h>
d6d89608
AZ
29#include <dl-static-tls.h>
30#include <dl-machine-rel.h>
04637865 31
5d9eaeec
RM
32#ifndef CLEAR_CACHE
33# error CLEAR_CACHE definition required to handle TEXTREL
34#endif
9d1306dd 35
f1dba308 36/* Return nonzero iff ELF header is compatible with the running host. */
04637865 37static inline int __attribute__ ((unused))
f1dba308 38elf_machine_matches_host (const Elf32_Ehdr *ehdr)
04637865 39{
f1dba308 40 return ehdr->e_machine == EM_ARM;
04637865
UD
41}
42
04637865 43/* Return the run-time load address of the shared object. */
bca0f5cb 44static inline ElfW(Addr) __attribute__ ((unused))
04637865
UD
45elf_machine_load_address (void)
46{
bca0f5cb
FS
47 extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
48 return (ElfW(Addr)) &__ehdr_start;
04637865
UD
49}
50
bca0f5cb
FS
51/* Return the link-time address of _DYNAMIC. */
52static inline ElfW(Addr) __attribute__ ((unused))
53elf_machine_dynamic (void)
54{
55 extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
56 return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
57}
04637865
UD
58
59/* Set up the loaded object described by L so its unrelocated PLT
60 entries will jump to the on-demand fixup code in dl-runtime.c. */
61
62static inline int __attribute__ ((unused))
490e6c62
FS
63elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
64 int lazy, int profile)
04637865
UD
65{
66 Elf32_Addr *got;
67 extern void _dl_runtime_resolve (Elf32_Word);
68 extern void _dl_runtime_profile (Elf32_Word);
69
70 if (l->l_info[DT_JMPREL] && lazy)
71 {
72 /* patb: this is different than i386 */
73 /* The GOT entries for functions in the PLT have not yet been filled
74 in. Their initial contents will arrange when called to push an
75 index into the .got section, load ip with &_GLOBAL_OFFSET_TABLE_[3],
76 and then jump to _GLOBAL_OFFSET_TABLE[2]. */
7189e3b8 77 got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
bcb5a520
UD
78 /* If a library is prelinked but we have to relocate anyway,
79 we have to be able to undo the prelinking of .got.plt.
80 The prelinker saved us here address of .plt. */
81 if (got[1])
82 l->l_mach.plt = got[1] + l->l_addr;
04637865
UD
83 got[1] = (Elf32_Addr) l; /* Identify this shared object. */
84
85 /* The got[2] entry contains the address of a function which gets
86 called to get the address of a so far unresolved function and
87 jump to it. The profiling extension of the dynamic linker allows
88 to intercept the calls to collect information. In this case we
89 don't store the address in the GOT so that all future calls also
90 end in this function. */
91 if (profile)
92 {
344b4b4e 93 got[2] = (Elf32_Addr) &_dl_runtime_profile;
17f56d2f 94
849e84dd
PB
95 if (GLRO(dl_profile) != NULL
96 && _dl_name_match_p (GLRO(dl_profile), l))
17f56d2f
UD
97 /* Say that we really want profiling and the timers are
98 started. */
f71d7f57 99 GL(dl_profile_map) = l;
04637865
UD
100 }
101 else
102 /* This function will get called to fix up the GOT entry indicated by
103 the offset on the stack, and then jump to the resolved address. */
104 got[2] = (Elf32_Addr) &_dl_runtime_resolve;
105 }
3447f0d7 106
04637865
UD
107 return lazy;
108}
109
3377126b 110#if defined(ARCH_HAS_BX)
12a15026 111#define BX(x) "bx\t" #x
04637865 112#else
12a15026 113#define BX(x) "mov\tpc, " #x
04637865
UD
114#endif
115
04637865
UD
116/* Mask identifying addresses reserved for the user program,
117 where the dynamic linker should not map anything. */
118#define ELF_MACHINE_USER_ADDRESS_MASK 0xf8000000UL
119
120/* Initial entry point code for the dynamic linker.
121 The C function `_dl_start' is the real entry point;
122 its return value is the user program's entry point. */
123
124#define RTLD_START asm ("\
bbb3856d
UD
125.text\n\
126.globl _start\n\
5631abde 127.type _start, %function\n\
bbb3856d 128.globl _dl_start_user\n\
5631abde 129.type _dl_start_user, %function\n\
bbb3856d 130_start:\n\
08b55be5
RM
131 @ we are PIC code, so get global offset table\n\
132 ldr sl, .L_GET_GOT\n\
bbb3856d
UD
133 @ at start time, all the args are on the stack\n\
134 mov r0, sp\n\
135 bl _dl_start\n\
136 @ returns user entry point in r0\n\
137_dl_start_user:\n\
5631abde
JM
138 adr r6, .L_GET_GOT\n\
139 add sl, sl, r6\n\
bbb3856d 140 ldr r4, [sl, r4]\n\
08b55be5
RM
141 @ save the entry point in another register\n\
142 mov r6, r0\n\
f10eff58
DJ
143 @ get the original arg count\n\
144 ldr r1, [sp]\n\
bbb3856d
UD
145 @ get the argv address\n\
146 add r2, sp, #4\n\
bbb3856d
UD
147 @ compute envp\n\
148 add r3, r2, r1, lsl #2\n\
149 add r3, r3, #4\n\
bbb3856d
UD
150 @ now we call _dl_init\n\
151 ldr r0, .L_LOADED\n\
152 ldr r0, [sl, r0]\n\
bbb3856d 153 @ call _dl_init\n\
c5684fdb 154 bl _dl_init(PLT)\n\
bbb3856d
UD
155 @ load the finalizer function\n\
156 ldr r0, .L_FINI_PROC\n\
08b55be5 157 add r0, sl, r0\n\
bbb3856d 158 @ jump to the user_s entry point\n\
47f0752a 159 " BX(r6) "\n\
f10eff58 160\n\
bbb3856d 161.L_GET_GOT:\n\
5631abde 162 .word _GLOBAL_OFFSET_TABLE_ - .L_GET_GOT\n\
bbb3856d 163.L_FINI_PROC:\n\
08b55be5 164 .word _dl_fini(GOTOFF)\n\
f10eff58 165.L_ARGV:\n\
d6dea8c8 166 .word __GI__dl_argv(GOTOFF)\n\
bbb3856d 167.L_LOADED:\n\
08b55be5 168 .word _rtld_local(GOTOFF)\n\
04637865
UD
169.previous\n\
170");
171
485a9bb9
DJ
172/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
173 TLS variable, so undefined references should not be allowed to
174 define the value.
209826bc 175 ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one
a7629b1c 176 of the main executable's symbols, as for a COPY reloc. */
6dcecc64
JM
177#ifndef RTLD_BOOTSTRAP
178# define elf_machine_type_class(type) \
485a9bb9 179 ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32 \
3447f0d7
NS
180 || (type) == R_ARM_TLS_DTPOFF32 || (type) == R_ARM_TLS_TPOFF32 \
181 || (type) == R_ARM_TLS_DESC) \
485a9bb9 182 * ELF_RTYPE_CLASS_PLT) \
a7629b1c 183 | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY))
6dcecc64
JM
184#else
185#define elf_machine_type_class(type) \
186 ((((type) == R_ARM_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT) \
a7629b1c 187 | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY))
6dcecc64 188#endif
04637865
UD
189
190/* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
70217455 191#define ELF_MACHINE_JMP_SLOT R_ARM_JUMP_SLOT
04637865 192
04637865
UD
193/* We define an initialization functions. This is called very early in
194 _dl_sysdep_start. */
195#define DL_PLATFORM_INIT dl_platform_init ()
196
04637865
UD
197static inline void __attribute__ ((unused))
198dl_platform_init (void)
199{
cebbd6e7 200 if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
9b1eef96 201 /* Avoid an empty string which would disturb us. */
cebbd6e7 202 GLRO(dl_platform) = NULL;
04637865
UD
203}
204
408c9db5
UD
205static inline Elf32_Addr
206elf_machine_fixup_plt (struct link_map *map, lookup_t t,
0572433b 207 const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
408c9db5 208 const Elf32_Rel *reloc,
04637865
UD
209 Elf32_Addr *reloc_addr, Elf32_Addr value)
210{
408c9db5 211 return *reloc_addr = value;
04637865
UD
212}
213
214/* Return the final value of a plt relocation. */
215static inline Elf32_Addr
216elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc,
217 Elf32_Addr value)
218{
219 return value;
220}
221
222#endif /* !dl_machine_h */
223
04637865 224
867700cb
DJ
225/* Names of the architecture-specific auditing callback functions. */
226#define ARCH_LA_PLTENTER arm_gnu_pltenter
227#define ARCH_LA_PLTEXIT arm_gnu_pltexit
228
849e84dd 229#ifdef RESOLVE_MAP
453b88ef
AZ
230/* Set NEW_VALUE based on V, and return true iff it overflows 24 bits. */
231static inline bool set_new_value (Elf32_Addr *new_value, Elf32_Addr v,
232 Elf32_Addr *const reloc_addr,
233 Elf32_Sword addend)
234{
235 *new_value = v + addend - (Elf32_Addr) reloc_addr;
236 Elf32_Addr topbits = *new_value & 0xfe000000;
237 return topbits != 0xfe000000 && topbits != 0x00000000;
238}
239
fb228a2d 240/* Handle a PC24 reloc, including the out-of-range case. */
490e6c62 241static void
fb228a2d
RM
242relocate_pc24 (struct link_map *map, Elf32_Addr value,
243 Elf32_Addr *const reloc_addr, Elf32_Sword addend)
a27fd430 244{
fb228a2d
RM
245 Elf32_Addr new_value;
246
453b88ef 247 if (set_new_value (&new_value, value, reloc_addr, addend))
a27fd430 248 {
fb228a2d
RM
249 /* The PC-relative address doesn't fit in 24 bits! */
250
251 static void *fix_page;
252 static size_t fix_offset;
253 if (fix_page == NULL)
254 {
255 void *new_page = __mmap (NULL, GLRO(dl_pagesize),
256 PROT_READ | PROT_WRITE | PROT_EXEC,
257 MAP_PRIVATE | MAP_ANON, -1, 0);
258 if (new_page == MAP_FAILED)
259 _dl_signal_error (0, map->l_name, NULL,
260 "could not map page for fixup");
261 fix_page = new_page;
262 assert (fix_offset == 0);
263 }
264
265 Elf32_Word *fix_address = fix_page + fix_offset;
266 fix_address[0] = 0xe51ff004; /* ldr pc, [pc, #-4] */
267 fix_address[1] = value;
268
269 fix_offset += sizeof fix_address[0] * 2;
270 if (fix_offset >= GLRO(dl_pagesize))
271 {
272 fix_page = NULL;
273 fix_offset = 0;
274 }
275
453b88ef
AZ
276 if (set_new_value (&new_value, (Elf32_Addr) fix_address, reloc_addr,
277 addend))
fb228a2d
RM
278 _dl_signal_error (0, map->l_name, NULL,
279 "R_ARM_PC24 relocation out of range");
a27fd430
UD
280 }
281
fb228a2d 282 *reloc_addr = (*reloc_addr & 0xff000000) | ((new_value >> 2) & 0x00ffffff);
a27fd430
UD
283}
284
04637865
UD
285/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
286 MAP is the object containing the reloc. */
287
490e6c62 288static inline void
dda081dd 289__attribute__ ((always_inline))
490e6c62
FS
290elf_machine_rel (struct link_map *map, struct r_scope_elem *scope[],
291 const Elf32_Rel *reloc, const Elf32_Sym *sym,
292 const struct r_found_version *version,
0507f293 293 void *const reloc_addr_arg, int skip_ifunc)
04637865 294{
5027ae10 295 Elf32_Addr *const reloc_addr = reloc_addr_arg;
85bdccdb
UD
296 const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
297
3ee318c9 298#if !defined RTLD_BOOTSTRAP
85bdccdb 299 if (__builtin_expect (r_type == R_ARM_RELATIVE, 0))
3ee318c9 300 *reloc_addr += map->l_addr;
ceb7d0bb 301# ifndef RTLD_BOOTSTRAP
85bdccdb
UD
302 else if (__builtin_expect (r_type == R_ARM_NONE, 0))
303 return;
ceb7d0bb 304# endif
85bdccdb 305 else
2aac58fc 306#endif
04637865
UD
307 {
308 const Elf32_Sym *const refsym = sym;
490e6c62
FS
309 struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
310 r_type);
10a446dd 311 Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
04637865 312
70063199 313 if (sym != NULL
0507f293
AS
314 && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
315 && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1)
316 && __builtin_expect (!skip_ifunc, 1))
73da6bac 317 value = elf_ifunc_invoke (value);
70063199 318
85bdccdb 319 switch (r_type)
04637865
UD
320 {
321 case R_ARM_COPY:
322 if (sym == NULL)
323 /* This can happen in trace mode if an object could not be
324 found. */
325 break;
326 if (sym->st_size > refsym->st_size
cebbd6e7 327 || (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
04637865
UD
328 {
329 const char *strtab;
330
7189e3b8 331 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
773b6400
UD
332 _dl_error_printf ("\
333%s: Symbol `%s' has different size in shared object, consider re-linking\n",
b9375348 334 RTLD_PROGNAME, strtab + refsym->st_name);
04637865 335 }
5027ae10
AO
336 memcpy (reloc_addr_arg, (void *) value,
337 MIN (sym->st_size, refsym->st_size));
04637865
UD
338 break;
339 case R_ARM_GLOB_DAT:
70217455 340 case R_ARM_JUMP_SLOT:
ceb7d0bb 341# ifdef RTLD_BOOTSTRAP
e7782e5e 342 /* Fix weak undefined references. */
44bff568 343 if (sym != NULL && sym->st_value == 0)
e7782e5e
UD
344 *reloc_addr = 0;
345 else
ceb7d0bb 346# endif
e7782e5e 347 *reloc_addr = value;
04637865 348 break;
70217455 349 case R_ARM_ABS32:
04637865 350 {
3447612d
MF
351 struct unaligned
352 {
353 Elf32_Addr x;
9770df4f 354 } __attribute__ ((packed, may_alias));
ceb7d0bb 355# ifndef RTLD_BOOTSTRAP
04637865
UD
356 /* This is defined in rtld.c, but nowhere in the static
357 libc.a; make the reference weak so static programs can
358 still link. This declaration cannot be done when
359 compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP) because
360 rtld.c contains the common defn for _dl_rtld_map, which
361 is incompatible with a weak decl in the same file. */
ceb7d0bb 362# ifndef SHARED
04637865 363 weak_extern (_dl_rtld_map);
ceb7d0bb 364# endif
2aac58fc 365 if (map == &GL(dl_rtld_map))
04637865
UD
366 /* Undo the relocation done here during bootstrapping.
367 Now we will relocate it anew, possibly using a
368 binding found in the user program or a loaded library
369 rather than the dynamic linker's built-in definitions
370 used while loading those libraries. */
10a446dd 371 value -= SYMBOL_ADDRESS (map, refsym, true);
ceb7d0bb 372# endif
3447612d
MF
373 /* Support relocations on mis-aligned offsets. */
374 ((struct unaligned *) reloc_addr)->x += value;
04637865
UD
375 break;
376 }
3447f0d7
NS
377 case R_ARM_TLS_DESC:
378 {
28e1ddf3 379 struct tlsdesc *td = (struct tlsdesc *)reloc_addr;
3447f0d7
NS
380
381# ifndef RTLD_BOOTSTRAP
382 if (! sym)
383 td->entry = _dl_tlsdesc_undefweak;
384 else
385# endif
386 {
90e5dd48
MR
387 if (ELF32_R_SYM (reloc->r_info) == STN_UNDEF)
388 value = td->argument.value;
389 else
390 value = sym->st_value;
3447f0d7
NS
391
392# ifndef RTLD_BOOTSTRAP
393# ifndef SHARED
394 CHECK_STATIC_TLS (map, sym_map);
395# else
396 if (!TRY_STATIC_TLS (map, sym_map))
397 {
398 td->argument.pointer
399 = _dl_make_tlsdesc_dynamic (sym_map, value);
400 td->entry = _dl_tlsdesc_dynamic;
401 }
402 else
403# endif
404# endif
0507f293 405 {
3447f0d7
NS
406 td->argument.value = value + sym_map->l_tls_offset;
407 td->entry = _dl_tlsdesc_return;
0507f293 408 }
3447f0d7
NS
409 }
410 }
411 break;
d1af493c 412 case R_ARM_PC24:
fb228a2d
RM
413 relocate_pc24 (map, value, reloc_addr,
414 /* Sign-extend the 24-bit addend in the
415 instruction (which counts instructions), and
416 then shift it up two so as to count bytes. */
417 (((Elf32_Sword) *reloc_addr << 8) >> 8) << 2);
485a9bb9 418 break;
70063199 419#if !defined RTLD_BOOTSTRAP
485a9bb9
DJ
420 case R_ARM_TLS_DTPMOD32:
421 /* Get the information from the link map returned by the
422 resolv function. */
423 if (sym_map != NULL)
424 *reloc_addr = sym_map->l_tls_modid;
425 break;
426
427 case R_ARM_TLS_DTPOFF32:
56865130
DJ
428 if (sym != NULL)
429 *reloc_addr += sym->st_value;
485a9bb9
DJ
430 break;
431
432 case R_ARM_TLS_TPOFF32:
56865130
DJ
433 if (sym != NULL)
434 {
435 CHECK_STATIC_TLS (map, sym_map);
436 *reloc_addr += sym->st_value + sym_map->l_tls_offset;
437 }
485a9bb9 438 break;
70063199
RS
439 case R_ARM_IRELATIVE:
440 value = map->l_addr + *reloc_addr;
4db71d2f
FW
441 if (__glibc_likely (!skip_ifunc))
442 value = ((Elf32_Addr (*) (int)) value) (GLRO(dl_hwcap));
70063199
RS
443 *reloc_addr = value;
444 break;
485a9bb9 445#endif
04637865 446 default:
85bdccdb 447 _dl_reloc_bad_type (map, r_type, 0);
04637865
UD
448 break;
449 }
450 }
451}
452
bcb5a520 453
490e6c62 454static inline void
dda081dd 455__attribute__ ((always_inline))
85bdccdb 456elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc,
5027ae10 457 void *const reloc_addr_arg)
85bdccdb 458{
5027ae10 459 Elf32_Addr *const reloc_addr = reloc_addr_arg;
85bdccdb
UD
460 *reloc_addr += l_addr;
461}
462
bcb5a520 463
490e6c62 464static inline void
dda081dd 465__attribute__ ((always_inline))
490e6c62 466elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
0507f293
AS
467 Elf32_Addr l_addr, const Elf32_Rel *reloc,
468 int skip_ifunc)
04637865 469{
68d11b26 470 Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
85bdccdb 471 const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
344b4b4e 472 /* Check for unexpected PLT reloc type. */
85bdccdb 473 if (__builtin_expect (r_type == R_ARM_JUMP_SLOT, 1))
bcb5a520
UD
474 {
475 if (__builtin_expect (map->l_mach.plt, 0) == 0)
476 *reloc_addr += l_addr;
477 else
478 *reloc_addr = map->l_mach.plt;
479 }
3447f0d7
NS
480 else if (__builtin_expect (r_type == R_ARM_TLS_DESC, 1))
481 {
0ca3d1d6
SN
482 const Elf_Symndx symndx = ELFW (R_SYM) (reloc->r_info);
483 const ElfW (Sym) *symtab = (const void *)D_PTR (map, l_info[DT_SYMTAB]);
484 const ElfW (Sym) *sym = &symtab[symndx];
485 const struct r_found_version *version = NULL;
486
487 if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
488 {
489 const ElfW (Half) *vernum =
490 (const void *)D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
491 version = &map->l_versions[vernum[symndx] & 0x7fff];
492 }
493
494 /* Always initialize TLS descriptors completely, because lazy
495 initialization requires synchronization at every TLS access. */
490e6c62 496 elf_machine_rel (map, scope, reloc, sym, version, reloc_addr, skip_ifunc);
3447f0d7 497 }
893f3d10 498 else
85bdccdb 499 _dl_reloc_bad_type (map, r_type, 1);
04637865
UD
500}
501
849e84dd 502#endif /* RESOLVE_MAP */