Browse Source

xtensa: add FDPIC support

This change implements Xtensa FDPIC ABI as specified in the first
version of the following document:

 https://github.com/jcmvbkbc/xtensa-abi/blob/master/fdpic-xtensa.txt

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Max Filippov 10 months ago
parent
commit
138274e92a

+ 4 - 0
Rules.mak

@@ -520,6 +520,10 @@ ifeq ($(TARGET_ARCH),c6x)
 	CPU_LDFLAGS-y += $(CPU_CFLAGS)
 endif
 
+ifeq ($(TARGET_ARCH),xtensa)
+	CPU_CFLAGS-$(UCLIBC_FORMAT_FDPIC_ELF) += -mfdpic
+endif
+
 $(eval $(call check-gcc-var,$(PIEFLAG_NAME)))
 PIEFLAG := $(CFLAG_$(PIEFLAG_NAME))
 ifeq ($(PIEFLAG),)

+ 1 - 1
extra/Configs/Config.in

@@ -615,7 +615,7 @@ config UCLIBC_HAS_THREADS_NATIVE
 		   !TARGET_h8300 && \
 		   !TARGET_hppa && \
 		   !TARGET_ia64 && \
-		   (ARCH_USE_MMU || TARGET_arm)
+		   (ARCH_USE_MMU || TARGET_arm || TARGET_xtensa)
 	help
 	  If you want to compile uClibc with NPTL support, then answer Y.
 

+ 1 - 1
extra/Configs/Config.in.arch

@@ -20,7 +20,7 @@ config UCLIBC_FORMAT_ELF
 	select HAVE_LDSO
 config UCLIBC_FORMAT_FDPIC_ELF
 	bool "FDPIC ELF"
-	depends on !ARCH_USE_MMU && (TARGET_bfin || TARGET_frv || TARGET_arm)
+	depends on !ARCH_USE_MMU && (TARGET_bfin || TARGET_frv || TARGET_arm || TARGET_xtensa)
 	select DOPIC
 config UCLIBC_FORMAT_DSBT_ELF
 	bool "DBST ELF"

+ 5 - 1
include/elf.h

@@ -3588,8 +3588,12 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_XTENSA_TLSDESC_FN	50
 #define R_XTENSA_TLSDESC_ARG	51
 #define R_XTENSA_TLS_TPOFF	53
+#define R_XTENSA_SYM32		63
+#define R_XTENSA_FUNCDESC	68
+#define R_XTENSA_FUNCDESC_VALUE	69
+#define R_XTENSA_TLSDESC	72
 /* Keep this the last entry.  */
-#define R_XTENSA_NUM		54
+#define R_XTENSA_NUM		77
 
 /* C6X specific relocs */
 #define R_C6000_NONE		0

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

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

+ 90 - 1
ldso/ldso/xtensa/dl-startup.h

@@ -7,6 +7,68 @@
  * Parts taken from glibc/sysdeps/xtensa/dl-machine.h.
  */
 
+#if defined(__FDPIC__)
+__asm__ (
+    "	.text\n"
+    "	.align  4\n"
+    "   .literal_position\n"
+    "	.global _start\n"
+    "	.type   _start, @function\n"
+    "	.hidden _start\n"
+    "_start:\n"
+    "	.begin	no-transform\n"
+    "	_call0  1f\n"
+    "2:\n"
+    "	.end	no-transform\n"
+    "	.align  4\n"
+    "1:\n"
+#if defined(__XTENSA_CALL0_ABI__)
+    "	movi	a15, 2b\n"
+    "	sub	a15, a0, a15\n"
+
+    /* Save FDPIC pointers in callee-saved registers */
+    "	mov	a12, a4\n"
+    "	mov	a13, a5\n"
+    "	mov	a14, a6\n"
+
+    /* Call __self_reloc */
+    "	mov	a2, a5\n"
+    "	movi	a3, __ROFIXUP_LIST__\n"
+    "	add	a3, a3, a15\n"
+    "	movi	a4, __ROFIXUP_END__\n"
+    "	add	a4, a4, a15\n"
+    "	movi    a0, __self_reloc\n"
+    "	add     a0, a0, a15\n"
+    "	callx0  a0\n"
+
+    /* call _dl_start */
+    "	mov	a3, a12\n"
+    "	mov	a4, a13\n"
+    "	mov	a5, a14\n"
+    "	mov	a7, sp\n"
+    "	addi	sp, sp, -16\n"
+    "	mov	a6, sp\n"
+    "	mov	a11, a2\n"
+    /* a13, interpreter map is no longer needed, save interpreter GOT there */
+    "	mov	a13, a2\n"
+    "	movi	a0, _dl_start\n"
+    "	add	a0, a0, a15\n"
+    "	callx0	a0\n"
+
+    /* call main */
+    "	l32i	a0, sp, 0\n"
+    "	l32i	a11, sp, 4\n"
+    "	addi	sp, sp, 16\n"
+    "	mov	a4, a12\n"
+    "	movi	a5, _dl_fini@GOTOFFFUNCDESC\n"
+    "	add	a5, a5, a13\n"
+    "	mov	a6, a14\n"
+    "	jx	a0\n"
+#else
+#error Unsupported Xtensa ABI
+#endif
+   );
+#else /* __FDPIC__ */
 #ifndef L_rcrt1
 __asm__ (
     "	.text\n"
@@ -83,6 +145,7 @@ __asm__ (
     "	bnez    a6, 3b\n"
     "	j      .Lfixup_stack_ret");
 #endif
+#endif /* __FDPIC__ */
 
 /* Get a pointer to the argv value.  */
 #define GET_ARGV(ARGVP, ARGS) ARGVP = (((unsigned long *) ARGS) + 1)
@@ -90,7 +153,7 @@ __asm__ (
 /* Function calls are not safe until the GOT relocations have been done.  */
 #define NO_FUNCS_BEFORE_BOOTSTRAP
 
-#if defined(__ARCH_USE_MMU__)
+#if defined(__ARCH_USE_MMU__) && !defined(__FDPIC__)
 #define PERFORM_BOOTSTRAP_GOT(tpnt) \
 do { \
 	xtensa_got_location *got_loc; \
@@ -128,3 +191,29 @@ do { \
 	} \
 } while (0)
 #endif
+
+#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)
+#endif /* __FDPIC__ */

+ 65 - 1
ldso/ldso/xtensa/dl-sysdep.h

@@ -26,6 +26,7 @@
    in l_info array.  */
 #define DT_XTENSA(x) (DT_XTENSA_##x - DT_LOPROC + DT_NUM + OS_NUM)
 
+#ifndef __FDPIC__
 typedef struct xtensa_got_location_struct {
   Elf32_Off offset;
   Elf32_Word length;
@@ -86,6 +87,7 @@ typedef struct xtensa_got_location_struct {
     else if (dpnt->d_tag == DT_XTENSA_GOT_LOC_SZ)			\
       dynamic[DT_XTENSA (GOT_LOC_SZ)] = dpnt->d_un.d_val;		\
   } while (0)
+#endif
 
 /* Here we define the magic numbers that this dynamic loader should accept. */
 #define MAGIC1 EM_XTENSA
@@ -115,10 +117,41 @@ elf_machine_dynamic (void)
   return (Elf32_Addr) &_DYNAMIC;
 }
 
+#ifdef __FDPIC__
+
+#define DL_CHECK_LIB_TYPE(epnt, piclib, _dl_progname, libname) \
+do \
+{ \
+  (piclib) = 2; \
+} \
+while (0)
+
+/* 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"
+#undef INIT_GOT
+#define INIT_GOT(GOT_BASE,MODULE) \
+{				\
+  (MODULE)->loadaddr.got_value = (GOT_BASE); \
+  GOT_BASE[0] = ((unsigned long *)&_dl_linux_resolve)[0]; \
+  GOT_BASE[1] = ((unsigned long *)&_dl_linux_resolve)[1]; \
+  GOT_BASE[2] = (unsigned long) MODULE; \
+}
+
+#endif /* __FDPIC__ */
+
 /* Return the run-time load address of the shared object.  */
 static __always_inline Elf32_Addr
 elf_machine_load_address (void)
 {
+#ifdef __FDPIC__
+  return 0;
+#else
   Elf32_Addr addr, tmp;
 
   /* At this point, the runtime linker is being bootstrapped and the GOT
@@ -135,11 +168,41 @@ elf_machine_load_address (void)
 	   : "=a" (addr), "=a" (tmp));
 
   return addr - 3;
+#endif
 }
 
+#ifdef __FDPIC__
+
+/* Need bootstrap relocations */
+#define ARCH_NEEDS_BOOTSTRAP_RELOCS
+
+#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD,SYMTAB)	\
+	switch (ELF_R_TYPE((RELP)->r_info)){			\
+	case R_XTENSA_SYM32:					\
+		*(REL)  = (SYMBOL) + (RELP)->r_addend;		\
+		break;						\
+	case R_XTENSA_RELATIVE:					\
+	case R_XTENSA_NONE:					\
+	default:						\
+		break;						\
+	}
+
 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_Rela *rpnt = (Elf32_Rela *) rel_addr;
+  while (relative_count--)
+    {
+      Elf32_Addr *const reloc_addr = (Elf32_Addr *) DL_RELOC_ADDR(load_off, rpnt->r_offset);
+      *reloc_addr = DL_RELOC_ADDR(load_off, *reloc_addr);
+      rpnt++;
+    }
+}
+#else
+static __always_inline void
+elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr,
+                     Elf32_Word relative_count)
 {
   Elf32_Rela *rpnt = (Elf32_Rela *) rel_addr;
   while (relative_count--)
@@ -149,3 +212,4 @@ elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr,
       rpnt++;
     }
 }
+#endif

+ 8 - 2
ldso/ldso/xtensa/dl-tlsdesc.S

@@ -24,6 +24,9 @@
 
 	.text
 HIDDEN_ENTRY (_dl_tlsdesc_return)
+#ifdef __FDPIC__
+	l32i		a2, a2, 4
+#endif
 	rur.threadptr	a3
 	add		a2, a2, a3
 	abi_ret
@@ -53,7 +56,9 @@ END (_dl_tlsdesc_return)
 	 */
 
 HIDDEN_ENTRY (_dl_tlsdesc_dynamic)
-
+#ifdef __FDPIC__
+	l32i	a2, a2, 4
+#endif
 	/* dtv_t *dtv = (dtv_t *)THREAD_DTV(); */
 	rur.threadptr	a3
 	l32i	a4, a3, 0
@@ -86,7 +91,8 @@ HIDDEN_ENTRY (_dl_tlsdesc_dynamic)
 #elif defined(__XTENSA_CALL0_ABI__)
 	addi	a1, a1, -16
 	s32i	a0, a1, 0
-	movi	a0, __tls_get_addr
+	movi	a0, JUMPTARGET(__tls_get_addr)
+	FDPIC_LOAD_JUMPTARGET(a0, a11, a0)
 	callx0	a0
 	l32i	a0, a1, 0
 	addi	a1, a1, 16

+ 89 - 25
ldso/ldso/xtensa/elfinterp.c

@@ -36,6 +36,13 @@
 #include "tlsdeschtab.h"
 #endif
 
+#ifdef __FDPIC__
+unsigned long
+_dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry)
+{
+	return 0;
+}
+#else
 unsigned long
 _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry)
 {
@@ -83,7 +90,7 @@ _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry)
 
 	return (unsigned long) new_addr;
 }
-
+#endif
 
 static int
 _dl_parse (struct elf_resolve *tpnt, struct r_scope_elem *scope,
@@ -145,8 +152,8 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	int reloc_type;
 	int symtab_index;
 	char *symname;
-#if defined USE_TLS && USE_TLS
-	struct elf_resolve *tls_tpnt = NULL;
+#if defined USE_TLS && USE_TLS || defined (__FDPIC__)
+	struct elf_resolve *def_mod = NULL;
 #endif
 	struct symbol_ref sym_ref;
 	ElfW(Addr) *reloc_addr;
@@ -155,7 +162,7 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	ElfW(Addr) old_val;
 #endif
 
-	reloc_addr = (ElfW(Addr) *) (tpnt->loadaddr + rpnt->r_offset);
+	reloc_addr = (ElfW(Addr) *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
 	reloc_type = ELF_R_TYPE (rpnt->r_info);
 	symtab_index = ELF_R_SYM (rpnt->r_info);
 	sym_ref.sym = &symtab[symtab_index];
@@ -164,9 +171,17 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	symname = strtab + sym_ref.sym->st_name;
 
 	if (symtab_index) {
-		symbol_addr = (ElfW(Addr))
-			_dl_find_hash (symname, scope, tpnt,
-						   elf_machine_type_class (reloc_type), &sym_ref);
+		if (ELF_ST_BIND (sym_ref.sym->st_info) == STB_LOCAL) {
+			symbol_addr = (ElfW(Addr))
+				DL_RELOC_ADDR(tpnt->loadaddr,
+					      symtab[symtab_index].st_value);
+			sym_ref.tpnt = tpnt;
+		} else {
+			symbol_addr = (ElfW(Addr))
+				_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
@@ -182,13 +197,13 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 			_dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
 						&sym_ref, elf_machine_type_class(reloc_type));
 		}
-#if defined USE_TLS && USE_TLS
-		tls_tpnt = sym_ref.tpnt;
+#if defined USE_TLS && USE_TLS || defined (__FDPIC__)
+		def_mod = sym_ref.tpnt;
 #endif
 	} else {
 		symbol_addr =symtab[symtab_index].st_value;
-#if defined USE_TLS && USE_TLS
-		tls_tpnt = tpnt;
+#if defined USE_TLS && USE_TLS || defined (__FDPIC__)
+		def_mod = tpnt;
 #endif
 	}
 
@@ -202,6 +217,7 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 
 	case R_XTENSA_GLOB_DAT:
 	case R_XTENSA_JMP_SLOT:
+	case R_XTENSA_SYM32:
 		*reloc_addr = symbol_addr + rpnt->r_addend;
 		break;
 
@@ -219,19 +235,63 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 		break;
 
 	case R_XTENSA_RELATIVE:
-		*reloc_addr += tpnt->loadaddr + rpnt->r_addend;
+		*reloc_addr += DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_addend);
 		break;
 
+#ifdef __FDPIC__
+	case R_XTENSA_FUNCDESC_VALUE:
+		{
+			struct funcdesc_value *dst = (struct funcdesc_value *) reloc_addr;
+
+			dst->entry_point = (void *) (symbol_addr + rpnt->r_addend);
+			dst->got_value = def_mod->loadaddr.got_value;
+		}
+		break;
+	case R_XTENSA_FUNCDESC:
+		if (symbol_addr)
+			*reloc_addr = (unsigned long)
+				_dl_funcdesc_for((void *) (symbol_addr + rpnt->r_addend),
+						 sym_ref.tpnt->loadaddr.got_value);
+		else
+			/* Relocation against an undefined weak symbol:
+			   set funcdesc to zero.  */
+			*reloc_addr = 0;
+		break;
+	case R_XTENSA_TLS_TPOFF:
+		CHECK_STATIC_TLS((struct link_map *) def_mod);
+		*reloc_addr = symbol_addr + rpnt->r_addend + def_mod->l_tls_offset;
+		break;
+	case R_XTENSA_TLSDESC:
+		{
+			struct tlsdesc *td = (struct tlsdesc *) reloc_addr;
+#ifndef SHARED
+			CHECK_STATIC_TLS((struct link_map *) def_mod);
+#else
+			if (!TRY_STATIC_TLS ((struct link_map *) def_mod))
+			{
+				td->entry = _dl_tlsdesc_dynamic;
+				td->argument = _dl_make_tlsdesc_dynamic((struct link_map *) def_mod,
+									symbol_addr + rpnt->r_addend);
+			}
+			else
+#endif
+			{
+				td->entry = _dl_tlsdesc_return;
+				td->argument = (void *) (symbol_addr + rpnt->r_addend + def_mod->l_tls_offset);
+			}
+		}
+		break;
+#else
 #if defined USE_TLS && USE_TLS
 	case R_XTENSA_TLS_TPOFF:
-		CHECK_STATIC_TLS((struct link_map *) tls_tpnt);
-		*reloc_addr = symbol_addr + tls_tpnt->l_tls_offset + rpnt->r_addend;
+		CHECK_STATIC_TLS((struct link_map *) def_mod);
+		*reloc_addr = symbol_addr + rpnt->r_addend + def_mod->l_tls_offset;
 		break;
 	case R_XTENSA_TLSDESC_FN:
 #ifndef SHARED
-		CHECK_STATIC_TLS((struct link_map *) tls_tpnt);
+		CHECK_STATIC_TLS((struct link_map *) def_mod);
 #else
-		if (!TRY_STATIC_TLS ((struct link_map *) tls_tpnt))
+		if (!TRY_STATIC_TLS ((struct link_map *) def_mod))
 			*reloc_addr = (ElfW(Addr)) _dl_tlsdesc_dynamic;
 		else
 #endif
@@ -239,25 +299,25 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 		break;
 	case R_XTENSA_TLSDESC_ARG:
 #ifndef SHARED
-		CHECK_STATIC_TLS((struct link_map *) tls_tpnt);
+		CHECK_STATIC_TLS((struct link_map *) def_mod);
 #else
-		if (!TRY_STATIC_TLS ((struct link_map *) tls_tpnt))
+		if (!TRY_STATIC_TLS ((struct link_map *) def_mod))
 			*reloc_addr = (ElfW(Addr))
-				_dl_make_tlsdesc_dynamic((struct link_map *) tls_tpnt,
+				_dl_make_tlsdesc_dynamic((struct link_map *) def_mod,
 							 symbol_addr + rpnt->r_addend);
 		else
 #endif
 			*reloc_addr = symbol_addr + rpnt->r_addend +
-				tls_tpnt->l_tls_offset;
+				def_mod->l_tls_offset;
 		break;
 #endif
-
+#endif
 	default:
 		return -1; /* Calls _dl_exit(1).  */
 	}
 #if defined (__SUPPORT_LD_DEBUG__)
 	if (_dl_debug_reloc && _dl_debug_detail)
-		_dl_dprintf (_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
+		_dl_dprintf (_dl_debug_file, "\tpatched: %x ==> %x @ %p\n",
 					 old_val, *reloc_addr, reloc_addr);
 #endif
 
@@ -275,7 +335,7 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	ElfW(Addr) old_val;
 #endif
 
-	reloc_addr = (ElfW(Addr) *) (tpnt->loadaddr + rpnt->r_offset);
+	reloc_addr = (ElfW(Addr) *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
 	reloc_type = ELF_R_TYPE (rpnt->r_info);
 
 #if defined (__SUPPORT_LD_DEBUG__)
@@ -286,7 +346,7 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	case R_XTENSA_JMP_SLOT:
 		/* Perform a RELATIVE reloc on the GOT entry that transfers
 		   to the stub function.  */
-		*reloc_addr += tpnt->loadaddr;
+		*reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, *reloc_addr);
 		break;
 	case R_XTENSA_NONE:
 		break;
@@ -296,7 +356,7 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 
 #if defined (__SUPPORT_LD_DEBUG__)
 	if (_dl_debug_reloc && _dl_debug_detail)
-		_dl_dprintf (_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
+		_dl_dprintf (_dl_debug_file, "\tpatched: %x ==> %x @ %p\n",
 					 old_val, *reloc_addr, reloc_addr);
 #endif
 	return 0;
@@ -320,3 +380,7 @@ _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/xtensa/crtreloc.c"
+#endif

+ 5 - 1
libc/sysdeps/linux/xtensa/__start_context.S

@@ -22,9 +22,10 @@
  * There's no entry instruction, makecontext sets up ucontext_t as if
  * getcontext was called above and is about to return here.
  * Registers on entry to this function:
- *   a12: func to call
+ *   a12: func to call (function descriptor in case of FDPIC)
  *   a13: ucp->uc_link, next context to activate if func returns
  *   a14: func argc
+ *   a15: current GOT pointer (in case of FDPIC)
  */
 	.literal_position
 
@@ -46,14 +47,17 @@ ENTRY_PREFIX(__start_context)
 	addi	a1, a1, 16
 	/* func arguments 6..argc - 1 are now at the top of the stack */
 1:
+	FDPIC_LOAD_FUNCDESC (a12, a12)
 	callx0	a12
 	beqz	a13, 1f
 	mov	a2, a13
 	movi	a4, JUMPTARGET (setcontext)
+	FDPIC_LOAD_JUMPTARGET (a4, a15, a4)
 	callx0	a4
 1:
 	movi	a4, JUMPTARGET (_exit)
 	movi	a2, 0
+	FDPIC_LOAD_JUMPTARGET (a4, a15, a4)
 	callx0	a4
 	ill
 END(__start_context)

+ 117 - 0
libc/sysdeps/linux/xtensa/bits/elf-fdpic.h

@@ -0,0 +1,117 @@
+/* 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 = (char*)p - (char*)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))
+
+void*
+__self_reloc (const struct elf32_fdpic_loadmap *map, void ***p, void ***e);
+
+#endif /* _BITS_ELF_FDPIC_H */

+ 6 - 0
libc/sysdeps/linux/xtensa/clone.S

@@ -81,11 +81,17 @@ ENTRY (__clone)
 	callx4	a2
 #elif defined(__XTENSA_CALL0_ABI__)
 	mov	a2, a9			/* load up the 'arg' parameter */
+#ifdef __FDPIC__
+	mov	a12, a11
+	l32i	a11, a7, 4
+	l32i	a7, a7, 0
+#endif
 	callx0	a7			/* call the user's function */
 
 	/* Call _exit.  Note that any return parameter from the user's
 	   function in a2 is seen as inputs to _exit.  */
 	movi	a0, JUMPTARGET(_exit)
+	FDPIC_LOAD_JUMPTARGET(a0, a12, a0)
 	callx0	a0
 #else
 #error Unsupported Xtensa ABI

+ 81 - 0
libc/sysdeps/linux/xtensa/crt1.S

@@ -35,6 +35,86 @@
 
 #include <features.h>
 
+#if defined(__FDPIC__)
+
+/* This is the canonical entry point, usually the first thing in the text
+   segment.  When the entry point runs, most register values are unspecified,
+   except for:
+
+	a6	Address of .dynamic section
+	a5	Interpreter map
+	a4	Executable map
+
+	a2	Contains a function pointer to be registered with `atexit'.
+		This is how the dynamic linker arranges to have DT_FINI
+		functions called for shared libraries that have been loaded
+		before this code runs.
+
+	a1	The stack (i.e., a1+16) contains the arguments and environment:
+		a1+0			argc
+		a1+4			argv[0]
+		...
+		a1+(4*argc)		NULL
+		a1+(4*(argc+1))		envp[0]
+		...
+					NULL
+ */
+	.text
+	.align	4
+	.literal_position
+	.global	_start
+	.type	_start, @function
+_start:
+#if defined(__XTENSA_CALL0_ABI__)
+
+	.begin	no-transform
+	call0	1f
+2:
+	.end	no-transform
+	.align	4
+	.literal_position
+1:
+	movi	a15, 2b
+	sub	a15, a0, a15
+
+	mov	a12, a4
+	mov	a13, a5
+	mov	a14, a6
+	mov	a2, a4
+	movi	a3, __ROFIXUP_LIST__
+	add	a3, a3, a15
+	movi	a4, __ROFIXUP_END__
+	add	a4, a4, a15
+	movi	a0, __self_reloc
+	add	a0, a0, a15
+	callx0	a0
+
+	mov	a11, a2
+	movi	a2, main@GOTOFFFUNCDESC
+	add	a2, a2, a11
+	l32i	a3, sp, 0	/* argc */
+	addi	a4, sp, 4	/* argv */
+	/* a5 is either 0 when static or set by the RTLD to the rtld_fini */
+	mov	a7, a13
+	/* unused stack_end argument is what used to be argc */
+	movi	a5, _init@GOTOFFFUNCDESC
+	add	a5, a5, a11
+	movi	a6, _fini@GOTOFFFUNCDESC
+	add	a6, a6, a11
+
+	movi	a0, __uClibc_main@GOTOFFFUNCDESC
+	add	a0, a0, a11
+	l32i	a11, a0, 4
+	l32i	a0, a0, 0
+	callx0	a0
+	ill
+
+#else
+#error Unsupported Xtensa ABI
+#endif
+
+#else /* defined(__FDPIC__) */
+
 #ifndef __UCLIBC_CTOR_DTOR__
         .weak _init
         .weak _fini
@@ -173,3 +253,4 @@ __data_start:
 	.long	0
 	.weak	data_start
 	data_start = __data_start
+#endif /* defined(__FDPIC__) */

+ 8 - 0
libc/sysdeps/linux/xtensa/crti.S

@@ -11,6 +11,10 @@ _init:
 #elif defined(__XTENSA_CALL0_ABI__)
 	addi	sp, sp, -16
 	s32i	a0, sp, 0
+#ifdef __FDPIC__
+	s32i	a12, sp, 4
+	mov	a12, a11
+#endif
 #else
 #error Unsupported Xtensa ABI
 #endif
@@ -26,6 +30,10 @@ _fini:
 #elif defined(__XTENSA_CALL0_ABI__)
 	addi	sp, sp, -16
 	s32i	a0, sp, 0
+#ifdef __FDPIC__
+	s32i	a12, sp, 4
+	mov	a12, a11
+#endif
 #else
 #error Unsupported Xtensa ABI
 #endif

+ 6 - 0
libc/sysdeps/linux/xtensa/crtn.S

@@ -4,6 +4,9 @@
 #if defined(__XTENSA_WINDOWED_ABI__)
 	retw
 #elif defined(__XTENSA_CALL0_ABI__)
+#ifdef __FDPIC__
+	l32i	a12, sp, 4
+#endif
 	l32i	a0, sp, 0
 	addi	sp, sp, 16
 	ret
@@ -15,6 +18,9 @@
 #if defined(__XTENSA_WINDOWED_ABI__)
 	retw
 #elif defined(__XTENSA_CALL0_ABI__)
+#ifdef __FDPIC__
+	l32i	a12, sp, 4
+#endif
 	l32i	a0, sp, 0
 	addi	sp, sp, 16
 	ret

+ 105 - 0
libc/sysdeps/linux/xtensa/crtreloc.c

@@ -0,0 +1,105 @@
+/* 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.  */
+
+/* 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)
+	    {
+	      unsigned long off = ((unsigned long)ptr & 3) * 8;
+	      unsigned long *pa = (unsigned long *)((unsigned long)ptr & -4);
+	      unsigned long v2;
+	      void *pt;
+
+	      if (off)
+		{
+		  unsigned long v0, v1;
+#ifdef __XTENSA_EB__
+		  v0 = pa[1]; v1 = pa[0];
+		  v2 = (v1 >> (32 - off)) | (v0 << off);
+#else /* __XTENSA_EL__ */
+		  v0 = pa[0]; v1 = pa[1];
+		  v2 = (v0 << (32 - off)) | (v1 >> off);
+#endif
+		  pt = (void *)((v1 << (32 - off)) | (v0 >> off));
+		}
+	      else
+		pt = *(void**)ptr;
+	      pt = __reloc_pointer (pt, map);
+	      if (off)
+		{
+		  unsigned long v = (unsigned long)pt;
+#ifdef __XTENSA_EB__
+		  pa[0] = (v2 << (32 - off)) | (v >> off);
+		  pa[1] = (v << (32 - off)) | (v2 >> off);
+#else /* __XTENSA_EL__ */
+		  pa[0] = (v2 >> (32 - off)) | (v << off);
+		  pa[1] = (v >> (32 - off)) | (v2 << off);
+#endif
+		}
+	      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);
+}
+
+#endif /* __FDPIC__ */

+ 1 - 0
libc/sysdeps/linux/xtensa/getcontext.S

@@ -33,6 +33,7 @@ ENTRY(__getcontext)
 	addi	a4, a2, UCONTEXT_SIGMASK
 	movi	a2, SIG_BLOCK
 	movi	a5, JUMPTARGET (sigprocmask)
+	FDPIC_LOAD_JUMPTARGET (a5, a11, a5)
 	jx	a5
 END(__getcontext)
 #elif defined(__XTENSA_WINDOWED_ABI__)

+ 5 - 0
libc/sysdeps/linux/xtensa/makecontext.c

@@ -73,7 +73,12 @@ __makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
     sp -= 4 * (argc + 2);
   sp &= -16;
 
+#ifdef __FDPIC__
+  ucp->uc_mcontext.sc_pc = ((unsigned long *) __start_context)[0];
+  ucp->uc_mcontext.sc_a[15] = ((unsigned long *) __start_context)[1];
+#else
   ucp->uc_mcontext.sc_pc = (unsigned long) __start_context;
+#endif
   ucp->uc_mcontext.sc_a[1] = sp;
   ucp->uc_mcontext.sc_a[12] = (unsigned long) func;
   ucp->uc_mcontext.sc_a[13] = (unsigned long) ucp->uc_link;

+ 1 - 0
libc/sysdeps/linux/xtensa/setcontext.S

@@ -28,6 +28,7 @@ ENTRY(__setcontext)
 	movi	a4, 0
 	movi	a2, SIG_SETMASK
 	movi	a5, JUMPTARGET (sigprocmask)
+	FDPIC_LOAD_JUMPTARGET (a5, a11, a5)
 	callx0	a5
 	bnez	a2, .Lerror
 

+ 2 - 1
libc/sysdeps/linux/xtensa/setjmp.S

@@ -155,7 +155,8 @@ ENTRY (__sigsetjmp)
 	s32i	a14, a2, 16
 	s32i	a15, a2, 20
 	mov	a12, a2
-	movi	a0, __sigjmp_save
+	movi	a0, JUMPTARGET(__sigjmp_save)
+	FDPIC_LOAD_JUMPTARGET(a0, a11, a0)
 	callx0	a0
 	l32i	a0, a12, 0
 	l32i	a12, a12, 8

+ 1 - 0
libc/sysdeps/linux/xtensa/swapcontext.S

@@ -36,6 +36,7 @@ ENTRY(__swapcontext)
 	addi	a4, a2, UCONTEXT_SIGMASK
 	movi	a2, SIG_SETMASK
 	movi	a5, JUMPTARGET (sigprocmask)
+	FDPIC_LOAD_JUMPTARGET (a5, a11, a5)
 	callx0	a5
 	bnez	a2, .Lerror
 

+ 45 - 3
libc/sysdeps/linux/xtensa/sysdep.h

@@ -75,13 +75,27 @@
 #define LITERAL_POSITION .literal_position
 
 #undef JUMPTARGET
-#ifdef __PIC__
+#if defined(__FDPIC__)
+#define JUMPTARGET(name) name##@GOTOFFFUNCDESC
+#define FDPIC_LOAD_FUNCDESC(call_target, funcdesc)		\
+	l32i	a11, funcdesc, 4;				\
+	l32i	call_target, funcdesc, 0
+
+#define FDPIC_LOAD_JUMPTARGET(call_target, got_base, jumptarget)\
+	add	call_target, got_base, jumptarget;		\
+	FDPIC_LOAD_FUNCDESC(call_target, call_target)
+
+#elif defined(__PIC__)
 /* The "@PLT" suffix is currently a no-op for non-shared linking, but
    it doesn't hurt to use it conditionally for PIC code in case that
    changes someday.  */
 #define JUMPTARGET(name) name##@PLT
+#define FDPIC_LOAD_FUNCDESC(call_target, funcdesc)
+#define FDPIC_LOAD_JUMPTARGET(call_target, got_base, jumptarget)
 #else
 #define JUMPTARGET(name) name
+#define FDPIC_LOAD_FUNCDESC(call_target, funcdesc)
+#define FDPIC_LOAD_JUMPTARGET(call_target, got_base, jumptarget)
 #endif
 
 #ifndef FRAMESIZE
@@ -153,6 +167,21 @@
 
 #if defined _LIBC_REENTRANT
 # if defined USE___THREAD
+#ifdef __FDPIC__
+#   define SYSCALL_ERROR_ERRNO errno
+#  define SYSCALL_ERROR_HANDLER						      \
+0:	rur	a4, THREADPTR;						      \
+	movi	a3, SYSCALL_ERROR_ERRNO@GOTTPOFF;			      \
+	.reloc	., R_XTENSA_TLS_TPOFF_PTR, SYSCALL_ERROR_ERRNO;		      \
+	add	a3, a3, a11;						      \
+	.reloc	., R_XTENSA_TLS_TPOFF_LOAD, SYSCALL_ERROR_ERRNO;	      \
+	l32i	a3, a3, 0;						      \
+	neg	a2, a2;							      \
+	add	a4, a4, a3;						      \
+	s32i	a2, a4, 0;						      \
+	movi	a2, -1;							      \
+	j	.Lpseudo_end;
+#else
 #   define SYSCALL_ERROR_ERRNO errno
 #  define SYSCALL_ERROR_HANDLER						      \
 0:	rur	a4, THREADPTR;						      \
@@ -162,13 +191,14 @@
 	s32i	a2, a4, 0;						      \
 	movi	a2, -1;							      \
 	j	.Lpseudo_end;
+#endif
 # else /* !USE___THREAD */
 
 #if defined(__XTENSA_WINDOWED_ABI__)
 #  define SYSCALL_ERROR_HANDLER						      \
 0:	neg	a2, a2;							      \
 	mov	a6, a2;							      \
-	movi	a4, __errno_location@PLT;				      \
+	movi	a4, JUMPTARGET(__errno_location);			      \
 	callx4	a4;						              \
 	s32i	a2, a6, 0;						      \
 	movi	a2, -1;							      \
@@ -179,7 +209,8 @@
 	addi	a1, a1, -16;						      \
 	s32i	a0, a1, 0;						      \
 	s32i	a2, a1, 4;						      \
-	movi	a0, __errno_location@PLT;				      \
+	movi	a0, JUMPTARGET(__errno_location);			      \
+	FDPIC_LOAD_JUMPTARGET(a0, a11, a0);				      \
 	callx0	a0;						              \
 	l32i	a0, a1, 0;						      \
 	l32i	a3, a1, 4;						      \
@@ -193,12 +224,23 @@
 
 # endif /* !USE___THREAD */
 #else /* !_LIBC_REENTRANT */
+#ifdef __FDPIC__
+#define SYSCALL_ERROR_HANDLER						      \
+0:	movi	a4, errno@GOT;						      \
+	add	a4, a4, a11;						      \
+	l32i	a4, a4, 0;						      \
+	neg	a2, a2;							      \
+	s32i	a2, a4, 0;						      \
+	movi	a2, -1;							      \
+	j	.Lpseudo_end;
+#else
 #define SYSCALL_ERROR_HANDLER						      \
 0:	movi	a4, errno;						      \
 	neg	a2, a2;							      \
 	s32i	a2, a4, 0;						      \
 	movi	a2, -1;							      \
 	j	.Lpseudo_end;
+#endif /* __FDPIC__ */
 #endif /* _LIBC_REENTRANT */
 
 #endif	/* __ASSEMBLER__ */

+ 2 - 0
libpthread/nptl/sysdeps/unix/sysv/linux/xtensa/Makefile.arch

@@ -9,3 +9,5 @@ CFLAGS-OMIT-fork.c = -DNOT_IN_libc -DIS_IN_libpthread
 
 ASFLAGS-syscall.S = -D_LIBC_REENTRANT
 ASFLAGS-mmap.S = -D_LIBC_REENTRANT
+
+ASFLAGS += -DUSE___THREAD

+ 24 - 9
libpthread/nptl/sysdeps/xtensa/dl-tls.h

@@ -28,6 +28,29 @@ typedef struct
 
 extern void *__tls_get_addr (tls_index *ti);
 
+/* Type used as the argument in a TLS descriptor for a symbol that
+   needs dynamic TLS offsets.  */
+struct tlsdesc_dynamic_arg
+{
+  tls_index tlsinfo;
+  size_t gen_count;
+};
+
+#ifdef __FDPIC__
+/* Type used to represent a TLS descriptor.  */
+struct tlsdesc
+{
+  ptrdiff_t (*entry)(struct tlsdesc *);
+  void *argument;
+};
+
+extern ptrdiff_t attribute_hidden
+  _dl_tlsdesc_return(struct tlsdesc *);
+
+extern void *_dl_make_tlsdesc_dynamic (struct link_map *map, size_t ti_offset);
+extern ptrdiff_t attribute_hidden
+  _dl_tlsdesc_dynamic(struct tlsdesc *);
+#else
 /* Type used to represent a TLS descriptor.  */
 struct tlsdesc
 {
@@ -39,19 +62,11 @@ struct tlsdesc
   ptrdiff_t (*entry)(struct tlsdesc *);
 };
 
-/* Type used as the argument in a TLS descriptor for a symbol that
-   needs dynamic TLS offsets.  */
-struct tlsdesc_dynamic_arg
-{
-  tls_index tlsinfo;
-  size_t gen_count;
-};
-
 extern ptrdiff_t attribute_hidden
   _dl_tlsdesc_return(struct tlsdesc_dynamic_arg *);
 
 extern void *_dl_make_tlsdesc_dynamic (struct link_map *map, size_t ti_offset);
 extern ptrdiff_t attribute_hidden
   _dl_tlsdesc_dynamic(struct tlsdesc_dynamic_arg *);
-
+#endif
 #endif