blob: 36f57517f5bb0be7c23006eb85cdb83c5824fd35 [file] [log] [blame]
philippe1670b052014-08-15 10:27:52 +00001#ifndef _GNU_SOURCE
2#define _GNU_SOURCE
3#endif
4#include <config.h>
5
6#include <assert.h>
7#include <errno.h>
8#include <stdlib.h>
9#include <stdio.h>
10#include <string.h>
11
12#include <link.h>
13#include <dlfcn.h>
14
15/* true if arg matches the provided option */
16static
17int is_opt(char* arg, const char *option)
18{
19 int option_len = strlen(option);
20 if (option[option_len-1] == '=')
21 return (0 == strncmp(option, arg, option_len));
22 else
23 return (0 == strcmp(option, arg));
24}
25
26static int verbose = 0;
27
28static
29void usage (char* progname)
30{
31 fprintf(stderr,
32"Usage: %s [--help] [-h] [-v] [-o <outputfile>]\n"
33"Outputs various user space offsets\n"
34"By default, outputs on stdout.\n"
35"Use -o to output to <outputfile>\n"
36"-v : be more verbose\n",
37progname);
38
39}
40/* Currently, only computes and output lm_modid_offset in struct link_map
41 of the dynamic linker. In theory, we should also compute the offset needed
42 to get the dtv from the thread register/pointer/...
43 Currently, the various valgrind-low-xxxxxx.c files are hardcoding this
44 offset as it is deemed (?) stable, and there is no clear way how to
45 compute this dtv offset.
46*/
47int main (int argc, char** argv)
48{
49 int i;
50 FILE *outputfile;
51 int nr_errors = 0;
52
53 outputfile = stdout;
54
55 i = 1;
56 while (i < argc) {
57 if (is_opt(argv[i], "--help") || is_opt(argv[i], "-h")) {
58 usage(argv[0]);
59 exit(0);
60 } else if (is_opt(argv[i], "-v")) {
61 verbose++;
62 } else if (is_opt(argv[i], "-o")) {
63 if (i+1 == argc) {
64 fprintf(stderr,
65 "missing output file for -o option\n"
66 "Use --help for more information.\n");
67 exit (1);
68 }
69 i++;
70 outputfile = fopen(argv[i], "w");
71 if (outputfile == NULL) {
72 fprintf(stderr, "Could not fopen %s in write mode\n", argv[i]);
73 perror ("fopen output file failed");
74 exit (1);
75 }
76 } else {
77 fprintf (stderr,
78 "unknown or invalid argument %s\n"
79 "Use --help for more information.\n",
80 argv[i]);
81 exit(1);
82 }
83 i++;
84 }
85
86#ifdef HAVE_DLINFO_RTLD_DI_TLS_MODID
87 /* Compute offset of lm_modid in struct link_map.
88 This is needed to support QGetTlsAddr gdbsrv query.
89 Computation is done using an ugly hack, but less ugly than
90 hardcoding the offset depending on the glibc version and
91 platform.
92 The below works, based the assumption that RTLD_DI_TLS_MODID
93 just access and returns directly the field in the dummy
94 link_map structure we have prepared.
95
96 If glibc debug info is installed on your system, you can
97 also find this offset by doing in GDB:
98 p &((struct link_map*)0x0)->l_tls_modid
99 (see also coregrind/m_gdbserver/valgrind_low.h target_get_dtv
100 comments).
101 */
102 {
103 #define MAX_LINKMAP_WORDS 10000
104 size_t dummy_link_map[MAX_LINKMAP_WORDS];
105 size_t off;
106 size_t modid_offset;
107 for (off = 0; off < MAX_LINKMAP_WORDS; off++)
108 dummy_link_map[off] = off;
109 if (dlinfo ((void*)dummy_link_map, RTLD_DI_TLS_MODID,
110 &modid_offset) == 0) {
111 assert(modid_offset >= 0 && modid_offset < MAX_LINKMAP_WORDS);
112 fprintf(outputfile,
113 "lm_modid_offset 0x%x\n", modid_offset*sizeof(size_t));
114 } else {
115 fprintf(stderr,
116 "Error computing lm_modid_offset.\n"
117 "dlinfo error %s\n", dlerror());
118 nr_errors++;
119 }
120 #undef MAX_LINKMAP_WORDS
121 }
122
123 if (outputfile != stdout)
124 if (fclose (outputfile) != 0) {
125 perror ("fclose output file failed\n");
126 nr_errors++;
127 }
128#else
129 if (verbose)
130 fprintf(stderr,
131 "cannot compute lm_modid_offset.\n"
132 "configure did not define HAVE_DLINFO_RTLD_DI_TLS_MODID.\n");
133#endif
134
135 if (nr_errors == 0)
136 exit(0);
137 else
138 exit(1);
139}