summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Lyon <christophe.lyon@st.com>2013-01-18 15:08:04 +0100
committerWaldemar Brodkorb <wbrodkorb@conet.de>2018-08-10 16:01:58 +0200
commit5ec586eb37f61e57f19ac60b58af61f167ca054a (patch)
tree6c72d03d43e76a33eb5d6360d9bdb2573a048c42
parentf88bb681ba22d77a19abfb4c1617c826298ed473 (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.h2
-rw-r--r--ldso/include/dl-string.h3
-rw-r--r--ldso/ldso/arm/dl-inlines.h1
-rw-r--r--ldso/ldso/arm/dl-startup.h47
-rw-r--r--ldso/ldso/arm/dl-sysdep.h67
-rw-r--r--ldso/ldso/arm/elfinterp.c85
-rw-r--r--ldso/ldso/arm/resolve.S1
-rw-r--r--ldso/ldso/fdpic/dl-inlines.h2
-rw-r--r--libc/sysdeps/linux/arm/bits/elf-fdpic.h114
-rw-r--r--libc/sysdeps/linux/arm/crtreloc.c144
-rw-r--r--libc/sysdeps/linux/arm/find_exidx.c38
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
}