Browse Source

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>
Christophe Lyon 12 years ago
parent
commit
5ec586eb37

+ 2 - 0
include/elf.h

@@ -2705,6 +2705,8 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_ARM_TLS_LDO12		109
 #define R_ARM_TLS_LDO12		109
 #define R_ARM_TLS_LE12		110
 #define R_ARM_TLS_LE12		110
 #define R_ARM_TLS_IE12GP	111
 #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_RXPC25		249
 #define R_ARM_RSBREL32		250
 #define R_ARM_RSBREL32		250
 #define R_ARM_THM_RPC22		251
 #define R_ARM_THM_RPC22		251

+ 2 - 1
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
 /* On some (wierd) arches, none of this stuff works at all, so
  * disable the whole lot... */
  * 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_STDERR(X)
 # define SEND_ADDRESS_STDERR(X, add_a_newline)
 # define SEND_ADDRESS_STDERR(X, add_a_newline)

+ 1 - 0
ldso/ldso/arm/dl-inlines.h

@@ -0,0 +1 @@
+#include "../fdpic/dl-inlines.h"

+ 45 - 2
ldso/ldso/arm/dl-startup.h

@@ -131,7 +131,7 @@ __asm__(
 /* Handle relocation of the symbols in the dynamic loader. */
 /* Handle relocation of the symbols in the dynamic loader. */
 static __always_inline
 static __always_inline
 void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr,
 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)) {
 	switch (ELF_R_TYPE(rpnt->r_info)) {
 		case R_ARM_NONE:
 		case R_ARM_NONE:
@@ -176,12 +176,55 @@ void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr,
 			*reloc_addr = symbol_addr;
 			*reloc_addr = symbol_addr;
 			break;
 			break;
 		case R_ARM_RELATIVE:
 		case R_ARM_RELATIVE:
-			*reloc_addr += load_addr;
+			*reloc_addr = DL_RELOC_ADDR(load_addr, *reloc_addr);
 			break;
 			break;
 		case R_ARM_COPY:
 		case R_ARM_COPY:
 			break;
 			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:
 		default:
 			SEND_STDERR("Unsupported relocation type\n");
 			SEND_STDERR("Unsupported relocation type\n");
 			_dl_exit(1);
 			_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__ */

+ 61 - 6
ldso/ldso/arm/dl-sysdep.h

@@ -10,6 +10,19 @@
 /* Define this if the system uses RELOCA.  */
 /* Define this if the system uses RELOCA.  */
 #undef ELF_USES_RELOCA
 #undef ELF_USES_RELOCA
 #include <elf.h>
 #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.  */
 /* Initialization sequence for the GOT.  */
 #define INIT_GOT(GOT_BASE,MODULE) \
 #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
    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.  */
    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)									\
 #define elf_machine_type_class(type)									\
   ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32			\
   ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32			\
      || (type) == R_ARM_TLS_DTPOFF32 || (type) == R_ARM_TLS_TPOFF32)	\
      || (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))
    | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY))
+#endif /* __FDPIC__ */
 
 
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
 /* 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
    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");
 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.  */
 /* Return the run-time load address of the shared object.  */
 static __always_inline Elf32_Addr __attribute__ ((unused))
 static __always_inline Elf32_Addr __attribute__ ((unused))
 elf_machine_load_address (void)
 elf_machine_load_address (void)
 {
 {
+#if defined(__FDPIC__)
+	return 0;
+#else
 	Elf32_Addr got_addr = (Elf32_Addr) &__dl_start;
 	Elf32_Addr got_addr = (Elf32_Addr) &__dl_start;
 	Elf32_Addr pcrel_addr;
 	Elf32_Addr pcrel_addr;
 #if defined __OPTIMIZE__ && !defined __thumb__
 #if defined __OPTIMIZE__ && !defined __thumb__
@@ -128,19 +169,33 @@ elf_machine_load_address (void)
 		 : "=r" (pcrel_addr), "=r" (tmp));
 		 : "=r" (pcrel_addr), "=r" (tmp));
 #endif
 #endif
 	return pcrel_addr - got_addr;
 	return pcrel_addr - got_addr;
+#endif
 }
 }
 
 
 static __always_inline void
 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,
 elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr,
+#endif
 		      Elf32_Word relative_count)
 		      Elf32_Word relative_count)
 {
 {
-	 Elf32_Rel * rpnt = (void *) rel_addr;
+#if defined(__FDPIC__)
-	--rpnt;
+    Elf32_Rel *rpnt = (void *) rel_addr;
-	do {
+
-		Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset);
+    do {
+        unsigned long *reloc_addr = (unsigned long *) DL_RELOC_ADDR(load_off, rpnt->r_offset);
 
 
-		*reloc_addr += load_off;
+        *reloc_addr = DL_RELOC_ADDR(load_off, *reloc_addr);
-	} while (--relative_count);
+        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 */
 #endif /* !_ARCH_DL_SYSDEP */
 
 

+ 64 - 21
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)
 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;
 	ELF_RELOC *this_reloc;
 	char *strtab;
 	char *strtab;
 	char *symname;
 	char *symname;
@@ -88,6 +93,7 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
 #endif
 #endif
 
 
 	return new_addr;
 	return new_addr;
+#endif
 }
 }
 
 
 static int
 static int
@@ -181,7 +187,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope,
 	struct elf_resolve *def_mod = 0;
 	struct elf_resolve *def_mod = 0;
 	int goof = 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);
 	reloc_type = ELF_R_TYPE(rpnt->r_info);
 	symtab_index = ELF_R_SYM(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;
 	symname = strtab + symtab[symtab_index].st_name;
 
 
 	if (symtab_index) {
 	if (symtab_index) {
-		symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
+		if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) {
-						elf_machine_type_class(reloc_type), &sym_ref);
+			symbol_addr = (unsigned long) DL_RELOC_ADDR(tpnt->loadaddr, symtab[symtab_index].st_value);
-
+			def_mod = tpnt;
-		/*
+		} else {
-		 * We want to allow undefined references to weak symbols - this might
+			symbol_addr =  (unsigned long)_dl_find_hash(symname, scope, tpnt,
-		 * have been intentional.  We should not be linking local symbols
+							elf_machine_type_class(reloc_type), &sym_ref);
-		 * here, so all bases should be covered.
+
-		 */
+			/*
-		if (!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS)
+			 * We want to allow undefined references to weak symbols - this might
-			&& (ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) {
+			 * have been intentional.  We should not be linking local symbols
-			/* This may be non-fatal if called from dlopen.  */
+			 * here, so all bases should be covered.
-			return 1;
+			 */
-
+			if (!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS)
-		}
+				&& (ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) {
-		if (_dl_trace_prelink) {
+				/* This may be non-fatal if called from dlopen.  */
-			_dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
+				return 1;
-					&sym_ref, elf_machine_type_class(reloc_type));
+
+			}
+			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 {
 	} else {
 		/*
 		/*
 		 * Relocs against STN_UNDEF are usually treated as using a
 		 * 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;
 				*reloc_addr = symbol_addr;
 				break;
 				break;
 			case R_ARM_RELATIVE:
 			case R_ARM_RELATIVE:
-				*reloc_addr += (unsigned long) tpnt->loadaddr;
+				*reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, *reloc_addr);
 				break;
 				break;
 			case R_ARM_COPY:
 			case R_ARM_COPY:
 				_dl_memcpy((void *) reloc_addr,
 				_dl_memcpy((void *) reloc_addr,
 					   (void *) symbol_addr, symtab[symtab_index].st_size);
 					   (void *) symbol_addr, symtab[symtab_index].st_size);
 				break;
 				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
 #if defined USE_TLS && USE_TLS
 			case R_ARM_TLS_DTPMOD32:
 			case R_ARM_TLS_DTPMOD32:
 				*reloc_addr = def_mod->l_tls_modid;
 				*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
 #endif
 	return 0;
 	return 0;
-
 }
 }
 
 
 void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
 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);
 	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

+ 1 - 0
ldso/ldso/arm/resolve.S

@@ -102,6 +102,7 @@
  .align 4      @ 16 byte boundary and there are 32 bytes below (arm case)
  .align 4      @ 16 byte boundary and there are 32 bytes below (arm case)
 #if 1 /*(!defined(__thumb__) || defined __THUMB_INTERWORK__) || defined(__thumb2__)*/
 #if 1 /*(!defined(__thumb__) || defined __THUMB_INTERWORK__) || defined(__thumb2__)*/
  .arm
  .arm
+ .hidden _dl_linux_resolve
  .globl _dl_linux_resolve
  .globl _dl_linux_resolve
  .type _dl_linux_resolve,%function
  .type _dl_linux_resolve,%function
  .align 4;
  .align 4;

+ 2 - 0
ldso/ldso/fdpic/dl-inlines.h

@@ -7,6 +7,8 @@
 
 
 #include <inline-hashtab.h>
 #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. */
 /* Initialize a DL_LOADADDR_TYPE given a got pointer and a complete load map. */
 static __always_inline void
 static __always_inline void
 __dl_init_loadaddr_map(struct elf32_fdpic_loadaddr *loadaddr, Elf32_Addr dl_boot_got_pointer,
 __dl_init_loadaddr_map(struct elf32_fdpic_loadaddr *loadaddr, Elf32_Addr dl_boot_got_pointer,

+ 114 - 0
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 */

+ 144 - 0
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__ */

+ 38 - 0
libc/sysdeps/linux/arm/find_exidx.c

@@ -18,6 +18,23 @@
 #include <link.h>
 #include <link.h>
 #include <unwind.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
 struct unw_eh_callback_data
 {
 {
   _Unwind_Ptr pc;
   _Unwind_Ptr pc;
@@ -32,6 +49,26 @@ struct unw_eh_callback_data
 static int
 static int
 find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr)
 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;
   struct unw_eh_callback_data * data;
   const ElfW(Phdr) *phdr;
   const ElfW(Phdr) *phdr;
   int i;
   int i;
@@ -59,6 +96,7 @@ find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr)
     }
     }
 
 
   return match;
   return match;
+#endif
 }
 }