* New and modified GDB server monitor features:
+ - thread local variables/storage (__thread) can now be displayed.
+
- The GDB server monitor command 'v.info location <address>'
outputs information about an address. The information produced depends
on the tool and on the options given to valgrind.
337528 leak check heuristic for block prefixed by length as 64bit number
338024 inlined functions are not shown if DW_AT_ranges is used
338115 DRD: computed conflict set differs from actual after fork
+338160 implement display of thread local storage in gdbsrv
n-i-bz Fix KVM_CREATE_IRQCHIP ioctl handling
n-i-bz s390x: Fix memory corruption for multithreaded applications
n-i-bz vex arm->IR: allow PC as basereg in some LDRD cases
if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN
valgrind_di_server_LDFLAGS += -Wl,-read_only_relocs -Wl,suppress
endif
+
+#----------------------------------------------------------------------------
+# getoff-<platform>
+# Used to retrieve user space various offsets, using user space libraries.
+#----------------------------------------------------------------------------
+noinst_PROGRAMS = getoff-@VGCONF_ARCH_PRI@-@VGCONF_OS@
+if VGCONF_HAVE_PLATFORM_SEC
+noinst_PROGRAMS += getoff-@VGCONF_ARCH_SEC@-@VGCONF_OS@
+endif
+
+getoff_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES = getoff.c
+getoff_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = $(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
+getoff_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
+getoff_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CCASFLAGS = $(AM_CCASFLAGS_PRI)
+getoff_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = $(AM_CFLAGS_PRI)
+if HAVE_DLINFO_RTLD_DI_TLS_MODID
+getoff_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = $(LDADD) -ldl
+endif
+
+if VGCONF_HAVE_PLATFORM_SEC
+getoff_@VGCONF_ARCH_SEC@_@VGCONF_OS@_SOURCES = getoff.c
+getoff_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CPPFLAGS = $(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
+getoff_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS = $(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
+getoff_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CCASFLAGS = $(AM_CCASFLAGS_SEC)
+getoff_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS = $(AM_CFLAGS_SEC)
+if HAVE_DLINFO_RTLD_DI_TLS_MODID
+getoff_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDADD = $(LDADD) -ldl
+endif
+endif
+
+#----------------------------------------------------------------------------
+# General stuff
+#----------------------------------------------------------------------------
+
+all-local: inplace-noinst_PROGRAMS inplace-noinst_DSYMS
+
+clean-local: clean-noinst_DSYMS
+
+install-exec-local: install-noinst_PROGRAMS install-noinst_DSYMS
+
+uninstall-local: uninstall-noinst_PROGRAMS uninstall-noinst_DSYMS
--- /dev/null
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <link.h>
+#include <dlfcn.h>
+
+/* true if arg matches the provided option */
+static
+int is_opt(char* arg, const char *option)
+{
+ int option_len = strlen(option);
+ if (option[option_len-1] == '=')
+ return (0 == strncmp(option, arg, option_len));
+ else
+ return (0 == strcmp(option, arg));
+}
+
+static int verbose = 0;
+
+static
+void usage (char* progname)
+{
+ fprintf(stderr,
+"Usage: %s [--help] [-h] [-v] [-o <outputfile>]\n"
+"Outputs various user space offsets\n"
+"By default, outputs on stdout.\n"
+"Use -o to output to <outputfile>\n"
+"-v : be more verbose\n",
+progname);
+
+}
+/* Currently, only computes and output lm_modid_offset in struct link_map
+ of the dynamic linker. In theory, we should also compute the offset needed
+ to get the dtv from the thread register/pointer/...
+ Currently, the various valgrind-low-xxxxxx.c files are hardcoding this
+ offset as it is deemed (?) stable, and there is no clear way how to
+ compute this dtv offset.
+*/
+int main (int argc, char** argv)
+{
+ int i;
+ FILE *outputfile;
+ int nr_errors = 0;
+
+ outputfile = stdout;
+
+ i = 1;
+ while (i < argc) {
+ if (is_opt(argv[i], "--help") || is_opt(argv[i], "-h")) {
+ usage(argv[0]);
+ exit(0);
+ } else if (is_opt(argv[i], "-v")) {
+ verbose++;
+ } else if (is_opt(argv[i], "-o")) {
+ if (i+1 == argc) {
+ fprintf(stderr,
+ "missing output file for -o option\n"
+ "Use --help for more information.\n");
+ exit (1);
+ }
+ i++;
+ outputfile = fopen(argv[i], "w");
+ if (outputfile == NULL) {
+ fprintf(stderr, "Could not fopen %s in write mode\n", argv[i]);
+ perror ("fopen output file failed");
+ exit (1);
+ }
+ } else {
+ fprintf (stderr,
+ "unknown or invalid argument %s\n"
+ "Use --help for more information.\n",
+ argv[i]);
+ exit(1);
+ }
+ i++;
+ }
+
+#ifdef HAVE_DLINFO_RTLD_DI_TLS_MODID
+ /* Compute offset of lm_modid in struct link_map.
+ This is needed to support QGetTlsAddr gdbsrv query.
+ Computation is done using an ugly hack, but less ugly than
+ hardcoding the offset depending on the glibc version and
+ platform.
+ The below works, based the assumption that RTLD_DI_TLS_MODID
+ just access and returns directly the field in the dummy
+ link_map structure we have prepared.
+
+ If glibc debug info is installed on your system, you can
+ also find this offset by doing in GDB:
+ p &((struct link_map*)0x0)->l_tls_modid
+ (see also coregrind/m_gdbserver/valgrind_low.h target_get_dtv
+ comments).
+ */
+ {
+ #define MAX_LINKMAP_WORDS 10000
+ size_t dummy_link_map[MAX_LINKMAP_WORDS];
+ size_t off;
+ size_t modid_offset;
+ for (off = 0; off < MAX_LINKMAP_WORDS; off++)
+ dummy_link_map[off] = off;
+ if (dlinfo ((void*)dummy_link_map, RTLD_DI_TLS_MODID,
+ &modid_offset) == 0) {
+ assert(modid_offset >= 0 && modid_offset < MAX_LINKMAP_WORDS);
+ fprintf(outputfile,
+ "lm_modid_offset 0x%x\n", modid_offset*sizeof(size_t));
+ } else {
+ fprintf(stderr,
+ "Error computing lm_modid_offset.\n"
+ "dlinfo error %s\n", dlerror());
+ nr_errors++;
+ }
+ #undef MAX_LINKMAP_WORDS
+ }
+
+ if (outputfile != stdout)
+ if (fclose (outputfile) != 0) {
+ perror ("fclose output file failed\n");
+ nr_errors++;
+ }
+#else
+ if (verbose)
+ fprintf(stderr,
+ "cannot compute lm_modid_offset.\n"
+ "configure did not define HAVE_DLINFO_RTLD_DI_TLS_MODID.\n");
+#endif
+
+ if (nr_errors == 0)
+ exit(0);
+ else
+ exit(1);
+}
test x$ac_have_pthread_create_glibc_2_0 = xyes)
+# Check for dlinfo RTLD_DI_TLS_MODID
+AC_MSG_CHECKING([for dlinfo RTLD_DI_TLS_MODID])
+
+safe_LIBS="$LIBS"
+LIBS="-ldl"
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <link.h>
+#include <dlfcn.h>
+]], [[
+ size_t sizes[10000];
+ size_t modid_offset;
+ (void) dlinfo ((void*)sizes, RTLD_DI_TLS_MODID, &modid_offset);
+ return 0;
+]])], [
+ac_have_dlinfo_rtld_di_tls_modid=yes
+AC_MSG_RESULT([yes])
+AC_DEFINE([HAVE_DLINFO_RTLD_DI_TLS_MODID], 1,
+ [Define to 1 if you have a dlinfo that can do RTLD_DI_TLS_MODID.])
+], [
+ac_have_dlinfo_rtld_di_tls_modid=no
+AC_MSG_RESULT([no])
+])
+LIBS=$safe_LIBS
+
+AM_CONDITIONAL(HAVE_DLINFO_RTLD_DI_TLS_MODID,
+ test x$ac_have_dlinfo_rtld_di_tls_modid = xyes)
+
+
# Check for eventfd_t, eventfd() and eventfd_read()
AC_MSG_CHECKING([for eventfd()])
{
static struct inferior_list_entry *thread_ptr;
+ /* thread local storage query */
+ if (strncmp ("qGetTLSAddr:", arg_own_buf, 12) == 0) {
+ char *from, *to;
+ char *end = arg_own_buf + strlen(arg_own_buf);
+ unsigned long gdb_id;
+ CORE_ADDR lm;
+ CORE_ADDR offset;
+ struct thread_info *ti;
+
+ from = arg_own_buf + 12;
+ to = strchr(from, ',');
+ *to = 0;
+ gdb_id = strtoul (from, NULL, 16);
+ from = to + 1;
+ to = strchr(from, ',');
+ decode_address (&offset, from, to - from);
+ from = to + 1;
+ to = end;
+ decode_address (&lm, from, to - from);
+ dlog(2, "qGetTLSAddr thread %lu offset %p lm %p\n",
+ gdb_id, (void*)offset, (void*)lm);
+
+ ti = gdb_id_to_thread (gdb_id);
+ if (ti != NULL) {
+ ThreadState *tst;
+ Addr tls_addr;
+
+ tst = (ThreadState *) inferior_target_data (ti);
+ if (valgrind_get_tls_addr(tst, offset, lm, &tls_addr)) {
+ VG_(sprintf) (arg_own_buf, "%lx", tls_addr);
+ return;
+ }
+ // else we will report we do not support qGetTLSAddr
+ } else {
+ write_enn (arg_own_buf);
+ return;
+ }
+ }
+
/* qRcmd, monitor command handling. */
if (strncmp ("qRcmd,", arg_own_buf, 6) == 0) {
char *p = arg_own_buf + 6;
return;
}
}
-
+
if (strcmp ("qAttached", arg_own_buf) == 0) {
/* tell gdb to always detach, never kill the process */
arg_own_buf[0] = '1';
return valgrind_point (/* insert*/ False, type, addr, len);
}
+/* Returns the (platform specific) offset of lm_modid field in the link map
+ struct.
+ Stores the offset in *result and returns True if offset can be determined.
+ Returns False otherwise. *result is not to be used then. */
+static Bool getplatformoffset (SizeT *result)
+{
+ static Bool getplatformoffset_called = False;
+
+ static Bool lm_modid_offset_found = False;
+ static SizeT lm_modid_offset = 1<<31; // Rubbish initial value.
+ // lm_modid_offset is a magic offset, retrieved using an external program.
+
+ if (!getplatformoffset_called) {
+ const HChar *platform = VG_PLATFORM;
+ const HChar *cmdformat = "%s/%s-%s -o %s";
+ const HChar *getoff = "getoff";
+ HChar outfile[VG_(mkstemp_fullname_bufsz) (VG_(strlen)(getoff))];
+ Int fd = VG_(mkstemp) (getoff, outfile);
+ HChar cmd[ VG_(strlen)(cmdformat)
+ + VG_(strlen)(VG_(libdir)) - 2
+ + VG_(strlen)(getoff) - 2
+ + VG_(strlen)(platform) - 2
+ + VG_(strlen)(outfile) - 2
+ + 1];
+ UInt cmdlen;
+ struct vg_stat stat_buf;
+ Int ret;
+
+ cmdlen = VG_(snprintf)(cmd, sizeof(cmd),
+ cmdformat,
+ VG_(libdir), getoff, platform, outfile);
+ vg_assert (cmdlen == sizeof(cmd) - 1);
+ ret = VG_(system) (cmd);
+ if (ret != 0 || VG_(debugLog_getLevel)() >= 1)
+ VG_(dmsg) ("command %s exit code %d\n", cmd, ret);
+ ret = VG_(fstat)( fd, &stat_buf );
+ if (ret != 0)
+ VG_(dmsg) ("error VG_(fstat) %d %s\n", fd, outfile);
+ else {
+ HChar *w;
+ HChar *ssaveptr;
+ HChar *os;
+ HChar *str;
+ HChar *endptr;
+
+ os = malloc (stat_buf.size+1);
+ vg_assert (os);
+ ret = VG_(read)(fd, os, stat_buf.size);
+ vg_assert(ret == stat_buf.size);
+ os[ret] = '\0';
+ str = os;
+ while ((w = VG_(strtok_r)(str, " \n", &ssaveptr)) != NULL) {
+ if (VG_(strcmp) (w, "lm_modid_offset") == 0) {
+ w = VG_(strtok_r)(NULL, " \n", &ssaveptr);
+ lm_modid_offset = (SizeT) VG_(strtoull16) ( w, &endptr );
+ if (endptr == w)
+ VG_(dmsg) ("%s lm_modid_offset unexpected hex value %s\n",
+ cmd, w);
+ else
+ lm_modid_offset_found = True;
+ } else {
+ VG_(dmsg) ("%s produced unexpected %s\n", cmd, w);
+ }
+ str = NULL; // ensure next VG_(strtok_r) continues the parsing.
+ }
+ VG_(free) (os);
+ }
+
+ VG_(close)(fd);
+ ret = VG_(unlink)( outfile );
+ if (ret != 0)
+ VG_(umsg) ("error: could not unlink %s\n", outfile);
+ getplatformoffset_called = True;
+ }
+
+ *result = lm_modid_offset;
+ return lm_modid_offset_found;
+}
+
+Bool valgrind_get_tls_addr (ThreadState *tst,
+ CORE_ADDR offset,
+ CORE_ADDR lm,
+ CORE_ADDR *tls_addr)
+{
+ CORE_ADDR **dtv_loc;
+ CORE_ADDR *dtv;
+ SizeT lm_modid_offset;
+ unsigned long int modid;
+
+#define CHECK_DEREF(addr, len, name) \
+ if (!VG_(am_is_valid_for_client) ((Addr)(addr), (len), VKI_PROT_READ)) { \
+ dlog(0, "get_tls_addr: %s at %p len %lu not addressable\n", \
+ name, (void*)(addr), (unsigned long)(len)); \
+ return False; \
+ }
+
+ *tls_addr = 0;
+
+ if (the_low_target.target_get_dtv == NULL) {
+ dlog(1, "low level dtv support not available\n");
+ return False;
+ }
+
+ if (!getplatformoffset (&lm_modid_offset)) {
+ dlog(0, "link_map modid field offset not available\n");
+ return False;
+ }
+ dlog (2, "link_map modid offset %p\n", (void*)lm_modid_offset);
+ vg_assert (lm_modid_offset < 0x10000); // let's say
+
+ dtv_loc = (*the_low_target.target_get_dtv)(tst);
+ if (dtv_loc == NULL) {
+ dlog(0, "low level dtv support returned NULL\n");
+ return False;
+ }
+
+ CHECK_DEREF(dtv_loc, sizeof(CORE_ADDR), "dtv_loc");
+ dtv = *dtv_loc;
+
+ // Check we can read at least 2 address at the beginning of dtv.
+ CHECK_DEREF(dtv, 2*sizeof(CORE_ADDR), "dtv 2 first entries");
+ dlog (2, "tid %d dtv %p\n", tst->tid, (void*)dtv);
+
+ // Check we can read the modid
+ CHECK_DEREF(lm+lm_modid_offset, sizeof(unsigned long int), "link_map modid");
+ modid = *(unsigned long int *)(lm+lm_modid_offset);
+
+ // Check we can access the dtv entry for modid
+ CHECK_DEREF(dtv + 2 * modid, sizeof(CORE_ADDR), "dtv[2*modid]");
+
+ // And finally compute the address of the tls variable.
+ *tls_addr = *(dtv + 2 * modid) + offset;
+
+ return True;
+
+#undef CHECK_DEREF
+}
+
/* returns a pointer to the architecture state corresponding to
the provided register set: 0 => normal guest registers,
1 => shadow1
extern int valgrind_insert_watchpoint (char type, CORE_ADDR addr, int len);
extern int valgrind_remove_watchpoint (char type, CORE_ADDR addr, int len);
+/* Get the address of a thread local variable.
+ 'tst' is the thread for which thread local address is searched for.
+ 'offset' is the offset of the variable in the tls data of the load
+ module identified by 'lm'.
+ 'lm' is the link_map address of the loaded module : it is the address
+ of the data structure used by the dynamic linker to maintain various
+ information about a loaded object.
+
+ Returns True if the address of the variable could be found.
+ *tls_addr is then set to this address.
+ Returns False if tls support is not available for this arch, or
+ if an error occured. *tls_addr is set to NULL. */
+extern Bool valgrind_get_tls_addr (ThreadState *tst,
+ CORE_ADDR offset,
+ CORE_ADDR lm,
+ CORE_ADDR *tls_addr);
+
/* -------------------------------------------------------------------------- */
/* ----------- Utils functions for low level arch specific files ------------ */
/* -------------------------------------------------------------------------- */
+
/* returns a pointer to the architecture state corresponding to
the provided register set: 0 => normal guest registers,
1 => shadow1
VG_(machine_get_VexArchInfo) (&va, &vai);
return (vai.hwcaps & VEX_HWCAPS_AMD64_AVX ? True : False);
}
+
static
const char* target_xml (Bool shadow_mode)
{
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_amd64)
+ return (CORE_ADDR**)(tst->arch.vex.guest_FS_ZERO + 0x8);
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
-1, // Must be computed at init time.
regs,
get_pc,
set_pc,
"amd64",
- target_xml
+ target_xml,
+ target_get_dtv
};
void amd64_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_arm)
+ // arm dtv is pointed to by TPIDRURO
+ return (CORE_ADDR**)(tst->arch.vex.guest_TPIDRURO);
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"arm",
- target_xml
+ target_xml,
+ target_get_dtv
};
void arm_init_architecture (struct valgrind_target_ops *target)
#endif
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_arm64)
+ // arm64 dtv is pointed to by TPIDR_EL0.
+ return (CORE_ADDR**)(tst->arch.vex.guest_TPIDR_EL0);
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"arm64",
- target_xml
+ target_xml,
+ target_get_dtv
};
void arm64_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_mips32)
+ // mips32 dtv location similar to ppc64
+ return (CORE_ADDR**)(tst->arch.vex.guest_ULR - 0x7000 - sizeof(CORE_ADDR));
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"mips",
- target_xml
+ target_xml,
+ target_get_dtv
};
void mips32_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_mips64)
+ // mips64 dtv location similar to ppc64
+ return (CORE_ADDR**)(tst->arch.vex.guest_ULR - 0x7000 - sizeof(CORE_ADDR));
+ return NULL;
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"mips64",
- target_xml
+ target_xml,
+ target_get_dtv
};
void mips64_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_ppc32)
+ // ppc32 dtv is located just before the tcb, which is 0x7000 before
+ // the thread id (r2)
+ return (CORE_ADDR**)(tst->arch.vex.guest_GPR2 - 0x7000 - sizeof(CORE_ADDR));
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"ppc32",
- target_xml
+ target_xml,
+ target_get_dtv
};
void ppc32_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_ppc64be) || defined(VGA_ppc64le)
+ // ppc64 dtv is located just before the tcb, which is 0x7000 before
+ // the thread id (r13)
+ return (CORE_ADDR**)(tst->arch.vex.guest_GPR13 - 0x7000 - sizeof(CORE_ADDR));
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"ppc64",
- target_xml
+ target_xml,
+ target_get_dtv
};
void ppc64_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_s390x)
+ // Thread pointer is in a0 (high 32 bits) and a1. Dtv is the second word.
+ return (CORE_ADDR**)(((CORE_ADDR)tst->arch.vex.guest_a0 << 32
+ | (CORE_ADDR)tst->arch.vex.guest_a1)
+ + sizeof(CORE_ADDR));
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"s390x",
- target_xml
+ target_xml,
+ target_get_dtv
};
void s390x_init_architecture (struct valgrind_target_ops *target)
}
}
+static CORE_ADDR** target_get_dtv (ThreadState *tst)
+{
+#if defined(VGA_x86)
+ // FIXME: should make the below formally visible from VEX.
+ extern ULong x86g_use_seg_selector ( HWord ldt, HWord gdt,
+ UInt seg_selector, UInt virtual_addr );
+
+ ULong dtv_loc_g = x86g_use_seg_selector (tst->arch.vex.guest_LDT,
+ tst->arch.vex.guest_GDT,
+ tst->arch.vex.guest_GS,
+ 0x4);
+ if (dtv_loc_g == 1ULL << 32) {
+ dlog(0, "Error getting x86 dtv\n");
+ return NULL;
+ } else {
+ CORE_ADDR dtv_loc = dtv_loc_g;
+ return (CORE_ADDR**)dtv_loc;
+ }
+#else
+ vg_assert(0);
+#endif
+}
+
static struct valgrind_target_ops low_target = {
num_regs,
regs,
get_pc,
set_pc,
"i386",
- target_xml
+ target_xml,
+ target_get_dtv
};
void x86_init_architecture (struct valgrind_target_ops *target)
Returns NULL if there is no target xml file*/
const char* (*target_xml) (Bool shadow_mode);
+ /* Returns the address in the thread control block where dtv is found.
+ Return NULL if an error occurs or no support for tls/dtv is available.
+ Note that the addressability of the returned result has not been
+ verified. In other words, target_get_dtv just adds some magic
+ offset to the arch specific thread register or thread pointer or ...
+
+ The implementation of this is of course depending on the arch
+ but also depends on the way pthread lib arranges its data.
+ For background info about tls handling, read
+ 'ELF Handling For Thread-Local Storage'
+ http://www.akkadia.org/drepper/tls.pdf
+ (slightly obsolete e.g. the size of a dtv entry is 2 words now).
+ The reference is the glibc source, in particular the arch specific
+ file tls.h.
+
+ For platforms where the dtv is located in the tcb, the magic offset
+ to add to the thread pointer/register/... can be found by doing:
+ cd none/tests
+ gdb ./tls
+ set debug-file-directory /usr/lib/debug # or equivalent
+ start
+ p &((struct pthread*)0x0)->header.dtv
+ Currently the dtv offset is hardcoded, based on the assumption
+ that this is relatively stable. If that would be false, then
+ getoff-<platform> should be modified to output this offset e.g.
+ depending on the glibc version. */
+ CORE_ADDR** (*target_get_dtv)(ThreadState *tst);
+
};
extern void x86_init_architecture (struct valgrind_target_ops *target);
hginfo.stdinB.gdb \
hginfo.stdoutB.exp \
hginfo.vgtest \
+ hgtls.stderrB.exp \
+ hgtls.stderr.exp \
+ hgtls.stdinB.gdb \
+ hgtls.stdoutB.exp \
+ hgtls.vgtest \
mcblocklistsearch.stderr.exp \
mcblocklistsearch.stdinB.gdb \
mcblocklistsearch.vgtest \
--- /dev/null
+# connect gdb to Valgrind gdbserver:
+target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-hgtls
+echo vgdb launched process attached\n
+monitor v.set vgdb-error 999999
+#
+#
+# insert break:
+break tls.c:55
+command
+set $tls_ip = main
+if test == &tests[0]
+ set $tls_ip = &race
+end
+if test == &tests[1]
+ set $tls_ip = &local
+end
+if test == &tests[2]
+ set $tls_ip = &global
+end
+if test == &tests[3]
+ set $tls_ip = &static_extern
+end
+if test == &tests[4]
+ set $tls_ip = &so_extern
+end
+if test == &tests[5]
+ set $tls_ip = &so_local
+end
+if test == &tests[6]
+ set $tls_ip = &global
+end
+printf "test %s tls_ip %p ip %p equal %d\n", test->name, $tls_ip, ip, $tls_ip == ip
+continue
+end
+# continue till the end
+continue
+quit
--- /dev/null
+Breakpoint 1 at 0x........: file tls.c, line 55.
+Continuing.
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests>) at tls.c:55
+55 int here = 0;
+test race tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests>) at tls.c:55
+55 int here = 0;
+test race tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+16>) at tls.c:55
+55 int here = 0;
+test local tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+16>) at tls.c:55
+55 int here = 0;
+test local tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+32>) at tls.c:55
+55 int here = 0;
+test global tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+32>) at tls.c:55
+55 int here = 0;
+test global tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+48>) at tls.c:55
+55 int here = 0;
+test static_extern tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+48>) at tls.c:55
+55 int here = 0;
+test static_extern tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+64>) at tls.c:55
+55 int here = 0;
+test so_extern tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+64>) at tls.c:55
+55 int here = 0;
+test so_extern tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+80>) at tls.c:55
+55 int here = 0;
+test so_local tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+80>) at tls.c:55
+55 int here = 0;
+test so_local tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+96>) at tls.c:55
+55 int here = 0;
+test so_global tls_ip 0x........ ip 0x........ equal 1
+[New Thread ....]
+Breakpoint 1, tls_ptr (p=0x........ <tests+96>) at tls.c:55
+55 int here = 0;
+test so_global tls_ip 0x........ ip 0x........ equal 1
+Program exited normally.
--- /dev/null
+# test tls addresses
+prog: ../none/tests/tls
+vgopts: --tool=helgrind --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-hgtls -q
+prereq: test -e ../none/tests/tls
+stdout_filter: filter_make_empty
+stderr_filter: filter_make_empty
+progB: gdb
+argsB: --quiet -l 60 --nx ../none/tests/tls
+stdinB: hgtls.stdinB.gdb
+stdoutB_filter: filter_gdb
+stderrB_filter: filter_make_empty
+# stderrB_filter_args: hg01_all_ok.c
threadederrno_LDADD = -lpthread
tls_SOURCES = tls.c tls2.c
tls_DEPENDENCIES = tls.so tls2.so
-tls_LDFLAGS = -Wl,-rpath,$(top_builddir)/none/tests
+tls_LDFLAGS = -Wl,-rpath,$(abs_top_builddir)/none/tests
tls_LDADD = tls.so tls2.so -lpthread
tls_so_SOURCES = tls_so.c
tls_so_DEPENDENCIES = tls2.so
tls_so_LDFLAGS = -dynamic -dynamiclib -all_load -fpic
tls_so_LDADD = `pwd`/tls2.so
else
- tls_so_LDFLAGS = -Wl,-rpath,$(top_builddir)/none/tests -shared -fPIC
+ tls_so_LDFLAGS = -Wl,-rpath,$(abs_top_builddir)/none/tests -shared -fPIC
tls_so_LDADD = tls2.so
endif
tls_so_CFLAGS = $(AM_CFLAGS) -fPIC
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;