diff options
author | Christophe Lyon <christophe.lyon@st.com> | 2013-01-18 15:08:04 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbrodkorb@conet.de> | 2018-08-10 16:01:58 +0200 |
commit | 5ec586eb37f61e57f19ac60b58af61f167ca054a (patch) | |
tree | 6c72d03d43e76a33eb5d6360d9bdb2573a048c42 | |
parent | f88bb681ba22d77a19abfb4c1617c826298ed473 (diff) |
rtld: Add FDPIC code for arm
Add FDPIC dynamic relocations support, similar to what other FDPIC
targets do.
Lazy binding is implemented in a folllow-up patch.
Disable the SEND* macros because they involve relocations to
access constant strings that are unsupported by the existing
arm version.
Define DL_START, START, ARCH_NEEDS_BOOTSTRAP_RELOCS,
DL_CHECK_LIB_TYPE similarly to what other FDPIC targets do.
Define raise() because _dl_find_hash references __aeabi_uidivmod,
which uses __aeabi_idiv0 which in turn references raise.
* include/elf.h (R_ARM_FUNCDESC): Define.
(R_ARM_FUNCDESC_VALUE): Define.
* ldso/include/dl-string.h (SEND_STDERR, SEND_ADDRESS_STDERR)
(SEND_NUMBER_STDERR): Define empty for __FDPIC__.
* ldso/ldso/arm/dl-inlines.h: New file.
* ldso/ldso/arm/dl-startup.h (PERFORM_BOOTSTRAP_RELOC): Fix type
of load_addr. Fix handling of R_ARM_RELATIVE, add support for
R_ARM_FUNCDESC_VALUE.
(DL_START, START): Define for __FDPIC__.
(raise): Define.
* ldso/ldso/arm/dl-sysdep.h (ARCH_NEEDS_BOOTSTRAP_RELOCS): Define.
(DL_CHECK_LIB_TYPE): Define.
(elf_machine_type_class): Take into account FDPIC related
relocations.
(elf_machine_load_address): Support __FDPIC__.
(elf_machine_relative): Likewise.
* ldso/ldso/arm/elfinterp.c (_dl_linux_resolver): Dummy support
for __FDPIC__, implemented in a later patch.
(_dl_do_reloc): Fix reloc_adr computation for __FDPIC__, fix
handling of local symbols. Fix handling of R_ARM_RELATIVE, add
support for R_ARM_FUNCDESC_VALUE, R_ARM_FUNCDESC.
* ldso/ldso/arm/resolve.S: Make _dl_linux_resolve hidden.
* ldso/ldso/fdpic/dl-inlines.h (htab_delete): Declare.
* libc/sysdeps/linux/arm/bits/elf-fdpic.h: New file, similar to bfin's.
* libc/sysdeps/linux/arm/crtreloc.c: Likewise.
* libc/sysdeps/linux/arm/find_exidx.c (__dl_addr_in_loadaddr) Define.
(find_exidx_callback): Support __FDPIC__.
Signed-off-by: Mickaël Guêné <mickael.guene@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@st.com>
-rw-r--r-- | include/elf.h | 2 | ||||
-rw-r--r-- | ldso/include/dl-string.h | 3 | ||||
-rw-r--r-- | ldso/ldso/arm/dl-inlines.h | 1 | ||||
-rw-r--r-- | ldso/ldso/arm/dl-startup.h | 47 | ||||
-rw-r--r-- | ldso/ldso/arm/dl-sysdep.h | 67 | ||||
-rw-r--r-- | ldso/ldso/arm/elfinterp.c | 85 | ||||
-rw-r--r-- | ldso/ldso/arm/resolve.S | 1 | ||||
-rw-r--r-- | ldso/ldso/fdpic/dl-inlines.h | 2 | ||||
-rw-r--r-- | libc/sysdeps/linux/arm/bits/elf-fdpic.h | 114 | ||||
-rw-r--r-- | libc/sysdeps/linux/arm/crtreloc.c | 144 | ||||
-rw-r--r-- | libc/sysdeps/linux/arm/find_exidx.c | 38 |
11 files changed, 474 insertions, 30 deletions
diff --git a/include/elf.h b/include/elf.h index abeab6e40..a9957fc31 100644 --- a/include/elf.h +++ b/include/elf.h @@ -2705,6 +2705,8 @@ typedef Elf32_Addr Elf32_Conflict; #define R_ARM_TLS_LDO12 109 #define R_ARM_TLS_LE12 110 #define R_ARM_TLS_IE12GP 111 +#define R_ARM_FUNCDESC 163 +#define R_ARM_FUNCDESC_VALUE 164 #define R_ARM_RXPC25 249 #define R_ARM_RSBREL32 250 #define R_ARM_THM_RPC22 251 diff --git a/ldso/include/dl-string.h b/ldso/include/dl-string.h index fc1d1fccd..bf6997188 100644 --- a/ldso/include/dl-string.h +++ b/ldso/include/dl-string.h @@ -256,7 +256,8 @@ static __always_inline char * _dl_simple_ltoahex(char *local, unsigned long i) /* On some (wierd) arches, none of this stuff works at all, so * disable the whole lot... */ -#if defined(__mips__) +/* The same applies for ARM FDPIC at least for the moment. */ +#if defined(__mips__) || (__FDPIC__) # define SEND_STDERR(X) # define SEND_ADDRESS_STDERR(X, add_a_newline) diff --git a/ldso/ldso/arm/dl-inlines.h b/ldso/ldso/arm/dl-inlines.h new file mode 100644 index 000000000..8fdf6eb48 --- /dev/null +++ b/ldso/ldso/arm/dl-inlines.h @@ -0,0 +1 @@ +#include "../fdpic/dl-inlines.h" diff --git a/ldso/ldso/arm/dl-startup.h b/ldso/ldso/arm/dl-startup.h index 371dc2293..ea1e9f6f9 100644 --- a/ldso/ldso/arm/dl-startup.h +++ b/ldso/ldso/arm/dl-startup.h @@ -131,7 +131,7 @@ __asm__( /* Handle relocation of the symbols in the dynamic loader. */ static __always_inline void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr, - unsigned long symbol_addr, unsigned long load_addr, Elf32_Sym *symtab) + unsigned long symbol_addr, DL_LOADADDR_TYPE load_addr, Elf32_Sym *symtab) { switch (ELF_R_TYPE(rpnt->r_info)) { case R_ARM_NONE: @@ -176,12 +176,55 @@ void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr, *reloc_addr = symbol_addr; break; case R_ARM_RELATIVE: - *reloc_addr += load_addr; + *reloc_addr = DL_RELOC_ADDR(load_addr, *reloc_addr); break; case R_ARM_COPY: break; +#ifdef __FDPIC__ + case R_ARM_FUNCDESC_VALUE: + { + struct funcdesc_value *dst = (struct funcdesc_value *) reloc_addr; + + dst->entry_point += symbol_addr; + dst->got_value = load_addr.got_value; + } + break; +#endif default: SEND_STDERR("Unsupported relocation type\n"); _dl_exit(1); } } + +#ifdef __FDPIC__ +#undef DL_START +#define DL_START(X) \ +static void __attribute__ ((used)) \ +_dl_start (Elf32_Addr dl_boot_got_pointer, \ + struct elf32_fdpic_loadmap *dl_boot_progmap, \ + struct elf32_fdpic_loadmap *dl_boot_ldsomap, \ + Elf32_Dyn *dl_boot_ldso_dyn_pointer, \ + struct funcdesc_value *dl_main_funcdesc, \ + X) + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. We return the address of the function's entry point to + * _dl_boot, see boot1_arch.h. + */ +#define START() do { \ + struct elf_resolve *exec_mod = _dl_loaded_modules; \ + dl_main_funcdesc->entry_point = _dl_elf_main; \ + while (exec_mod->libtype != elf_executable) \ + exec_mod = exec_mod->next; \ + dl_main_funcdesc->got_value = exec_mod->loadaddr.got_value; \ + return; \ +} while (0) + +/* We use __aeabi_idiv0 in _dl_find_hash, so we need to have the raise + symbol. */ +int raise(int sig) +{ + _dl_exit(1); +} +#endif /* __FDPIC__ */ diff --git a/ldso/ldso/arm/dl-sysdep.h b/ldso/ldso/arm/dl-sysdep.h index a47a55213..0f783e1c4 100644 --- a/ldso/ldso/arm/dl-sysdep.h +++ b/ldso/ldso/arm/dl-sysdep.h @@ -10,6 +10,19 @@ /* Define this if the system uses RELOCA. */ #undef ELF_USES_RELOCA #include <elf.h> + +#ifdef __FDPIC__ +/* Need bootstrap relocations */ +#define ARCH_NEEDS_BOOTSTRAP_RELOCS + +#define DL_CHECK_LIB_TYPE(epnt, piclib, _dl_progname, libname) \ +do \ +{ \ + (piclib) = 2; \ +} \ +while (0) +#endif /* __FDPIC__ */ + /* Initialization sequence for the GOT. */ #define INIT_GOT(GOT_BASE,MODULE) \ { \ @@ -63,11 +76,25 @@ unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one of the main executable's symbols, as for a COPY reloc. */ + +#ifdef __FDPIC__ +/* Avoid R_ARM_ABS32 to go through the PLT so that R_ARM_TARGET1 + translated to R_ARM_ABS32 doesn't use the PLT: otherwise, this + breaks init_array because functions are referenced through the + PLT. */ +#define elf_machine_type_class(type) \ + ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32 \ + || (type) == R_ARM_FUNCDESC_VALUE || (type) == R_ARM_FUNCDESC || (type) == R_ARM_ABS32 \ + || (type) == R_ARM_TLS_DTPOFF32 || (type) == R_ARM_TLS_TPOFF32) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY)) +#else #define elf_machine_type_class(type) \ ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32 \ || (type) == R_ARM_TLS_DTPOFF32 || (type) == R_ARM_TLS_TPOFF32) \ * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY)) +#endif /* __FDPIC__ */ /* Return the link-time address of _DYNAMIC. Conveniently, this is the first element of the GOT. We used to use the PIC register to do this @@ -106,10 +133,24 @@ elf_machine_dynamic (void) extern char __dl_start[] __asm__("_dl_start"); +#ifdef __FDPIC__ +/* We must force strings used early in the bootstrap into the data + segment. */ +#undef SEND_EARLY_STDERR +#define SEND_EARLY_STDERR(S) \ + do { /* FIXME: implement */; } while (0) + +#undef INIT_GOT +#include "../fdpic/dl-sysdep.h" +#endif /* __FDPIC__ */ + /* Return the run-time load address of the shared object. */ static __always_inline Elf32_Addr __attribute__ ((unused)) elf_machine_load_address (void) { +#if defined(__FDPIC__) + return 0; +#else Elf32_Addr got_addr = (Elf32_Addr) &__dl_start; Elf32_Addr pcrel_addr; #if defined __OPTIMIZE__ && !defined __thumb__ @@ -128,19 +169,33 @@ elf_machine_load_address (void) : "=r" (pcrel_addr), "=r" (tmp)); #endif return pcrel_addr - got_addr; +#endif } static __always_inline void +#ifdef __FDPIC__ +elf_machine_relative (DL_LOADADDR_TYPE load_off, const Elf32_Addr rel_addr, +#else elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, +#endif Elf32_Word relative_count) { - Elf32_Rel * rpnt = (void *) rel_addr; - --rpnt; - do { - Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); +#if defined(__FDPIC__) + Elf32_Rel *rpnt = (void *) rel_addr; + + do { + unsigned long *reloc_addr = (unsigned long *) DL_RELOC_ADDR(load_off, rpnt->r_offset); - *reloc_addr += load_off; - } while (--relative_count); + *reloc_addr = DL_RELOC_ADDR(load_off, *reloc_addr); + rpnt++; +#else + Elf32_Rel * rpnt = (void *) rel_addr; + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + *reloc_addr += load_off; +#endif + } while(--relative_count); } #endif /* !_ARCH_DL_SYSDEP */ diff --git a/ldso/ldso/arm/elfinterp.c b/ldso/ldso/arm/elfinterp.c index 96809a9ae..402ba9618 100644 --- a/ldso/ldso/arm/elfinterp.c +++ b/ldso/ldso/arm/elfinterp.c @@ -36,6 +36,11 @@ extern int _dl_linux_resolve(void); unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) { +#if __FDPIC__ + /* FIXME: implement. */ + while(1) ; + return 0; +#else ELF_RELOC *this_reloc; char *strtab; char *symname; @@ -88,6 +93,7 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) #endif return new_addr; +#endif } static int @@ -181,7 +187,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope, struct elf_resolve *def_mod = 0; int goof = 0; - reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); + reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset); reloc_type = ELF_R_TYPE(rpnt->r_info); symtab_index = ELF_R_SYM(rpnt->r_info); @@ -191,25 +197,30 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope, symname = strtab + symtab[symtab_index].st_name; if (symtab_index) { - symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt, - elf_machine_type_class(reloc_type), &sym_ref); - - /* - * We want to allow undefined references to weak symbols - this might - * have been intentional. We should not be linking local symbols - * here, so all bases should be covered. - */ - if (!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS) - && (ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) { - /* This may be non-fatal if called from dlopen. */ - return 1; - - } - if (_dl_trace_prelink) { - _dl_debug_lookup (symname, tpnt, &symtab[symtab_index], - &sym_ref, elf_machine_type_class(reloc_type)); + if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) { + symbol_addr = (unsigned long) DL_RELOC_ADDR(tpnt->loadaddr, symtab[symtab_index].st_value); + def_mod = tpnt; + } else { + symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt, + elf_machine_type_class(reloc_type), &sym_ref); + + /* + * We want to allow undefined references to weak symbols - this might + * have been intentional. We should not be linking local symbols + * here, so all bases should be covered. + */ + if (!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS) + && (ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) { + /* This may be non-fatal if called from dlopen. */ + return 1; + + } + if (_dl_trace_prelink) { + _dl_debug_lookup (symname, tpnt, &symtab[symtab_index], + &sym_ref, elf_machine_type_class(reloc_type)); + } + def_mod = sym_ref.tpnt; } - def_mod = sym_ref.tpnt; } else { /* * Relocs against STN_UNDEF are usually treated as using a @@ -267,12 +278,42 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope, *reloc_addr = symbol_addr; break; case R_ARM_RELATIVE: - *reloc_addr += (unsigned long) tpnt->loadaddr; + *reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, *reloc_addr); break; case R_ARM_COPY: _dl_memcpy((void *) reloc_addr, (void *) symbol_addr, symtab[symtab_index].st_size); break; +#ifdef __FDPIC__ + case R_ARM_FUNCDESC_VALUE: + { + struct funcdesc_value funcval; + struct funcdesc_value *dst = (struct funcdesc_value *) reloc_addr; + + funcval.entry_point = (void*)symbol_addr; + /* Add offset to section address for local symbols. */ + if (ELF_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL) + funcval.entry_point += *reloc_addr; + funcval.got_value = def_mod->loadaddr.got_value; + *dst = funcval; + } + break; + case R_ARM_FUNCDESC: + { + unsigned long reloc_value = *reloc_addr; + + if (symbol_addr) + reloc_value = (unsigned long) _dl_funcdesc_for(symbol_addr + reloc_value, sym_ref.tpnt->loadaddr.got_value); + else + /* Relocation against an + undefined weak symbol: + set funcdesc to zero. */ + reloc_value = 0; + + *reloc_addr = reloc_value; + } + break; +#endif #if defined USE_TLS && USE_TLS case R_ARM_TLS_DTPMOD32: *reloc_addr = def_mod->l_tls_modid; @@ -330,7 +371,6 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, #endif return 0; - } void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, @@ -345,3 +385,6 @@ int _dl_parse_relocation_information(struct dyn_elf *rpnt, return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc); } +#ifndef IS_IN_libdl +# include "../../libc/sysdeps/linux/arm/crtreloc.c" +#endif diff --git a/ldso/ldso/arm/resolve.S b/ldso/ldso/arm/resolve.S index 7e0058e0d..2a516436e 100644 --- a/ldso/ldso/arm/resolve.S +++ b/ldso/ldso/arm/resolve.S @@ -102,6 +102,7 @@ .align 4 @ 16 byte boundary and there are 32 bytes below (arm case) #if 1 /*(!defined(__thumb__) || defined __THUMB_INTERWORK__) || defined(__thumb2__)*/ .arm + .hidden _dl_linux_resolve .globl _dl_linux_resolve .type _dl_linux_resolve,%function .align 4; diff --git a/ldso/ldso/fdpic/dl-inlines.h b/ldso/ldso/fdpic/dl-inlines.h index f59087568..89e7a9a68 100644 --- a/ldso/ldso/fdpic/dl-inlines.h +++ b/ldso/ldso/fdpic/dl-inlines.h @@ -7,6 +7,8 @@ #include <inline-hashtab.h> +static __always_inline void htab_delete(struct funcdesc_ht *htab); + /* Initialize a DL_LOADADDR_TYPE given a got pointer and a complete load map. */ static __always_inline void __dl_init_loadaddr_map(struct elf32_fdpic_loadaddr *loadaddr, Elf32_Addr dl_boot_got_pointer, diff --git a/libc/sysdeps/linux/arm/bits/elf-fdpic.h b/libc/sysdeps/linux/arm/bits/elf-fdpic.h new file mode 100644 index 000000000..3d6db54af --- /dev/null +++ b/libc/sysdeps/linux/arm/bits/elf-fdpic.h @@ -0,0 +1,114 @@ +/* Copyright 2003, 2004 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +In addition to the permissions in the GNU Lesser General Public +License, the Free Software Foundation gives you unlimited +permission to link the compiled version of this file with other +programs, and to distribute those programs without any restriction +coming from the use of this file. (The GNU Lesser General Public +License restrictions do apply in other respects; for example, they +cover modification of the file, and distribution when not linked +into another program.) + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _BITS_ELF_FDPIC_H +#define _BITS_ELF_FDPIC_H + +/* These data structures are described in the FDPIC ABI extension. + The kernel passes a process a memory map, such that for every LOAD + segment there is an elf32_fdpic_loadseg entry. A pointer to an + elf32_fdpic_loadmap is passed in r7 at start-up, and a pointer to + an additional such map is passed in r8 for the interpreter, when + there is one. */ + +#include <elf.h> + +/* This data structure represents a PT_LOAD segment. */ +struct elf32_fdpic_loadseg +{ + /* Core address to which the segment is mapped. */ + Elf32_Addr addr; + /* VMA recorded in the program header. */ + Elf32_Addr p_vaddr; + /* Size of this segment in memory. */ + Elf32_Word p_memsz; +}; + +struct elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + Elf32_Half version; + /* Number of segments in this map. */ + Elf32_Half nsegs; + /* The actual memory map. */ + struct elf32_fdpic_loadseg segs[/*nsegs*/]; +}; + +struct elf32_fdpic_loadaddr { + struct elf32_fdpic_loadmap *map; + void *got_value; +}; + +/* Map a pointer's VMA to its corresponding address according to the + load map. */ +static __always_inline void * +__reloc_pointer (void *p, + const struct elf32_fdpic_loadmap *map) +{ + int c; + +#if 0 + if (map->version != 0) + /* Crash. */ + ((void(*)())0)(); +#endif + + /* No special provision is made for NULL. We don't want NULL + addresses to go through relocation, so they shouldn't be in + .rofixup sections, and, if they're present in dynamic + relocations, they shall be mapped to the NULL address without + undergoing relocations. */ + + for (c = 0; + /* Take advantage of the fact that the loadmap is ordered by + virtual addresses. In general there will only be 2 entries, + so it's not profitable to do a binary search. */ + c < map->nsegs && p >= (void*)map->segs[c].p_vaddr; + c++) + { + /* This should be computed as part of the pointer comparison + above, but we want to use the carry in the comparison, so we + can't convert it to an integer type beforehand. */ + unsigned long offset = p - (void*)map->segs[c].p_vaddr; + /* We only check for one-past-the-end for the last segment, + assumed to be the data segment, because other cases are + ambiguous in the absence of padding between segments, and + rofixup already serves as padding between text and data. + Unfortunately, unless we special-case the last segment, we + fail to relocate the _end symbol. */ + if (offset < map->segs[c].p_memsz + || (offset == map->segs[c].p_memsz && c + 1 == map->nsegs)) + return (char*)map->segs[c].addr + offset; + } + + /* We might want to crash instead. */ + return (void*)-1; +} + +# define __RELOC_POINTER(ptr, loadaddr) \ + (__reloc_pointer ((void*)(ptr), \ + (loadaddr).map)) + +#endif /* _BITS_ELF_FDPIC_H */ diff --git a/libc/sysdeps/linux/arm/crtreloc.c b/libc/sysdeps/linux/arm/crtreloc.c new file mode 100644 index 000000000..560b4168b --- /dev/null +++ b/libc/sysdeps/linux/arm/crtreloc.c @@ -0,0 +1,144 @@ +/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. + written by Alexandre Oliva <aoliva@redhat.com> +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +In addition to the permissions in the GNU Lesser General Public +License, the Free Software Foundation gives you unlimited +permission to link the compiled version of this file with other +programs, and to distribute those programs without any restriction +coming from the use of this file. (The GNU Lesser General Public +License restrictions do apply in other respects; for example, they +cover modification of the file, and distribution when not linked +into another program.) + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, see <http://www.gnu.org/licenses/>. */ + +#ifdef __FDPIC__ + +#include <sys/types.h> +#include <link.h> + +/* This file is to be compiled into crt object files, to enable + executables to easily self-relocate. */ + +union word { + char c[4]; + void *v; +}; + +/* Compute the runtime address of pointer in the range [p,e), and then + map the pointer pointed by it. */ +static __always_inline void *** +reloc_range_indirect (void ***p, void ***e, + const struct elf32_fdpic_loadmap *map) +{ + while (p < e) + { + if (*p != (void **)-1) + { + void *ptr = __reloc_pointer (*p, map); + if (ptr != (void *)-1) + { + void *pt; + if ((long)ptr & 3) + { + unsigned char *c = ptr; + int i; + unsigned long v = 0; + for (i = 0; i < 4; i++) + v |= c[i] << 8 * i; + pt = (void *)v; + } + else + pt = *(void**)ptr; + pt = __reloc_pointer (pt, map); + if ((long)ptr & 3) + { + unsigned char *c = ptr; + int i; + unsigned long v = (unsigned long)pt; + for (i = 0; i < 4; i++, v >>= 8) + c[i] = v; + } + else + *(void**)ptr = pt; + } + } + p++; + } + return p; +} + +/* Call __reloc_range_indirect for the given range except for the last + entry, whose contents are only relocated. It's expected to hold + the GOT value. */ +attribute_hidden void* +__self_reloc (const struct elf32_fdpic_loadmap *map, + void ***p, void ***e) +{ + p = reloc_range_indirect (p, e-1, map); + + if (p >= e) + return (void*)-1; + + return __reloc_pointer (*p, map); +} + +#if 0 +/* These are other functions that might be useful, but that we don't + need. */ + +/* Remap pointers in [p,e). */ +static __always_inline void** +reloc_range (void **p, void **e, + const struct elf32_fdpic_loadmap *map) +{ + while (p < e) + { + *p = __reloc_pointer (*p, map); + p++; + } + return p; +} + +/* Remap p, adjust e by the same offset, then map the pointers in the + range determined by them. */ +void attribute_hidden +__reloc_range (const struct elf32_fdpic_loadmap *map, + void **p, void **e) +{ + void **old = p; + + p = __reloc_pointer (p, map); + e += p - old; + reloc_range (p, e, map); +} + +/* Remap p, adjust e by the same offset, then map pointers referenced + by the (unadjusted) pointers in the range. Return the relocated + value of the last pointer in the range. */ +void* attribute_hidden +__reloc_range_indirect (const struct elf32_fdpic_loadmap *map, + void ***p, void ***e) +{ + void ***old = p; + + p = __reloc_pointer (p, map); + e += p - old; + return reloc_range_indirect (p, e, map); +} +#endif + +#endif /* __FDPIC__ */ diff --git a/libc/sysdeps/linux/arm/find_exidx.c b/libc/sysdeps/linux/arm/find_exidx.c index 679d90c05..cd4d442cf 100644 --- a/libc/sysdeps/linux/arm/find_exidx.c +++ b/libc/sysdeps/linux/arm/find_exidx.c @@ -18,6 +18,23 @@ #include <link.h> #include <unwind.h> +#if __FDPIC__ +#include <bits/elf-fdpic.h> +static __always_inline int +__dl_addr_in_loadaddr(void *p, struct elf32_fdpic_loadaddr loadaddr) +{ + struct elf32_fdpic_loadmap *map = loadaddr.map; + int c; + + for (c = 0; c < map->nsegs; c++) + if ((void *)map->segs[c].addr <= p && + (char *)p < (char *)map->segs[c].addr + map->segs[c].p_memsz) + return 1; + + return 0; +} +#endif + struct unw_eh_callback_data { _Unwind_Ptr pc; @@ -32,6 +49,26 @@ struct unw_eh_callback_data static int find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr) { +#if __FDPIC__ + struct unw_eh_callback_data * data; + const ElfW(Phdr) *phdr; + int i; + int match = 0; + + data = (struct unw_eh_callback_data *) ptr; + if (__dl_addr_in_loadaddr((void *) data->pc, info->dlpi_addr)) { + match = 1; + phdr = info->dlpi_phdr; + for (i = info->dlpi_phnum; i > 0; i--, phdr++) { + if (phdr->p_type == PT_ARM_EXIDX) { + data->exidx_start = (_Unwind_Ptr) __RELOC_POINTER(phdr->p_vaddr, info->dlpi_addr); + data->exidx_len = phdr->p_memsz; + } + } + } + + return match; +#else struct unw_eh_callback_data * data; const ElfW(Phdr) *phdr; int i; @@ -59,6 +96,7 @@ find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr) } return match; +#endif } |