Home | History | Annotate | Download | only in auxprogs
      1 /*
      2    This file is part of Valgrind, a dynamic binary instrumentation
      3    framework.
      4 
      5    Copyright (C) 2014-2017 Philippe Waroquiers
      6 
      7    This program is free software; you can redistribute it and/or
      8    modify it under the terms of the GNU General Public License as
      9    published by the Free Software Foundation; either version 2 of the
     10    License, or (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful, but
     13    WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15    General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program; if not, write to the Free Software
     19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     20    02111-1307, USA.
     21 
     22    The GNU General Public License is contained in the file COPYING.
     23 */
     24 
     25 /* This file is used to generate target executable(s) getoff-<platform>
     26    In a bi-arch setup, this is used to build 2 executables
     27    (for the primary and secondary platforms).
     28 
     29    This program uses user space libraries to retrieve some platform
     30    dependent offsets needed for Valgrind core, but that cannot (easily)
     31    be retrieved by Valgrind core.
     32 
     33    This is currently used only for handling the gdbsrv QGetTlsAddr query :
     34    it only computes and outputs lm_modid_offset in struct link_map
     35    of the dynamic linker. In theory, we should also compute the offset needed
     36    to get the dtv from the thread register/pointer/...
     37    Currently, the various valgrind-low-xxxxxx.c files are hardcoding this
     38    offset as it is deemed (?) stable, and there is no clear way how to
     39    compute this dtv offset.
     40 
     41    The getoff-<platform> executable will be launched automatically by
     42    Valgrind gdbserver when the first QGetTlsAddr query is retrieved.
     43 
     44    On plaforms that do not support __thread and/or that do not provide
     45    dlinfo RTLD_DI_TLS_MODID, this executable produces no output. */
     46 
     47 #ifndef _GNU_SOURCE
     48 #define _GNU_SOURCE
     49 #endif
     50 #include <config.h>
     51 
     52 #include <assert.h>
     53 #include <errno.h>
     54 #include <stdlib.h>
     55 #include <stdio.h>
     56 #include <string.h>
     57 
     58 #ifdef HAVE_DLINFO_RTLD_DI_TLS_MODID
     59 #include <link.h>
     60 #include <dlfcn.h>
     61 #endif
     62 
     63 /* true if arg matches the provided option */
     64 static
     65 int is_opt(char* arg, const char *option)
     66 {
     67    int option_len = strlen(option);
     68    if (option[option_len-1] == '=')
     69       return (0 == strncmp(option, arg, option_len));
     70    else
     71       return (0 == strcmp(option, arg));
     72 }
     73 
     74 static int verbose = 0;
     75 
     76 static
     77 void usage (char* progname)
     78 {
     79    fprintf(stderr,
     80 "Usage: %s [--help] [-h] [-v] [-o <outputfile>]\n"
     81 "Outputs various user space offsets\n"
     82 "By default, outputs on stdout.\n"
     83 "Use -o to output to <outputfile>\n"
     84 "-v : be more verbose\n",
     85 progname);
     86 
     87 }
     88 
     89 int main (int argc, char** argv)
     90 {
     91    int i;
     92    FILE *outputfile;
     93    int nr_errors = 0;
     94 
     95    outputfile = stdout;
     96 
     97    i = 1;
     98    while (i < argc) {
     99       if (is_opt(argv[i], "--help") || is_opt(argv[i], "-h")) {
    100          usage(argv[0]);
    101          exit(0);
    102       } else if (is_opt(argv[i], "-v")) {
    103          verbose++;
    104       } else if (is_opt(argv[i], "-o")) {
    105          if (i+1 == argc) {
    106             fprintf(stderr,
    107                     "missing output file for -o option\n"
    108                     "Use --help for more information.\n");
    109             exit (1);
    110          }
    111          i++;
    112          outputfile = fopen(argv[i], "w");
    113          if (outputfile == NULL) {
    114             fprintf(stderr, "Could not fopen %s in write mode\n", argv[i]);
    115             perror ("fopen output file failed");
    116             exit (1);
    117          }
    118       } else {
    119          fprintf (stderr,
    120                   "unknown or invalid argument %s\n"
    121                   "Use --help for more information.\n",
    122                   argv[i]);
    123          exit(1);
    124       }
    125       i++;
    126    }
    127 
    128 #ifdef HAVE_DLINFO_RTLD_DI_TLS_MODID
    129    /* Compute offset of lm_modid in struct link_map.
    130       This is needed to support QGetTlsAddr gdbsrv query.
    131       Computation is done using an ugly hack, but less ugly than
    132       hardcoding the offset depending on the glibc version and
    133       platform.
    134       The below works, based the assumption that RTLD_DI_TLS_MODID
    135       just access and returns directly the field in the dummy
    136       link_map structure we have prepared.
    137 
    138       If glibc debug info is installed on your system, you can
    139       also find this offset by doing in GDB:
    140           p &((struct link_map*)0x0)->l_tls_modid
    141       (see also coregrind/m_gdbserver/valgrind_low.h target_get_dtv
    142        comments).
    143    */
    144    {
    145       #define MAX_LINKMAP_WORDS 10000
    146       size_t dummy_link_map[MAX_LINKMAP_WORDS];
    147       size_t off;
    148       size_t modid_offset;
    149       for (off = 0; off < MAX_LINKMAP_WORDS; off++)
    150          dummy_link_map[off] = off;
    151       if (dlinfo ((void*)dummy_link_map, RTLD_DI_TLS_MODID,
    152                   &modid_offset) == 0) {
    153          assert(modid_offset >= 0 && modid_offset < MAX_LINKMAP_WORDS);
    154          fprintf(outputfile,
    155                  "lm_modid_offset 0x%zx\n", modid_offset*sizeof(size_t));
    156       } else {
    157          fprintf(stderr,
    158                  "Error computing lm_modid_offset.\n"
    159                  "dlinfo error %s\n", dlerror());
    160          nr_errors++;
    161       }
    162       #undef MAX_LINKMAP_WORDS
    163    }
    164 
    165    if (outputfile != stdout)
    166       if (fclose (outputfile) != 0) {
    167          perror ("fclose output file failed\n");
    168          nr_errors++;
    169       }
    170 #else
    171    if (verbose)
    172       fprintf(stderr,
    173               "cannot compute lm_modid_offset.\n"
    174               "configure did not define HAVE_DLINFO_RTLD_DI_TLS_MODID.\n");
    175 #endif
    176 
    177    if (nr_errors == 0)
    178       exit(0);
    179    else
    180       exit(1);
    181 }
    182