blob: 8f1e239a949eb7203e8ed15a872ad90c4b9da14c [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* Locate source files or functions which caused text relocations.
2 Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc.
3 This file is part of elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 elfutils is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#include <argp.h>
24#include <assert.h>
25#include <errno.h>
26#include <error.h>
27#include <fcntl.h>
28#include <gelf.h>
29#include <libdw.h>
30#include <libintl.h>
31#include <locale.h>
32#include <search.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include <printversion.h>
40
41
42struct segments
43{
44 GElf_Addr from;
45 GElf_Addr to;
46};
47
48
49/* Name and version of program. */
50ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
51
52/* Bug report address. */
53ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
54
55/* Values for the parameters which have no short form. */
56#define OPT_DEBUGINFO 0x100
57
58/* Definitions of arguments for argp functions. */
59static const struct argp_option options[] =
60{
61 { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
62 { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
63 { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
64 N_("Use PATH as root of debuginfo hierarchy"), 0 },
65
66 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
67 { NULL, 0, NULL, 0, NULL, 0 }
68};
69
70/* Short description of program. */
71static const char doc[] = N_("\
72Locate source of text relocations in FILEs (a.out by default).");
73
74/* Strings for arguments in help texts. */
75static const char args_doc[] = N_("[FILE...]");
76
77/* Prototype for option handler. */
78static error_t parse_opt (int key, char *arg, struct argp_state *state);
79
80/* Data structure to communicate with argp functions. */
81static struct argp argp =
82{
83 options, parse_opt, args_doc, doc, NULL, NULL, NULL
84};
85
86
87/* Print symbols in file named FNAME. */
88static int process_file (const char *fname, bool more_than_one);
89
90/* Check for text relocations in the given file. The segment
91 information is known. */
92static void check_rel (size_t nsegments, struct segments segments[nsegments],
93 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
94 const char *fname, bool more_than_one,
95 void **knownsrcs);
96
97
98
99/* User-provided root directory. */
100static const char *rootdir = "/";
101
102/* Root of debuginfo directory hierarchy. */
103static const char *debuginfo_root;
104
105
106int
107main (int argc, char *argv[])
108{
109 int remaining;
110 int result = 0;
111
112 /* Set locale. */
113 (void) setlocale (LC_ALL, "");
114
115 /* Make sure the message catalog can be found. */
116 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
117
118 /* Initialize the message catalog. */
119 (void) textdomain (PACKAGE_TARNAME);
120
121 /* Parse and process arguments. */
122 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
123
124 /* Tell the library which version we are expecting. */
125 elf_version (EV_CURRENT);
126
127 /* If the user has not specified the root directory for the
128 debuginfo hierarchy, we have to determine it ourselves. */
129 if (debuginfo_root == NULL)
130 {
131 // XXX The runtime should provide this information.
132#if defined __ia64__ || defined __alpha__
133 debuginfo_root = "/usr/lib/debug";
134#else
135 debuginfo_root = (sizeof (long int) == 4
136 ? "/usr/lib/debug" : "/usr/lib64/debug");
137#endif
138 }
139
140 if (remaining == argc)
141 result = process_file ("a.out", false);
142 else
143 {
144 /* Process all the remaining files. */
145 const bool more_than_one = remaining + 1 < argc;
146
147 do
148 result |= process_file (argv[remaining], more_than_one);
149 while (++remaining < argc);
150 }
151
152 return result;
153}
154
155
156/* Handle program arguments. */
157static error_t
158parse_opt (int key, char *arg,
159 struct argp_state *state __attribute__ ((unused)))
160{
161 switch (key)
162 {
163 case 'r':
164 rootdir = arg;
165 break;
166
167 case OPT_DEBUGINFO:
168 debuginfo_root = arg;
169 break;
170
171 default:
172 return ARGP_ERR_UNKNOWN;
173 }
174 return 0;
175}
176
177
178static void
179noop (void *arg __attribute__ ((unused)))
180{
181}
182
183
184static int
185process_file (const char *fname, bool more_than_one)
186{
187 int result = 0;
188 void *knownsrcs = NULL;
189
190 size_t fname_len = strlen (fname);
191 size_t rootdir_len = strlen (rootdir);
192 const char *real_fname = fname;
193 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
194 {
195 /* Prepend the user-provided root directory. */
196 char *new_fname = alloca (rootdir_len + fname_len + 2);
197 *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
198 "/"),
199 fname, fname_len)) = '\0';
200 real_fname = new_fname;
201 }
202
203 int fd = open (real_fname, O_RDONLY);
204 if (fd == -1)
205 {
206 error (0, errno, gettext ("cannot open '%s'"), fname);
207 return 1;
208 }
209
210 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
211 if (elf == NULL)
212 {
213 error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
214 fname, elf_errmsg (-1));
215 goto err_close;
216 }
217
218 /* Make sure the file is a DSO. */
219 GElf_Ehdr ehdr_mem;
220 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
221 if (ehdr == NULL)
222 {
223 error (0, 0, gettext ("cannot get ELF header '%s': %s"),
224 fname, elf_errmsg (-1));
225 err_elf_close:
226 elf_end (elf);
227 err_close:
228 close (fd);
229 return 1;
230 }
231
232 if (ehdr->e_type != ET_DYN)
233 {
234 error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname);
235 goto err_elf_close;
236 }
237
238 /* Determine whether the DSO has text relocations at all and locate
239 the symbol table. */
240 Elf_Scn *symscn = NULL;
241 Elf_Scn *scn = NULL;
242 bool seen_dynamic = false;
243 bool have_textrel = false;
244 while ((scn = elf_nextscn (elf, scn)) != NULL
245 && (!seen_dynamic || symscn == NULL))
246 {
247 /* Handle the section if it is a symbol table. */
248 GElf_Shdr shdr_mem;
249 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
250
251 if (shdr == NULL)
252 {
253 error (0, 0,
254 gettext ("getting get section header of section %zu: %s"),
255 elf_ndxscn (scn), elf_errmsg (-1));
256 goto err_elf_close;
257 }
258
259 switch (shdr->sh_type)
260 {
261 case SHT_DYNAMIC:
262 if (!seen_dynamic)
263 {
264 seen_dynamic = true;
265
266 Elf_Data *data = elf_getdata (scn, NULL);
267
268 for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize;
269 ++cnt)
270 {
271 GElf_Dyn dynmem;
272 GElf_Dyn *dyn;
273
274 dyn = gelf_getdyn (data, cnt, &dynmem);
275 if (dyn == NULL)
276 {
277 error (0, 0, gettext ("cannot read dynamic section: %s"),
278 elf_errmsg (-1));
279 goto err_elf_close;
280 }
281
282 if (dyn->d_tag == DT_TEXTREL
283 || (dyn->d_tag == DT_FLAGS
284 && (dyn->d_un.d_val & DF_TEXTREL) != 0))
285 have_textrel = true;
286 }
287 }
288 break;
289
290 case SHT_SYMTAB:
291 symscn = scn;
292 break;
293 }
294 }
295
296 if (!have_textrel)
297 {
298 error (0, 0, gettext ("no text relocations reported in '%s'"), fname);
299 goto err_elf_close;
300 }
301
302 int fd2 = -1;
303 Elf *elf2 = NULL;
304 /* Get the address ranges for the loaded segments. */
305 size_t nsegments_max = 10;
306 size_t nsegments = 0;
307 struct segments *segments
308 = (struct segments *) malloc (nsegments_max * sizeof (segments[0]));
309 if (segments == NULL)
310 error (1, errno, gettext ("while reading ELF file"));
311
312 size_t phnum;
313 if (elf_getphdrnum (elf, &phnum) != 0)
314 error (1, 0, gettext ("cannot get program header count: %s"),
315 elf_errmsg (-1));
316
317
318 for (size_t i = 0; i < phnum; ++i)
319 {
320 GElf_Phdr phdr_mem;
321 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
322 if (phdr == NULL)
323 {
324 error (0, 0,
325 gettext ("cannot get program header index at offset %zd: %s"),
326 i, elf_errmsg (-1));
327 result = 1;
328 goto next;
329 }
330
331 if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
332 {
333 if (nsegments == nsegments_max)
334 {
335 nsegments_max *= 2;
336 segments
337 = (struct segments *) realloc (segments,
338 nsegments_max
339 * sizeof (segments[0]));
340 if (segments == NULL)
341 {
342 error (0, 0, gettext ("\
343cannot get program header index at offset %zd: %s"),
344 i, elf_errmsg (-1));
345 result = 1;
346 goto next;
347 }
348 }
349
350 segments[nsegments].from = phdr->p_vaddr;
351 segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
352 ++nsegments;
353 }
354 }
355
356 if (nsegments > 0)
357 {
358
359 Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
360 /* Look for debuginfo files if the information is not the in
361 opened file itself. This makes only sense if the input file
362 is specified with an absolute path. */
363 if (dw == NULL && fname[0] == '/')
364 {
365 size_t debuginfo_rootlen = strlen (debuginfo_root);
366 char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
367 + fname_len + 8);
368 strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
369 rootdir_len),
370 debuginfo_root,
371 debuginfo_rootlen),
372 "/"),
373 fname, fname_len),
374 ".debug");
375
376 fd2 = open (difname, O_RDONLY);
377 if (fd2 != -1
378 && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
379 dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
380 }
381
382 /* Look at all relocations and determine which modify
383 write-protected segments. */
384 scn = NULL;
385 while ((scn = elf_nextscn (elf, scn)) != NULL)
386 {
387 /* Handle the section if it is a symbol table. */
388 GElf_Shdr shdr_mem;
389 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
390
391 if (shdr == NULL)
392 {
393 error (0, 0,
394 gettext ("cannot get section header of section %zu: %s"),
395 elf_ndxscn (scn), elf_errmsg (-1));
396 result = 1;
397 goto next;
398 }
399
400 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
401 && symscn == NULL)
402 {
403 symscn = elf_getscn (elf, shdr->sh_link);
404 if (symscn == NULL)
405 {
406 error (0, 0, gettext ("\
407cannot get symbol table section %zu in '%s': %s"),
408 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
409 result = 1;
410 goto next;
411 }
412 }
413
414 if (shdr->sh_type == SHT_REL)
415 {
416 Elf_Data *data = elf_getdata (scn, NULL);
417
418 for (int cnt = 0;
419 (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
420 ++cnt)
421 {
422 GElf_Rel rel_mem;
423 GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
424 if (rel == NULL)
425 {
426 error (0, 0, gettext ("\
427cannot get relocation at index %d in section %zu in '%s': %s"),
428 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
429 result = 1;
430 goto next;
431 }
432
433 check_rel (nsegments, segments, rel->r_offset, elf,
434 symscn, dw, fname, more_than_one, &knownsrcs);
435 }
436 }
437 else if (shdr->sh_type == SHT_RELA)
438 {
439 Elf_Data *data = elf_getdata (scn, NULL);
440
441 for (int cnt = 0;
442 (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
443 ++cnt)
444 {
445 GElf_Rela rela_mem;
446 GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
447 if (rela == NULL)
448 {
449 error (0, 0, gettext ("\
450cannot get relocation at index %d in section %zu in '%s': %s"),
451 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
452 result = 1;
453 goto next;
454 }
455
456 check_rel (nsegments, segments, rela->r_offset, elf,
457 symscn, dw, fname, more_than_one, &knownsrcs);
458 }
459 }
460 }
461
462 dwarf_end (dw);
463 }
464
465 next:
466 elf_end (elf);
467 elf_end (elf2);
468 close (fd);
469 if (fd2 != -1)
470 close (fd2);
471
472 free (segments);
473 tdestroy (knownsrcs, noop);
474
475 return result;
476}
477
478
479static int
480ptrcompare (const void *p1, const void *p2)
481{
482 if ((uintptr_t) p1 < (uintptr_t) p2)
483 return -1;
484 if ((uintptr_t) p1 > (uintptr_t) p2)
485 return 1;
486 return 0;
487}
488
489
490static void
491check_rel (size_t nsegments, struct segments segments[nsegments],
492 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
493 const char *fname, bool more_than_one, void **knownsrcs)
494{
495 for (size_t cnt = 0; cnt < nsegments; ++cnt)
496 if (segments[cnt].from <= addr && segments[cnt].to > addr)
497 {
498 Dwarf_Die die_mem;
499 Dwarf_Die *die;
500 Dwarf_Line *line;
501 const char *src;
502
503 if (more_than_one)
504 printf ("%s: ", fname);
505
506 if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
507 && (line = dwarf_getsrc_die (die, addr)) != NULL
508 && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
509 {
510 /* There can be more than one relocation against one file.
511 Try to avoid multiple messages. And yes, the code uses
512 pointer comparison. */
513 if (tfind (src, knownsrcs, ptrcompare) == NULL)
514 {
515 printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src);
516 tsearch (src, knownsrcs, ptrcompare);
517 }
518 return;
519 }
520 else
521 {
522 /* At least look at the symbol table to see which function
523 the modified address is in. */
524 Elf_Data *symdata = elf_getdata (symscn, NULL);
525 GElf_Shdr shdr_mem;
526 GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
527 if (shdr != NULL)
528 {
529 GElf_Addr lowaddr = 0;
530 int lowidx = -1;
531 GElf_Addr highaddr = ~0ul;
532 int highidx = -1;
533 GElf_Sym sym_mem;
534 GElf_Sym *sym;
535
536 for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize;
537 ++i)
538 {
539 sym = gelf_getsym (symdata, i, &sym_mem);
540 if (sym == NULL)
541 continue;
542
543 if (sym->st_value < addr && sym->st_value > lowaddr)
544 {
545 lowaddr = sym->st_value;
546 lowidx = i;
547 }
548 if (sym->st_value > addr && sym->st_value < highaddr)
549 {
550 highaddr = sym->st_value;
551 highidx = i;
552 }
553 }
554
555 if (lowidx != -1)
556 {
557 sym = gelf_getsym (symdata, lowidx, &sym_mem);
558 assert (sym != NULL);
559
560 const char *lowstr = elf_strptr (elf, shdr->sh_link,
561 sym->st_name);
562
563 if (sym->st_value + sym->st_size > addr)
564 {
565 /* It is this function. */
566 if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
567 {
568 printf (gettext ("\
569the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
570 lowstr);
571 tsearch (lowstr, knownsrcs, ptrcompare);
572 }
573 }
574 else if (highidx == -1)
575 printf (gettext ("\
576the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
577 lowstr);
578 else
579 {
580 sym = gelf_getsym (symdata, highidx, &sym_mem);
581 assert (sym != NULL);
582
583 printf (gettext ("\
584either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
585 lowstr, elf_strptr (elf, shdr->sh_link,
586 sym->st_name));
587 }
588 return;
589 }
590 else if (highidx != -1)
591 {
592 sym = gelf_getsym (symdata, highidx, &sym_mem);
593 assert (sym != NULL);
594
595 printf (gettext ("\
596the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
597 elf_strptr (elf, shdr->sh_link, sym->st_name));
598 return;
599 }
600 }
601 }
602
603 printf (gettext ("\
604a relocation modifies memory at offset %llu in a write-protected segment\n"),
605 (unsigned long long int) addr);
606 break;
607 }
608}
609
610
611#include "debugpred.h"