Philippe Waroquiers bc353dfe97 fix 338160: Implement QGetTlsAddr query so that GDB+V gdbsrv can print __thread variables.
To implement QGetTlsAddr, gdbsrv has to know how to get the glibc dtv
address and the module id from the link_map.
These 2 things are dependent on the internals of glibc.
The dependency is mostly isolated in a few lines of arch dependent
code or in an external utility that used a hack + -ldl lib to find
the offset of the modid in the link_map structure.

Tested on x86/amd64/ppc64/s390x. Somewhat tested on ppc32 and arm64.
Untested/a few #ifdef-ed lines not compiled on arm/mips32/mips64
and darwin.

For more background info about thread local storage handling, see
'ELF Handling For Thread-Local Storage' http://www.akkadia.org/drepper/tls.pdf

Changes:
* auxprogs/getoff.c new auxilliary program to get platform specific offsets
  (currently only the offset for the module id in struct link_map).
* configure.ac : check for dlinfo(RTLD_DI_TLS_MODID) needed for getoff.c
* new gdbserver_tests/hgtls, testing various types of __thread variables
* various m_gdbserver files:
  - implement decoding of the QGetTlsAddr query
  - for each platform: platform specific code to get the dtv
  - call to external program getoff-<platform> the first time an
    __thread variable is printed.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14283
2014-08-15 10:27:52 +00:00

112 lines
1.8 KiB
C

#include <config.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#ifdef HAVE_TLS
#define COUNT 10
static int race;
static __thread int local;
__thread int global;
extern __thread int static_extern;
extern __thread int so_extern;
/* deliberate failure */
static int *test_race(void)
{
return &race;
}
static int *test_local(void)
{
return &local;
}
static int *test_global(void)
{
return &global;
}
static int *test_static_extern(void)
{
return &static_extern;
}
static int *test_so_extern(void)
{
return &so_extern;
}
static const struct timespec awhile = { 0, 200000000 };
typedef int *(*func_t)(void);
struct testcase {
const char *name;
func_t func;
char pad[2 * (8 - sizeof(void*))];
};
static void *tls_ptr(void *p)
{
struct testcase *test = (struct testcase *)p;
int *ip = (*test->func)();
int here = 0;
int i;
for(i = 0; i < COUNT; i++) {
int a = (*ip)++;
int b = here++;
if (a != b)
printf("tls_ptr: case \"%s\" has mismatch: *ip=%d here=%d\n",
test->name, a, b);
nanosleep(&awhile, 0);
}
return 0;
}
int *test_so_extern(void);
int *test_so_local(void);
int *test_so_global(void);
static const struct testcase tests[] = {
#define T(t) { #t, test_##t }
T(race),
T(local),
T(global),
T(static_extern),
T(so_extern),
T(so_local),
T(so_global),
#undef T
};
#define NTESTS (sizeof(tests)/sizeof(*tests))
int main()
{
pthread_t threads[NTESTS*2];
int curthread = 0;
static
int i;
for(i = 0; i < NTESTS; i++) {
pthread_create(&threads[curthread++], NULL, tls_ptr, (void *)&tests[i]);
pthread_create(&threads[curthread++], NULL, tls_ptr, (void *)&tests[i]);
}
for(i = 0; i < curthread; i++)
pthread_join(threads[i], NULL);
return 0;
}
#else
int main()
{
printf("FAILED: no compiler support for __thread\n");
return 1;
}
#endif