1 /* 2 This file is part of Valgrind, a dynamic binary instrumentation 3 framework. 4 5 Copyright (C) 2014-2015 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