diff mbox series

[uclibc-ng-devel,RFC,1/2] xtensa: add FDPIC support

Message ID 20240501120553.1124504-2-jcmvbkbc@gmail.com
State New
Headers show
Series xtensa: add FDPIC ABI support | expand

Commit Message

Max Filippov May 1, 2024, 12:05 p.m. UTC
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>
---
The toolchain components matching the libc are available in
the following repositories with the tag xtensa-fdpic-abi-spec-1.1:

 https://github.com/jcmvbkbc/binutils-gdb-xtensa/tree/xtensa-fdpic-abi-spec-1.1
 https://github.com/jcmvbkbc/gcc-xtensa/tree/xtensa-fdpic-abi-spec-1.1
---
 Rules.mak                                   |   4 +
 extra/Configs/Config.in                     |   2 +-
 extra/Configs/Config.in.arch                |   2 +-
 include/elf.h                               |   6 +-
 ldso/ldso/xtensa/dl-inlines.h               |   1 +
 ldso/ldso/xtensa/dl-startup.h               |  91 ++++++++++++++-
 ldso/ldso/xtensa/dl-sysdep.h                |  66 ++++++++++-
 ldso/ldso/xtensa/dl-tlsdesc.S               |  10 +-
 ldso/ldso/xtensa/elfinterp.c                | 114 ++++++++++++++-----
 libc/sysdeps/linux/xtensa/__start_context.S |   6 +-
 libc/sysdeps/linux/xtensa/bits/elf-fdpic.h  | 117 ++++++++++++++++++++
 libc/sysdeps/linux/xtensa/clone.S           |   6 +
 libc/sysdeps/linux/xtensa/crt1.S            |  81 ++++++++++++++
 libc/sysdeps/linux/xtensa/crti.S            |   8 ++
 libc/sysdeps/linux/xtensa/crtn.S            |   6 +
 libc/sysdeps/linux/xtensa/crtreloc.c        | 105 ++++++++++++++++++
 libc/sysdeps/linux/xtensa/getcontext.S      |   1 +
 libc/sysdeps/linux/xtensa/makecontext.c     |   5 +
 libc/sysdeps/linux/xtensa/setcontext.S      |   1 +
 libc/sysdeps/linux/xtensa/setjmp.S          |   3 +-
 libc/sysdeps/linux/xtensa/swapcontext.S     |   1 +
 libc/sysdeps/linux/xtensa/sysdep.h          |  48 +++++++-
 libpthread/nptl/sysdeps/xtensa/dl-tls.h     |  33 ++++--
 23 files changed, 671 insertions(+), 46 deletions(-)
 create mode 100644 ldso/ldso/xtensa/dl-inlines.h
 create mode 100644 libc/sysdeps/linux/xtensa/bits/elf-fdpic.h
 create mode 100644 libc/sysdeps/linux/xtensa/crtreloc.c
diff mbox series

Patch

diff --git a/Rules.mak b/Rules.mak
index 5be74b86edd4..70d681238b24 100644
--- a/Rules.mak
+++ b/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),)
diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in
index b814449b43a5..be63eae60d3a 100644
--- a/extra/Configs/Config.in
+++ b/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.
 
diff --git a/extra/Configs/Config.in.arch b/extra/Configs/Config.in.arch
index 1ae5134b9df5..c13497893414 100644
--- a/extra/Configs/Config.in.arch
+++ b/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"
diff --git a/include/elf.h b/include/elf.h
index c2efa9978677..1e7c89615a44 100644
--- a/include/elf.h
+++ b/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
diff --git a/ldso/ldso/xtensa/dl-inlines.h b/ldso/ldso/xtensa/dl-inlines.h
new file mode 100644
index 000000000000..8fdf6eb48679
--- /dev/null
+++ b/ldso/ldso/xtensa/dl-inlines.h
@@ -0,0 +1 @@ 
+#include "../fdpic/dl-inlines.h"
diff --git a/ldso/ldso/xtensa/dl-startup.h b/ldso/ldso/xtensa/dl-startup.h
index c9350c0f201e..2a453752acaf 100644
--- a/ldso/ldso/xtensa/dl-startup.h
+++ b/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__ */
diff --git a/ldso/ldso/xtensa/dl-sysdep.h b/ldso/ldso/xtensa/dl-sysdep.h
index 6b908989a8f1..5aa3e177f3a1 100644
--- a/ldso/ldso/xtensa/dl-sysdep.h
+++ b/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
diff --git a/ldso/ldso/xtensa/dl-tlsdesc.S b/ldso/ldso/xtensa/dl-tlsdesc.S
index 426f2180b3c0..1a8eacff20cc 100644
--- a/ldso/ldso/xtensa/dl-tlsdesc.S
+++ b/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
diff --git a/ldso/ldso/xtensa/elfinterp.c b/ldso/ldso/xtensa/elfinterp.c
index e38a02666477..d97f234353e8 100644
--- a/ldso/ldso/xtensa/elfinterp.c
+++ b/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
diff --git a/libc/sysdeps/linux/xtensa/__start_context.S b/libc/sysdeps/linux/xtensa/__start_context.S
index a30d7b6188ad..e6ce93347d6d 100644
--- a/libc/sysdeps/linux/xtensa/__start_context.S
+++ b/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)
diff --git a/libc/sysdeps/linux/xtensa/bits/elf-fdpic.h b/libc/sysdeps/linux/xtensa/bits/elf-fdpic.h
new file mode 100644
index 000000000000..19bb247b878e
--- /dev/null
+++ b/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 */
diff --git a/libc/sysdeps/linux/xtensa/clone.S b/libc/sysdeps/linux/xtensa/clone.S
index ebfdcc1f65be..a11044cd0271 100644
--- a/libc/sysdeps/linux/xtensa/clone.S
+++ b/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
diff --git a/libc/sysdeps/linux/xtensa/crt1.S b/libc/sysdeps/linux/xtensa/crt1.S
index 3fa14ae583a9..a12f82dd656b 100644
--- a/libc/sysdeps/linux/xtensa/crt1.S
+++ b/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__) */
diff --git a/libc/sysdeps/linux/xtensa/crti.S b/libc/sysdeps/linux/xtensa/crti.S
index 43e66e30820b..2923ff09d0bb 100644
--- a/libc/sysdeps/linux/xtensa/crti.S
+++ b/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
diff --git a/libc/sysdeps/linux/xtensa/crtn.S b/libc/sysdeps/linux/xtensa/crtn.S
index a3598da1a537..6f797e8bdd60 100644
--- a/libc/sysdeps/linux/xtensa/crtn.S
+++ b/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
diff --git a/libc/sysdeps/linux/xtensa/crtreloc.c b/libc/sysdeps/linux/xtensa/crtreloc.c
new file mode 100644
index 000000000000..697ef91ab21f
--- /dev/null
+++ b/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__ */
diff --git a/libc/sysdeps/linux/xtensa/getcontext.S b/libc/sysdeps/linux/xtensa/getcontext.S
index 7588a91b33b3..4cc644552d42 100644
--- a/libc/sysdeps/linux/xtensa/getcontext.S
+++ b/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__)
diff --git a/libc/sysdeps/linux/xtensa/makecontext.c b/libc/sysdeps/linux/xtensa/makecontext.c
index da26a0130325..0a8f7116fef2 100644
--- a/libc/sysdeps/linux/xtensa/makecontext.c
+++ b/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;
diff --git a/libc/sysdeps/linux/xtensa/setcontext.S b/libc/sysdeps/linux/xtensa/setcontext.S
index 4df7cc049006..72915ef8d74e 100644
--- a/libc/sysdeps/linux/xtensa/setcontext.S
+++ b/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
 
diff --git a/libc/sysdeps/linux/xtensa/setjmp.S b/libc/sysdeps/linux/xtensa/setjmp.S
index b8152fdd8bac..d629c11a8c41 100644
--- a/libc/sysdeps/linux/xtensa/setjmp.S
+++ b/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
diff --git a/libc/sysdeps/linux/xtensa/swapcontext.S b/libc/sysdeps/linux/xtensa/swapcontext.S
index a215edc6d54a..40b38e98ca01 100644
--- a/libc/sysdeps/linux/xtensa/swapcontext.S
+++ b/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
 
diff --git a/libc/sysdeps/linux/xtensa/sysdep.h b/libc/sysdeps/linux/xtensa/sysdep.h
index 80b3f30fcc5d..c527d163f66d 100644
--- a/libc/sysdeps/linux/xtensa/sysdep.h
+++ b/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__ */
diff --git a/libpthread/nptl/sysdeps/xtensa/dl-tls.h b/libpthread/nptl/sysdeps/xtensa/dl-tls.h
index adc02d74a55c..5fc4ea2d05c8 100644
--- a/libpthread/nptl/sysdeps/xtensa/dl-tls.h
+++ b/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