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 "system.h"
40
41 struct segments
42 {
43 GElf_Addr from;
44 GElf_Addr to;
45 };
46
47
48 /* Name and version of program. */
49 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
50
51 /* Bug report address. */
52 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
53
54 /* Values for the parameters which have no short form. */
55 #define OPT_DEBUGINFO 0x100
56
57 /* Definitions of arguments for argp functions. */
58 static const struct argp_option options[] =
59 {
60 { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
61 { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
62 { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
63 N_("Use PATH as root of debuginfo hierarchy"), 0 },
64
65 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
66 { NULL, 0, NULL, 0, NULL, 0 }
67 };
68
69 /* Short description of program. */
70 static const char doc[] = N_("\
71 Locate source of text relocations in FILEs (a.out by default).");
72
73 /* Strings for arguments in help texts. */
74 static const char args_doc[] = N_("[FILE...]");
75
76 /* Prototype for option handler. */
77 static error_t parse_opt (int key, char *arg, struct argp_state *state);
78
79 /* Data structure to communicate with argp functions. */
80 static struct argp argp =
81 {
82 options, parse_opt, args_doc, doc, NULL, NULL, NULL
83 };
84
85
86 /* Print symbols in file named FNAME. */
87 static int process_file (const char *fname, bool more_than_one);
88
89 /* Check for text relocations in the given file. The segment
90 information is known. */
91 static void check_rel (size_t nsegments, struct segments segments[nsegments],
92 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
93 const char *fname, bool more_than_one,
94 void **knownsrcs);
95
96
97
98 /* User-provided root directory. */
99 static const char *rootdir = "/";
100
101 /* Root of debuginfo directory hierarchy. */
102 static const char *debuginfo_root;
103
104
105 int
main(int argc,char * argv[])106 main (int argc, char *argv[])
107 {
108 int remaining;
109 int result = 0;
110
111 /* Set locale. */
112 (void) setlocale (LC_ALL, "");
113
114 /* Make sure the message catalog can be found. */
115 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
116
117 /* Initialize the message catalog. */
118 (void) textdomain (PACKAGE_TARNAME);
119
120 /* Parse and process arguments. */
121 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
122
123 /* Tell the library which version we are expecting. */
124 elf_version (EV_CURRENT);
125
126 /* If the user has not specified the root directory for the
127 debuginfo hierarchy, we have to determine it ourselves. */
128 if (debuginfo_root == NULL)
129 {
130 // XXX The runtime should provide this information.
131 #if defined __ia64__ || defined __alpha__
132 debuginfo_root = "/usr/lib/debug";
133 #else
134 debuginfo_root = (sizeof (long int) == 4
135 ? "/usr/lib/debug" : "/usr/lib64/debug");
136 #endif
137 }
138
139 if (remaining == argc)
140 result = process_file ("a.out", false);
141 else
142 {
143 /* Process all the remaining files. */
144 const bool more_than_one = remaining + 1 < argc;
145
146 do
147 result |= process_file (argv[remaining], more_than_one);
148 while (++remaining < argc);
149 }
150
151 return result;
152 }
153
154
155 /* Handle program arguments. */
156 static error_t
parse_opt(int key,char * arg,struct argp_state * state)157 parse_opt (int key, char *arg,
158 struct argp_state *state __attribute__ ((unused)))
159 {
160 switch (key)
161 {
162 case 'r':
163 rootdir = arg;
164 break;
165
166 case OPT_DEBUGINFO:
167 debuginfo_root = arg;
168 break;
169
170 default:
171 return ARGP_ERR_UNKNOWN;
172 }
173 return 0;
174 }
175
176
177 static void
noop(void * arg)178 noop (void *arg __attribute__ ((unused)))
179 {
180 }
181
182
183 static int
process_file(const char * fname,bool more_than_one)184 process_file (const char *fname, bool more_than_one)
185 {
186 int result = 0;
187 void *knownsrcs = NULL;
188
189 size_t fname_len = strlen (fname);
190 size_t rootdir_len = strlen (rootdir);
191 const char *real_fname = fname;
192 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
193 {
194 /* Prepend the user-provided root directory. */
195 char *new_fname = alloca (rootdir_len + fname_len + 2);
196 *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
197 "/"),
198 fname, fname_len)) = '\0';
199 real_fname = new_fname;
200 }
201
202 int fd = open (real_fname, O_RDONLY);
203 if (fd == -1)
204 {
205 error (0, errno, _("cannot open '%s'"), fname);
206 return 1;
207 }
208
209 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
210 if (elf == NULL)
211 {
212 error (0, 0, _("cannot create ELF descriptor for '%s': %s"),
213 fname, elf_errmsg (-1));
214 goto err_close;
215 }
216
217 /* Make sure the file is a DSO. */
218 GElf_Ehdr ehdr_mem;
219 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
220 if (ehdr == NULL)
221 {
222 error (0, 0, _("cannot get ELF header '%s': %s"),
223 fname, elf_errmsg (-1));
224 err_elf_close:
225 elf_end (elf);
226 err_close:
227 close (fd);
228 return 1;
229 }
230
231 if (ehdr->e_type != ET_DYN)
232 {
233 error (0, 0, _("'%s' is not a DSO or PIE"), fname);
234 goto err_elf_close;
235 }
236
237 /* Determine whether the DSO has text relocations at all and locate
238 the symbol table. */
239 Elf_Scn *symscn = NULL;
240 Elf_Scn *scn = NULL;
241 bool seen_dynamic = false;
242 bool have_textrel = false;
243 while ((scn = elf_nextscn (elf, scn)) != NULL
244 && (!seen_dynamic || symscn == NULL))
245 {
246 /* Handle the section if it is a symbol table. */
247 GElf_Shdr shdr_mem;
248 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
249
250 if (shdr == NULL)
251 {
252 error (0, 0,
253 _("getting get section header of section %zu: %s"),
254 elf_ndxscn (scn), elf_errmsg (-1));
255 goto err_elf_close;
256 }
257
258 switch (shdr->sh_type)
259 {
260 case SHT_DYNAMIC:
261 if (!seen_dynamic)
262 {
263 seen_dynamic = true;
264
265 Elf_Data *data = elf_getdata (scn, NULL);
266 size_t entries = (shdr->sh_entsize == 0
267 ? 0 : shdr->sh_size / shdr->sh_entsize);
268
269 for (size_t cnt = 0; cnt < entries; ++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, _("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, _("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, _("while reading ELF file"));
311
312 size_t phnum;
313 if (elf_getphdrnum (elf, &phnum) != 0)
314 error (1, 0, _("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 _("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, _("\
343 cannot 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 _("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, _("\
407 cannot 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 size_t entries = (shdr->sh_entsize == 0
418 ? 0 : shdr->sh_size / shdr->sh_entsize);
419
420 for (int cnt = 0;
421 (size_t) cnt < entries; ++cnt)
422 {
423 GElf_Rel rel_mem;
424 GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
425 if (rel == NULL)
426 {
427 error (0, 0, _("\
428 cannot get relocation at index %d in section %zu in '%s': %s"),
429 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
430 result = 1;
431 goto next;
432 }
433
434 check_rel (nsegments, segments, rel->r_offset, elf,
435 symscn, dw, fname, more_than_one, &knownsrcs);
436 }
437 }
438 else if (shdr->sh_type == SHT_RELA)
439 {
440 Elf_Data *data = elf_getdata (scn, NULL);
441 size_t entries = (shdr->sh_entsize == 0
442 ? 0 : shdr->sh_size / shdr->sh_entsize);
443
444 for (int cnt = 0; (size_t) cnt < entries; ++cnt)
445 {
446 GElf_Rela rela_mem;
447 GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
448 if (rela == NULL)
449 {
450 error (0, 0, _("\
451 cannot get relocation at index %d in section %zu in '%s': %s"),
452 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
453 result = 1;
454 goto next;
455 }
456
457 check_rel (nsegments, segments, rela->r_offset, elf,
458 symscn, dw, fname, more_than_one, &knownsrcs);
459 }
460 }
461 }
462
463 dwarf_end (dw);
464 }
465
466 next:
467 elf_end (elf);
468 elf_end (elf2);
469 close (fd);
470 if (fd2 != -1)
471 close (fd2);
472
473 free (segments);
474 tdestroy (knownsrcs, noop);
475
476 return result;
477 }
478
479
480 static int
ptrcompare(const void * p1,const void * p2)481 ptrcompare (const void *p1, const void *p2)
482 {
483 if ((uintptr_t) p1 < (uintptr_t) p2)
484 return -1;
485 if ((uintptr_t) p1 > (uintptr_t) p2)
486 return 1;
487 return 0;
488 }
489
490
491 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)492 check_rel (size_t nsegments, struct segments segments[nsegments],
493 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
494 const char *fname, bool more_than_one, void **knownsrcs)
495 {
496 for (size_t cnt = 0; cnt < nsegments; ++cnt)
497 if (segments[cnt].from <= addr && segments[cnt].to > addr)
498 {
499 Dwarf_Die die_mem;
500 Dwarf_Die *die;
501 Dwarf_Line *line;
502 const char *src;
503
504 if (more_than_one)
505 printf ("%s: ", fname);
506
507 if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
508 && (line = dwarf_getsrc_die (die, addr)) != NULL
509 && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
510 {
511 /* There can be more than one relocation against one file.
512 Try to avoid multiple messages. And yes, the code uses
513 pointer comparison. */
514 if (tfind (src, knownsrcs, ptrcompare) == NULL)
515 {
516 printf (_("%s not compiled with -fpic/-fPIC\n"), src);
517 tsearch (src, knownsrcs, ptrcompare);
518 }
519 return;
520 }
521 else
522 {
523 /* At least look at the symbol table to see which function
524 the modified address is in. */
525 Elf_Data *symdata = elf_getdata (symscn, NULL);
526 GElf_Shdr shdr_mem;
527 GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
528 if (shdr != NULL)
529 {
530 GElf_Addr lowaddr = 0;
531 int lowidx = -1;
532 GElf_Addr highaddr = ~0ul;
533 int highidx = -1;
534 GElf_Sym sym_mem;
535 GElf_Sym *sym;
536 size_t entries = (shdr->sh_entsize == 0
537 ? 0 : shdr->sh_size / shdr->sh_entsize);
538
539 for (int i = 0; (size_t) i < entries; ++i)
540 {
541 sym = gelf_getsym (symdata, i, &sym_mem);
542 if (sym == NULL)
543 continue;
544
545 if (sym->st_value < addr && sym->st_value > lowaddr)
546 {
547 lowaddr = sym->st_value;
548 lowidx = i;
549 }
550 if (sym->st_value > addr && sym->st_value < highaddr)
551 {
552 highaddr = sym->st_value;
553 highidx = i;
554 }
555 }
556
557 if (lowidx != -1)
558 {
559 sym = gelf_getsym (symdata, lowidx, &sym_mem);
560 assert (sym != NULL);
561
562 const char *lowstr = elf_strptr (elf, shdr->sh_link,
563 sym->st_name);
564
565 if (sym->st_value + sym->st_size > addr)
566 {
567 /* It is this function. */
568 if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
569 {
570 printf (_("\
571 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
572 lowstr);
573 tsearch (lowstr, knownsrcs, ptrcompare);
574 }
575 }
576 else if (highidx == -1)
577 printf (_("\
578 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
579 lowstr);
580 else
581 {
582 sym = gelf_getsym (symdata, highidx, &sym_mem);
583 assert (sym != NULL);
584
585 printf (_("\
586 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
587 lowstr, elf_strptr (elf, shdr->sh_link,
588 sym->st_name));
589 }
590 return;
591 }
592 else if (highidx != -1)
593 {
594 sym = gelf_getsym (symdata, highidx, &sym_mem);
595 assert (sym != NULL);
596
597 printf (_("\
598 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
599 elf_strptr (elf, shdr->sh_link, sym->st_name));
600 return;
601 }
602 }
603 }
604
605 printf (_("\
606 a relocation modifies memory at offset %llu in a write-protected segment\n"),
607 (unsigned long long int) addr);
608 break;
609 }
610 }
611
612
613 #include "debugpred.h"
614