philippe | e004ad8 | 2014-08-16 21:00:15 +0000 | [diff] [blame] | 1 | /* |
| 2 | This file is part of Valgrind, a dynamic binary instrumentation |
| 3 | framework. |
| 4 | |
Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 5 | Copyright (C) 2014-2017 Philippe Waroquiers |
philippe | e004ad8 | 2014-08-16 21:00:15 +0000 | [diff] [blame] | 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 | |
philippe | 8c98ebc | 2014-08-17 13:33:57 +0000 | [diff] [blame] | 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 | |
philippe | 1670b05 | 2014-08-15 10:27:52 +0000 | [diff] [blame] | 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 | |
philippe | e004ad8 | 2014-08-16 21:00:15 +0000 | [diff] [blame] | 58 | #ifdef HAVE_DLINFO_RTLD_DI_TLS_MODID |
philippe | 1670b05 | 2014-08-15 10:27:52 +0000 | [diff] [blame] | 59 | #include <link.h> |
| 60 | #include <dlfcn.h> |
philippe | e004ad8 | 2014-08-16 21:00:15 +0000 | [diff] [blame] | 61 | #endif |
philippe | 1670b05 | 2014-08-15 10:27:52 +0000 | [diff] [blame] | 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 | } |
philippe | 8c98ebc | 2014-08-17 13:33:57 +0000 | [diff] [blame] | 88 | |
philippe | 1670b05 | 2014-08-15 10:27:52 +0000 | [diff] [blame] | 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, |
florian | ee0ee03 | 2014-09-02 14:21:25 +0000 | [diff] [blame] | 155 | "lm_modid_offset 0x%zx\n", modid_offset*sizeof(size_t)); |
philippe | 1670b05 | 2014-08-15 10:27:52 +0000 | [diff] [blame] | 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 | } |