This is the mail archive of the
libc-alpha@sources.redhat.com
mailing list for the glibc project.
PPC32 TLS support first cut
- From: Paul Mackerras <paulus at samba dot org>
- To: Franz Sirl <Franz dot Sirl-kernel at lauterbach dot com>,Roland McGrath <roland at redhat dot com>, sjmunroe at us dot ibm dot com,amodra at bigpond dot net dot au
- Cc: libc-alpha at sources dot redhat dot com
- Date: Fri, 28 Feb 2003 22:57:32 +1100
- Subject: PPC32 TLS support first cut
Here is a first cut at TLS support in glibc for ppc32. It compiles
and passes all the tests when configured for TLS support.
I haven't checked that it is still OK when configured without TLS
support. I suspect I probably broke it by removing the THREAD_GETMEM
etc. definitions from pt-machine.h (they conflict with the ones in
tls.h).
For the moment I am testing __local_multiple_threads in
sysdep-candel.h for the TLS case because I couldn't get
tcb-offsets.sym to do what I want just yet. Ultimately it would be
better to use an offset from r2 for the multiple_threads test in the
TLS case as well as the non-TLS case.
Also I probably have the TPREL16_* relocations wrong, since I copied
ppc64, which gets these relocs wrong according to Roland.
Comments welcome.
Regards,
Paul.
diff -urN cvs/libc/elf/elf.h libc/elf/elf.h
--- cvs/libc/elf/elf.h 2003-02-26 11:47:45.000000000 +1100
+++ libc/elf/elf.h 2003-02-26 12:02:38.000000000 +1100
@@ -1881,8 +1881,39 @@
#define R_PPC_SECTOFF_LO 34
#define R_PPC_SECTOFF_HI 35
#define R_PPC_SECTOFF_HA 36
+
+/* Relocations for TLS support */
+#define R_PPC_TLS 67
+#define R_PPC_DTPMOD32 68
+#define R_PPC_TPREL16 69
+#define R_PPC_TPREL16_LO 70
+#define R_PPC_TPREL16_HI 71
+#define R_PPC_TPREL16_HA 72
+#define R_PPC_TPREL32 73
+#define R_PPC_DTPREL16 74
+#define R_PPC_DTPREL16_LO 75
+#define R_PPC_DTPREL16_HI 76
+#define R_PPC_DTPREL16_HA 77
+#define R_PPC_DTPREL32 78
+#define R_PPC_GOT_TLSGD16 79
+#define R_PPC_GOT_TLSGD16_LO 80
+#define R_PPC_GOT_TLSGD16_HI 81
+#define R_PPC_GOT_TLSGD16_HA 82
+#define R_PPC_GOT_TLSLD16 83
+#define R_PPC_GOT_TLSLD16_LO 84
+#define R_PPC_GOT_TLSLD16_HI 85
+#define R_PPC_GOT_TLSLD16_HA 86
+#define R_PPC_GOT_TPREL16 87
+#define R_PPC_GOT_TPREL16_LO 88
+#define R_PPC_GOT_TPREL16_HI 89
+#define R_PPC_GOT_TPREL16_HA 90
+#define R_PPC_GOT_DTPREL16 91
+#define R_PPC_GOT_DTPREL16_LO 92
+#define R_PPC_GOT_DTPREL16_HI 93
+#define R_PPC_GOT_DTPREL16_HA 94
+
/* Keep this the last entry. */
-#define R_PPC_NUM 37
+#define R_PPC_NUM 95
/* PowerPC64 relocations defined by the ABIs */
#define R_PPC64_NONE R_PPC_NONE
diff -urN cvs/libc/elf/tls-macros.h libc/elf/tls-macros.h
--- cvs/libc/elf/tls-macros.h 2003-02-26 11:47:45.000000000 +1100
+++ libc/elf/tls-macros.h 2003-02-28 17:42:07.000000000 +1100
@@ -623,6 +623,53 @@
(int *) (__builtin_thread_pointer() + __offset); })
# endif
+#elif defined __powerpc__ && !defined __powerpc64__
+
+# define __TLS_CALL_CLOBBERS \
+ "0", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", \
+ "lr", "ctr", "cr0", "cr1", "cr5", "cr6", "cr7"
+
+/* PowerPC32 Local Exec TLS access. */
+# define TLS_LE(x) \
+ ({ int *__result; \
+ asm ("addi %0,2," #x "@tprel" \
+ : "=r" (__result)); \
+ __result; })
+
+/* PowerPC32 Initial Exec TLS access. */
+# define TLS_IE(x) \
+ ({ int *__result; \
+ asm ("bl _GLOBAL_OFFSET_TABLE_ at local-4\n\t" \
+ "mflr %0\n\t" \
+ "lwz %0," #x "@got at tprel(%0)\n\t" \
+ "add %0,%0," #x "@tls" \
+ : "=b" (__result) : \
+ : "lr"); \
+ __result; })
+
+/* PowerPC32 Local Dynamic TLS access. */
+# define TLS_LD(x) \
+ ({ int *__result; \
+ asm ("bl _GLOBAL_OFFSET_TABLE_ at local-4\n\t" \
+ "mflr 3\n\t" \
+ "addi 3,3," #x "@got at tlsld\n\t" \
+ "bl __tls_get_addr at plt\n\t" \
+ "addi %0,3," #x "@dtprel" \
+ : "=r" (__result) : \
+ : __TLS_CALL_CLOBBERS); \
+ __result; })
+
+/* PowerPC32 General Dynamic TLS access. */
+# define TLS_GD(x) \
+ ({ register int *__result __asm__ ("r3"); \
+ asm ("bl _GLOBAL_OFFSET_TABLE_ at local-4\n\t" \
+ "mflr 3\n\t" \
+ "addi 3,3," #x "@got at tlsgd\n\t" \
+ "bl __tls_get_addr at plt" \
+ : : \
+ : __TLS_CALL_CLOBBERS); \
+ __result; })
+
#elif defined __powerpc__ && defined __powerpc64__
/* PowerPC64 Local Exec TLS access. */
diff -urN cvs/libc/linuxthreads/sysdeps/powerpc/powerpc32/pt-machine.h libc/linuxthreads/sysdeps/powerpc/powerpc32/pt-machine.h
--- cvs/libc/linuxthreads/sysdeps/powerpc/powerpc32/pt-machine.h 2003-02-18 11:15:16.000000000 +1100
+++ libc/linuxthreads/sysdeps/powerpc/powerpc32/pt-machine.h 2003-02-28 22:00:33.000000000 +1100
@@ -57,16 +57,6 @@
/* Initialize the thread-unique value. */
#define INIT_THREAD_SELF(descr, nr) (__thread_self = (descr))
-/* Access to data in the thread descriptor is easy. */
-#define THREAD_GETMEM(descr, member) \
- ((void) (descr), THREAD_SELF->member)
-#define THREAD_GETMEM_NC(descr, member) \
- ((void) (descr), THREAD_SELF->member)
-#define THREAD_SETMEM(descr, member, value) \
- ((void) (descr), THREAD_SELF->member = (value))
-#define THREAD_SETMEM_NC(descr, member, value) \
- ((void) (descr), THREAD_SELF->member = (value))
-
/* Compare-and-swap for semaphores. */
/* note that test-and-set(x) is the same as !compare-and-swap(x, 0, 1) */
diff -urN cvs/libc/linuxthreads/sysdeps/powerpc/tls.h libc/linuxthreads/sysdeps/powerpc/tls.h
--- cvs/libc/linuxthreads/sysdeps/powerpc/tls.h 2003-02-28 08:39:22.000000000 +1100
+++ libc/linuxthreads/sysdeps/powerpc/tls.h 2003-02-28 22:05:02.000000000 +1100
@@ -67,12 +67,12 @@
/* This is the size we need before TCB. */
# define TLS_PRE_TCB_SIZE sizeof (struct _pthread_descr_struct)
-/* The following assumes that TP (R13) is points to the end of the
+/* The following assumes that TP (R2 or R13) is points to the end of the
TCB + 0x7000 (per the ABI). This implies that TCB address is
- R13-(TLS_TCB_SIZE + 0x7000). As we define TLS_DTV_AT_TP we can
+ TP-(TLS_TCB_SIZE + 0x7000). As we define TLS_DTV_AT_TP we can
assume that the pthread_descr is allocated immediately ahead of the
TCB. This implies that the pthread_descr address is
- R13-(TLS_PRE_TCB_SIZE + TLS_TCB_SIZE + 0x7000). */
+ TP-(TLS_PRE_TCB_SIZE + TLS_TCB_SIZE + 0x7000). */
# define TLS_TCB_OFFSET 0x7000
/* The DTV is allocated at the TP; the TCB is placed elsewhere. */
diff -urN cvs/libc/linuxthreads/sysdeps/unix/sysv/linux/powerpc/powerpc32/sysdep-cancel.h libc/linuxthreads/sysdeps/unix/sysv/linux/powerpc/powerpc32/sysdep-cancel.h
--- cvs/libc/linuxthreads/sysdeps/unix/sysv/linux/powerpc/powerpc32/sysdep-cancel.h 2003-02-18 11:15:16.000000000 +1100
+++ libc/linuxthreads/sysdeps/unix/sysv/linux/powerpc/powerpc32/sysdep-cancel.h 2003-02-27 19:57:49.000000000 +1100
@@ -78,19 +78,46 @@
# ifdef IS_IN_libpthread
# define CENABLE bl JUMPTARGET(__pthread_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__pthread_disable_asynccancel)
+# define __local_multiple_threads __pthread_multiple_threads
# else
# define CENABLE bl JUMPTARGET(__libc_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__libc_disable_asynccancel)
+# define __local_multiple_threads __libc_multiple_threads
# endif
-# ifndef __ASSEMBLER__
-# define SINGLE_THREAD_P \
+# if !USE_TLS
+# ifndef __ASSEMBLER__
+# define SINGLE_THREAD_P \
__builtin_expect (THREAD_GETMEM (THREAD_SELF, \
p_header.data.multiple_threads) == 0, 1)
-# else
+# else
# define SINGLE_THREAD_P \
lwz 10,MULTIPLE_THREADS_OFFSET(2); \
cmpwi 10,0
+# endif
+
+# else /* USE_TLS */
+# ifndef __ASSEMBLER__
+extern int __local_multiple_threads attribute_hidden;
+# define SINGLE_THREAD_P __builtin_expect (__local_multiple_threads == 0, 1)
+# else
+# if !defined PIC
+# define SINGLE_THREAD_P \
+ lis 10,__local_multiple_threads at ha; \
+ lwz 10,__local_multiple_threads at l(10); \
+ cmpwi 10,0
+# else
+# define SINGLE_THREAD_P \
+ mflr 9; \
+ bl _GLOBAL_OFFSET_TABLE_ at local-4; \
+ mflr 10; \
+ mtlr 9; \
+ lwz 10,__local_multiple_threads at got(10); \
+ lwz 10,0(10); \
+ cmpwi 10,0
+# endif
+# endif
+
# endif
#elif !defined __ASSEMBLER__
diff -urN cvs/libc/sysdeps/powerpc/powerpc32/dl-machine.c libc/sysdeps/powerpc/powerpc32/dl-machine.c
--- cvs/libc/sysdeps/powerpc/powerpc32/dl-machine.c 2002-11-21 15:10:53.000000000 +1100
+++ libc/sysdeps/powerpc/powerpc32/dl-machine.c 2003-02-28 15:14:05.000000000 +1100
@@ -433,6 +433,12 @@
*(Elf32_Half*) reloc_addr = finaladdr;
break;
+ case R_PPC_TPREL16:
+ if (__builtin_expect (finaladdr > 0x7fff && finaladdr < 0xffff8000, 0))
+ dl_reloc_overflow (map, "R_PPC_TPREL16", reloc_addr, sym, refsym);
+ *(Elf32_Half*) reloc_addr = finaladdr;
+ break;
+
case R_PPC_UADDR16:
if (__builtin_expect (finaladdr > 0x7fff && finaladdr < 0xffff8000, 0))
dl_reloc_overflow (map, "R_PPC_UADDR16", reloc_addr, sym, refsym);
@@ -441,14 +447,17 @@
break;
case R_PPC_ADDR16_LO:
+ case R_PPC_TPREL16_LO:
*(Elf32_Half*) reloc_addr = finaladdr;
break;
case R_PPC_ADDR16_HI:
+ case R_PPC_TPREL16_HI:
*(Elf32_Half*) reloc_addr = finaladdr >> 16;
break;
case R_PPC_ADDR16_HA:
+ case R_PPC_TPREL16_HA:
*(Elf32_Half*) reloc_addr = (finaladdr + 0x8000) >> 16;
break;
diff -urN cvs/libc/sysdeps/powerpc/powerpc32/dl-machine.h libc/sysdeps/powerpc/powerpc32/dl-machine.h
--- cvs/libc/sysdeps/powerpc/powerpc32/dl-machine.h 2003-01-31 07:25:44.000000000 +1100
+++ libc/sysdeps/powerpc/powerpc32/dl-machine.h 2003-02-28 22:32:11.000000000 +1100
@@ -23,6 +23,7 @@
#define ELF_MACHINE_NAME "powerpc"
#include <assert.h>
+#include <dl-tls.h>
/* Return nonzero iff ELF header is compatible with the running host. */
static inline int
@@ -275,11 +276,22 @@
/* We never want to use a PLT entry as the destination of a
reloc, when what is being relocated is a branch. This is
partly for efficiency, but mostly so we avoid loops. */
+#if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
+#define elf_machine_type_class(type) \
+ ((((type) == R_PPC_JMP_SLOT \
+ || (type) == R_PPC_REL24 \
+ || (type) == R_PPC_DTPMOD32 \
+ || (type) == R_PPC_DTPREL32 \
+ || (type) == R_PPC_TPREL32 \
+ || (type) == R_PPC_ADDR24) * ELF_RTYPE_CLASS_PLT) \
+ | (((type) == R_PPC_COPY) * ELF_RTYPE_CLASS_COPY))
+#else
#define elf_machine_type_class(type) \
((((type) == R_PPC_JMP_SLOT \
|| (type) == R_PPC_REL24 \
|| (type) == R_PPC_ADDR24) * ELF_RTYPE_CLASS_PLT) \
| (((type) == R_PPC_COPY) * ELF_RTYPE_CLASS_COPY))
+#endif
/* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
#define ELF_MACHINE_JMP_SLOT R_PPC_JMP_SLOT
@@ -341,13 +353,16 @@
Elf32_Addr *const reloc_addr)
{
const Elf32_Sym *const refsym = sym;
- Elf32_Word finaladdr;
+ Elf32_Word finaladdr, rawvalue;
+ struct link_map *sym_map = NULL;
const int rinfo = ELF32_R_TYPE (reloc->r_info);
#ifndef RESOLVE_CONFLICT_FIND_MAP
if (__builtin_expect (rinfo == R_PPC_NONE, 0))
return;
+ finaladdr = rawvalue = reloc->r_addend;
+
/* The condition on the next two lines is a hack around a bug in Solaris
tools on Sparc. It's not clear whether it should really be here at all,
but if not the binutils need to be changed. */
@@ -357,25 +372,25 @@
{
/* Has already been relocated. */
Elf32_Word loadbase = map->l_addr;
- finaladdr = loadbase + reloc->r_addend;
+ finaladdr += loadbase;
}
else
{
- Elf32_Word loadbase
- = (Elf32_Word) (char *) (RESOLVE (&sym, version,
- ELF32_R_TYPE(reloc->r_info)));
+ Elf32_Word loadbase = 0;
+
+ sym_map = RESOLVE_MAP (&sym, version, rinfo);
+ if (sym_map)
+ loadbase = sym_map->l_addr;
if (sym == NULL)
{
/* Weak symbol that wasn't actually defined anywhere. */
assert (loadbase == 0);
- finaladdr = reloc->r_addend;
}
else
- finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value
- + reloc->r_addend);
+ rawvalue += (Elf32_Word) (char *) sym->st_value;
+ finaladdr = rawvalue + loadbase;
}
#else
- finaladdr = reloc->r_addend;
if (rinfo == R_PPC_JMP_SLOT)
RESOLVE_CONFLICT_FIND_MAP (map, reloc_addr);
#endif
@@ -392,6 +407,44 @@
{
*reloc_addr = finaladdr;
}
+
+#if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
+ else if (rinfo == R_PPC_DTPMOD32)
+ {
+#ifdef RTLD_BOOTSTRAP
+ /* During startup the dynamic linker is always index 1. */
+ *reloc_addr = 1;
+#else
+ /* Get the information from the link map returned by the
+ RESOLVE_MAP function. */
+ if (sym_map != NULL)
+ *reloc_addr = sym_map->l_tls_modid;
+#endif
+ }
+ else if (rinfo == R_PPC_DTPREL32)
+ {
+#ifndef RTLD_BOOTSTRAP
+ /* During relocation all TLS symbols are defined and used.
+ Therefore the offset is already correct. */
+ *reloc_addr = rawvalue - TLS_DTV_OFFSET;
+#endif
+ }
+ else if (rinfo == R_PPC_TPREL32)
+ {
+#ifdef RTLD_BOOTSTRAP
+ *reloc_addr = rawvalue + sym_map->l_tls_offset
+ - TLS_TCB_SIZE - TLS_TP_OFFSET;
+#else
+ if (sym_map)
+ {
+ CHECK_STATIC_TLS (map, sym_map);
+ *reloc_addr = rawvalue + sym_map->l_tls_offset
+ - TLS_TCB_SIZE - TLS_TP_OFFSET;
+ }
+#endif
+ }
+#endif /* USE_TLS etc. */
+
else
__process_machine_rela (map, reloc, sym, refsym,
reloc_addr, finaladdr, rinfo);
diff -urN cvs/libc/sysdeps/powerpc/powerpc32/elf/configure.in libc/sysdeps/powerpc/powerpc32/elf/configure.in
--- cvs/libc/sysdeps/powerpc/powerpc32/elf/configure.in Thu Jan 01 10:00:00 1970
+++ libc/sysdeps/powerpc/powerpc32/elf/configure.in Fri Feb 21 15:08:42 2003
@@ -0,0 +1,42 @@
+GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
+# Local configure fragment for sysdeps/powerpc32/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+AC_CACHE_CHECK(for powerpc32 TLS support, libc_cv_powerpc32_tls, [dnl
+cat > conftest.s <<\EOF
+ .section ".tdata","awT",@progbits
+x: .long 1
+x1: .long 1
+x2: .long 1
+ .text
+ addi 3,31,x at got@tlsgd
+ addi 3,31,x1 at got@tlsld
+ addi 9,3,x1 at dtprel
+ addis 9,3,x2 at dtprel@ha
+ addi 9,9,x2 at dtprel@l
+ lwz 0,x1 at dtprel(3)
+ addis 9,3,x2 at dtprel@ha
+ lwz 0,x2 at dtprel@l(9)
+ lwz 9,x3 at got@tprel(31)
+ add 9,9,x at tls
+ addi 9,2,x1 at tprel
+ addis 9,2,x2 at tprel@ha
+ addi 9,9,x2 at tprel@l
+EOF
+dnl
+if AC_TRY_COMMAND(${CC-cc} -c $CFLAGS conftest.s 1>&AS_MESSAGE_LOG_FD); then
+ libc_cv_powerpc32_tls=yes
+else
+ libc_cv_powerpc32_tls=no
+fi
+rm -f conftest*])
+if test $libc_cv_powerpc32_tls = yes; then
+ AC_DEFINE(HAVE_TLS_SUPPORT)
+fi
+fi
+
+dnl It is always possible to access static and hidden symbols in an
+dnl position independent way.
+AC_DEFINE(PI_STATIC_AND_HIDDEN)