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 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/auxprogs/getoff.c b/auxprogs/getoff.c
new file mode 100644
index 0000000..36f5751
--- /dev/null
+++ b/auxprogs/getoff.c
@@ -0,0 +1,139 @@
+#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);
+}