diff mbox series

[uclibc-ng-devel,04/32,ARM,FDPIC] rtld: Add FDPIC code for arm

Message ID 20180704155605.1892-5-christophe.lyon@st.com
State Accepted
Headers show
Series FDPIC ABI for ARM | expand

Commit Message

Christophe Lyon July 4, 2018, 3:55 p.m. UTC
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>

Comments

Christophe Lyon Aug. 6, 2018, 1:23 p.m. UTC | #1
On 04/07/2018 17:55, Christophe Lyon wrote:
> 	Add FDPIC dynamic relocations support, similar to what other FDPIC
> 	targets do.

Here is version 2 of this patch.
From 01c34002c0033f7027b0505a3ffb7e2c0ebf423a Mon Sep 17 00:00:00 2001
From: Christophe Lyon <christophe.lyon@st.com>
Date: Fri, 18 Jan 2013 15:08:04 +0100
Subject: [PATCH 04/32] [ARM][FDPIC] rtld: Add FDPIC code for arm
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

	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>

diff --git a/include/elf.h b/include/elf.h
index 2db85c5..6280a56 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -2696,6 +2696,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 fc1d1fc..bf69971 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 0000000..8fdf6eb
--- /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 371dc22..ea1e9f6 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 a47a552..0f783e1 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 96809a9..402ba96 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 7e0058e..2a51643 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 f590875..89e7a9a 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 0000000..3d6db54
--- /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 0000000..560b416
--- /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 679d90c..cd4d442 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
 }
diff mbox series

Patch

diff --git a/include/elf.h b/include/elf.h
index 2db85c5..6280a56 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -2696,6 +2696,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 fc1d1fc..bf69971 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 0000000..8fdf6eb
--- /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 371dc22..d15264f 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,53 @@  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;
+		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;
 		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 a47a552..2bb0023 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) \
 {				\
@@ -62,11 +75,17 @@  unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry);
    define the value.
 
    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.  */
-#define elf_machine_type_class(type)									\
-  ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32			\
+   of the main executable's symbols, as for a COPY reloc.
+
+   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)												\
+    * ELF_RTYPE_CLASS_PLT)						\
    | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY))
 
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
@@ -106,10 +125,22 @@  elf_machine_dynamic (void)
 
 extern char __dl_start[] __asm__("_dl_start");
 
+/* 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"
+
 /* 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 +159,29 @@  elf_machine_load_address (void)
 		 : "=r" (pcrel_addr), "=r" (tmp));
 #endif
 	return pcrel_addr - got_addr;
+#endif
 }
 
 static __always_inline void
-elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr,
+elf_machine_relative (DL_LOADADDR_TYPE load_off, const Elf32_Addr rel_addr,
 		      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 96809a9..1435c2c 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,40 @@  _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;
+			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;
 #if defined USE_TLS && USE_TLS
 			case R_ARM_TLS_DTPMOD32:
 				*reloc_addr = def_mod->l_tls_modid;
@@ -330,7 +369,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 +383,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 7e0058e..2a51643 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 f590875..89e7a9a 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 0000000..3d6db54
--- /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 0000000..560b416
--- /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 679d90c..cd4d442 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
 }