1 /* Locate source files or functions which caused text relocations.
2 Copyright (C) 2005-2010, 2012, 2014, 2018 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 <fcntl.h>
27 #include <gelf.h>
28 #include <libdw.h>
29 #include <libintl.h>
30 #include <locale.h>
31 #include <search.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include <printversion.h>
39 #include "libeu.h"
40 #include "system.h"
41
42 struct segments
43 {
44 GElf_Addr from;
45 GElf_Addr to;
46 };
47
48
49 /* Name and version of program. */
50 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
51
52 /* Bug report address. */
53 ARGP_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. */
59 static 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. */
71 static const char doc[] = N_("\
72 Locate source of text relocations in FILEs (a.out by default).");
73
74 /* Strings for arguments in help texts. */
75 static const char args_doc[] = N_("[FILE...]");
76
77 /* Prototype for option handler. */
78 static error_t parse_opt (int key, char *arg, struct argp_state *state);
79
80 /* Data structure to communicate with argp functions. */
81 static struct argp argp =
82 {
83 options, parse_opt, args_doc, doc, NULL, NULL, NULL
84 };
85
86
87 /* Print symbols in file named FNAME. */
88 static 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. */
92 static 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. */
100 static const char *rootdir = "/";
101
102 /* Root of debuginfo directory hierarchy. */
103 static const char *debuginfo_root;
104
105
106 int
main(int argc,char * argv[])107 main (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. */
157 static error_t
parse_opt(int key,char * arg,struct argp_state * state)158 parse_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
178 static void
noop(void * arg)179 noop (void *arg __attribute__ ((unused)))
180 {
181 }
182
183
184 static int
open_rootdir_file(const char * fname)185 open_rootdir_file (const char *fname)
186 {
187 char *new_fname = NULL;
188 const char *real_fname = fname;
189
190 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
191 real_fname = new_fname = xasprintf ("%s/%s", rootdir, fname);
192
193 int fd = open (real_fname, O_RDONLY);
194 if (fd == -1)
195 error (0, errno, _("cannot open '%s'"), fname);
196
197 free (new_fname);
198 return fd;
199 }
200
201
202 static int
process_file(const char * fname,bool more_than_one)203 process_file (const char *fname, bool more_than_one)
204 {
205 int result = 0;
206 void *knownsrcs = NULL;
207 int fd = open_rootdir_file (fname);
208 if (fd == -1)
209 return 1;
210
211 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
212 if (elf == NULL)
213 {
214 error (0, 0, _("cannot create ELF descriptor for '%s': %s"),
215 fname, elf_errmsg (-1));
216 goto err_close;
217 }
218
219 /* Make sure the file is a DSO. */
220 GElf_Ehdr ehdr_mem;
221 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
222 if (ehdr == NULL)
223 {
224 error (0, 0, _("cannot get ELF header '%s': %s"),
225 fname, elf_errmsg (-1));
226 err_elf_close:
227 elf_end (elf);
228 err_close:
229 close (fd);
230 return 1;
231 }
232
233 if (ehdr->e_type != ET_DYN)
234 {
235 error (0, 0, _("'%s' is not a DSO or PIE"), fname);
236 goto err_elf_close;
237 }
238
239 /* Determine whether the DSO has text relocations at all and locate
240 the symbol table. */
241 Elf_Scn *symscn = NULL;
242 Elf_Scn *scn = NULL;
243 bool seen_dynamic = false;
244 bool have_textrel = false;
245 while ((scn = elf_nextscn (elf, scn)) != NULL
246 && (!seen_dynamic || symscn == NULL))
247 {
248 /* Handle the section if it is a symbol table. */
249 GElf_Shdr shdr_mem;
250 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
251
252 if (shdr == NULL)
253 {
254 error (0, 0,
255 _("getting get section header of section %zu: %s"),
256 elf_ndxscn (scn), elf_errmsg (-1));
257 goto err_elf_close;
258 }
259
260 switch (shdr->sh_type)
261 {
262 case SHT_DYNAMIC:
263 if (!seen_dynamic)
264 {
265 seen_dynamic = true;
266
267 Elf_Data *data = elf_getdata (scn, NULL);
268 size_t entries = (shdr->sh_entsize == 0
269 ? 0 : shdr->sh_size / shdr->sh_entsize);
270
271 for (size_t cnt = 0; cnt < entries; ++cnt)
272 {
273 GElf_Dyn dynmem;
274 GElf_Dyn *dyn;
275
276 dyn = gelf_getdyn (data, cnt, &dynmem);
277 if (dyn == NULL)
278 {
279 error (0, 0, _("cannot read dynamic section: %s"),
280 elf_errmsg (-1));
281 goto err_elf_close;
282 }
283
284 if (dyn->d_tag == DT_TEXTREL
285 || (dyn->d_tag == DT_FLAGS
286 && (dyn->d_un.d_val & DF_TEXTREL) != 0))
287 have_textrel = true;
288 }
289 }
290 break;
291
292 case SHT_SYMTAB:
293 symscn = scn;
294 break;
295 }
296 }
297
298 if (!have_textrel)
299 {
300 error (0, 0, _("no text relocations reported in '%s'"), fname);
301 goto err_elf_close;
302 }
303
304 int fd2 = -1;
305 Elf *elf2 = NULL;
306 /* Get the address ranges for the loaded segments. */
307 size_t nsegments_max = 10;
308 size_t nsegments = 0;
309 struct segments *segments = malloc (nsegments_max * sizeof (segments[0]));
310 if (segments == NULL)
311 error (1, errno, _("while reading ELF file"));
312
313 size_t phnum;
314 if (elf_getphdrnum (elf, &phnum) != 0)
315 error (1, 0, _("cannot get program header count: %s"),
316 elf_errmsg (-1));
317
318
319 for (size_t i = 0; i < phnum; ++i)
320 {
321 GElf_Phdr phdr_mem;
322 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
323 if (phdr == NULL)
324 {
325 error (0, 0,
326 _("cannot get program header index at offset %zd: %s"),
327 i, elf_errmsg (-1));
328 result = 1;
329 goto next;
330 }
331
332 if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
333 {
334 if (nsegments == nsegments_max)
335 {
336 nsegments_max *= 2;
337 segments
338 = realloc (segments, nsegments_max * sizeof (segments[0]));
339 if (segments == NULL)
340 {
341 error (0, 0, _("\
342 cannot get program header index at offset %zd: %s"),
343 i, elf_errmsg (-1));
344 result = 1;
345 goto next;
346 }
347 }
348
349 segments[nsegments].from = phdr->p_vaddr;
350 segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
351 ++nsegments;
352 }
353 }
354
355 if (nsegments > 0)
356 {
357
358 Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
359 /* Look for debuginfo files if the information is not the in
360 opened file itself. This makes only sense if the input file
361 is specified with an absolute path. */
362 if (dw == NULL && fname[0] == '/')
363 {
364 char *difname =
365 xasprintf("%s%s/%s.debug", rootdir, debuginfo_root, fname);
366 fd2 = open (difname, O_RDONLY);
367 free (difname);
368 if (fd2 != -1
369 && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
370 dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
371 }
372
373 /* Look at all relocations and determine which modify
374 write-protected segments. */
375 scn = NULL;
376 while ((scn = elf_nextscn (elf, scn)) != NULL)
377 {
378 /* Handle the section if it is a symbol table. */
379 GElf_Shdr shdr_mem;
380 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
381
382 if (shdr == NULL)
383 {
384 error (0, 0,
385 _("cannot get section header of section %zu: %s"),
386 elf_ndxscn (scn), elf_errmsg (-1));
387 result = 1;
388 goto next;
389 }
390
391 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
392 && symscn == NULL)
393 {
394 symscn = elf_getscn (elf, shdr->sh_link);
395 if (symscn == NULL)
396 {
397 error (0, 0, _("\
398 cannot get symbol table section %zu in '%s': %s"),
399 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
400 result = 1;
401 goto next;
402 }
403 }
404
405 if (shdr->sh_type == SHT_REL)
406 {
407 Elf_Data *data = elf_getdata (scn, NULL);
408 size_t entries = (shdr->sh_entsize == 0
409 ? 0 : shdr->sh_size / shdr->sh_entsize);
410
411 for (int cnt = 0;
412 (size_t) cnt < entries; ++cnt)
413 {
414 GElf_Rel rel_mem;
415 GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
416 if (rel == NULL)
417 {
418 error (0, 0, _("\
419 cannot get relocation at index %d in section %zu in '%s': %s"),
420 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
421 result = 1;
422 goto next;
423 }
424
425 check_rel (nsegments, segments, rel->r_offset, elf,
426 symscn, dw, fname, more_than_one, &knownsrcs);
427 }
428 }
429 else if (shdr->sh_type == SHT_RELA)
430 {
431 Elf_Data *data = elf_getdata (scn, NULL);
432 size_t entries = (shdr->sh_entsize == 0
433 ? 0 : shdr->sh_size / shdr->sh_entsize);
434
435 for (int cnt = 0; (size_t) cnt < entries; ++cnt)
436 {
437 GElf_Rela rela_mem;
438 GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
439 if (rela == NULL)
440 {
441 error (0, 0, _("\
442 cannot get relocation at index %d in section %zu in '%s': %s"),
443 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
444 result = 1;
445 goto next;
446 }
447
448 check_rel (nsegments, segments, rela->r_offset, elf,
449 symscn, dw, fname, more_than_one, &knownsrcs);
450 }
451 }
452 }
453
454 dwarf_end (dw);
455 }
456
457 next:
458 elf_end (elf);
459 elf_end (elf2);
460 close (fd);
461 if (fd2 != -1)
462 close (fd2);
463
464 free (segments);
465 tdestroy (knownsrcs, noop);
466
467 return result;
468 }
469
470
471 static int
ptrcompare(const void * p1,const void * p2)472 ptrcompare (const void *p1, const void *p2)
473 {
474 if ((uintptr_t) p1 < (uintptr_t) p2)
475 return -1;
476 if ((uintptr_t) p1 > (uintptr_t) p2)
477 return 1;
478 return 0;
479 }
480
481
482 static void
check_rel(size_t nsegments,struct segments segments[nsegments],GElf_Addr addr,Elf * elf,Elf_Scn * symscn,Dwarf * dw,const char * fname,bool more_than_one,void ** knownsrcs)483 check_rel (size_t nsegments, struct segments segments[nsegments],
484 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
485 const char *fname, bool more_than_one, void **knownsrcs)
486 {
487 for (size_t cnt = 0; cnt < nsegments; ++cnt)
488 if (segments[cnt].from <= addr && segments[cnt].to > addr)
489 {
490 Dwarf_Die die_mem;
491 Dwarf_Die *die;
492 Dwarf_Line *line;
493 const char *src;
494
495 if (more_than_one)
496 printf ("%s: ", fname);
497
498 if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
499 && (line = dwarf_getsrc_die (die, addr)) != NULL
500 && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
501 {
502 /* There can be more than one relocation against one file.
503 Try to avoid multiple messages. And yes, the code uses
504 pointer comparison. */
505 if (tfind (src, knownsrcs, ptrcompare) == NULL)
506 {
507 printf (_("%s not compiled with -fpic/-fPIC\n"), src);
508 tsearch (src, knownsrcs, ptrcompare);
509 }
510 return;
511 }
512 else
513 {
514 /* At least look at the symbol table to see which function
515 the modified address is in. */
516 Elf_Data *symdata = elf_getdata (symscn, NULL);
517 GElf_Shdr shdr_mem;
518 GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
519 if (shdr != NULL)
520 {
521 GElf_Addr lowaddr = 0;
522 int lowidx = -1;
523 GElf_Addr highaddr = ~0ul;
524 int highidx = -1;
525 GElf_Sym sym_mem;
526 GElf_Sym *sym;
527 size_t entries = (shdr->sh_entsize == 0
528 ? 0 : shdr->sh_size / shdr->sh_entsize);
529
530 for (int i = 0; (size_t) i < entries; ++i)
531 {
532 sym = gelf_getsym (symdata, i, &sym_mem);
533 if (sym == NULL)
534 continue;
535
536 if (sym->st_value < addr && sym->st_value > lowaddr)
537 {
538 lowaddr = sym->st_value;
539 lowidx = i;
540 }
541 if (sym->st_value > addr && sym->st_value < highaddr)
542 {
543 highaddr = sym->st_value;
544 highidx = i;
545 }
546 }
547
548 if (lowidx != -1)
549 {
550 sym = gelf_getsym (symdata, lowidx, &sym_mem);
551 assert (sym != NULL);
552
553 const char *lowstr = elf_strptr (elf, shdr->sh_link,
554 sym->st_name);
555
556 if (sym->st_value + sym->st_size > addr)
557 {
558 /* It is this function. */
559 if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
560 {
561 printf (_("\
562 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
563 lowstr);
564 tsearch (lowstr, knownsrcs, ptrcompare);
565 }
566 }
567 else if (highidx == -1)
568 printf (_("\
569 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
570 lowstr);
571 else
572 {
573 sym = gelf_getsym (symdata, highidx, &sym_mem);
574 assert (sym != NULL);
575
576 printf (_("\
577 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
578 lowstr, elf_strptr (elf, shdr->sh_link,
579 sym->st_name));
580 }
581 return;
582 }
583 else if (highidx != -1)
584 {
585 sym = gelf_getsym (symdata, highidx, &sym_mem);
586 assert (sym != NULL);
587
588 printf (_("\
589 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
590 elf_strptr (elf, shdr->sh_link, sym->st_name));
591 return;
592 }
593 }
594 }
595
596 printf (_("\
597 a relocation modifies memory at offset %llu in a write-protected segment\n"),
598 (unsigned long long int) addr);
599 break;
600 }
601 }
602
603
604 #include "debugpred.h"
605