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 <locale.h>
30 #include <search.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include <printversion.h>
38 #include "libeu.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
open_rootdir_file(const char * fname)184 open_rootdir_file (const char *fname)
185 {
186 char *new_fname = NULL;
187 const char *real_fname = fname;
188
189 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
190 real_fname = new_fname = xasprintf ("%s/%s", rootdir, fname);
191
192 int fd = open (real_fname, O_RDONLY);
193 if (fd == -1)
194 error (0, errno, _("cannot open '%s'"), fname);
195
196 free (new_fname);
197 return fd;
198 }
199
200
201 static int
process_file(const char * fname,bool more_than_one)202 process_file (const char *fname, bool more_than_one)
203 {
204 int result = 0;
205 void *knownsrcs = NULL;
206 int fd = open_rootdir_file (fname);
207 if (fd == -1)
208 return 1;
209
210 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
211 if (elf == NULL)
212 {
213 error (0, 0, _("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, _("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, _("'%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 _("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 size_t entries = (shdr->sh_entsize == 0
268 ? 0 : shdr->sh_size / shdr->sh_entsize);
269
270 for (size_t cnt = 0; cnt < entries; ++cnt)
271 {
272 GElf_Dyn dynmem;
273 GElf_Dyn *dyn;
274
275 dyn = gelf_getdyn (data, cnt, &dynmem);
276 if (dyn == NULL)
277 {
278 error (0, 0, _("cannot read dynamic section: %s"),
279 elf_errmsg (-1));
280 goto err_elf_close;
281 }
282
283 if (dyn->d_tag == DT_TEXTREL
284 || (dyn->d_tag == DT_FLAGS
285 && (dyn->d_un.d_val & DF_TEXTREL) != 0))
286 have_textrel = true;
287 }
288 }
289 break;
290
291 case SHT_SYMTAB:
292 symscn = scn;
293 break;
294 }
295 }
296
297 if (!have_textrel)
298 {
299 error (0, 0, _("no text relocations reported in '%s'"), fname);
300 goto err_elf_close;
301 }
302
303 int fd2 = -1;
304 Elf *elf2 = NULL;
305 /* Get the address ranges for the loaded segments. */
306 size_t nsegments_max = 10;
307 size_t nsegments = 0;
308 struct segments *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 = realloc (segments, nsegments_max * sizeof (segments[0]));
338 if (segments == NULL)
339 {
340 error (0, 0, _("\
341 cannot get program header index at offset %zd: %s"),
342 i, elf_errmsg (-1));
343 result = 1;
344 goto next;
345 }
346 }
347
348 segments[nsegments].from = phdr->p_vaddr;
349 segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
350 ++nsegments;
351 }
352 }
353
354 if (nsegments > 0)
355 {
356
357 Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
358 /* Look for debuginfo files if the information is not the in
359 opened file itself. This makes only sense if the input file
360 is specified with an absolute path. */
361 if (dw == NULL && fname[0] == '/')
362 {
363 char *difname =
364 xasprintf("%s%s/%s.debug", rootdir, debuginfo_root, fname);
365 fd2 = open (difname, O_RDONLY);
366 free (difname);
367 if (fd2 != -1
368 && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
369 dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
370 }
371
372 /* Look at all relocations and determine which modify
373 write-protected segments. */
374 scn = NULL;
375 while ((scn = elf_nextscn (elf, scn)) != NULL)
376 {
377 /* Handle the section if it is a symbol table. */
378 GElf_Shdr shdr_mem;
379 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
380
381 if (shdr == NULL)
382 {
383 error (0, 0,
384 _("cannot get section header of section %zu: %s"),
385 elf_ndxscn (scn), elf_errmsg (-1));
386 result = 1;
387 goto next;
388 }
389
390 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
391 && symscn == NULL)
392 {
393 symscn = elf_getscn (elf, shdr->sh_link);
394 if (symscn == NULL)
395 {
396 error (0, 0, _("\
397 cannot get symbol table section %zu in '%s': %s"),
398 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
399 result = 1;
400 goto next;
401 }
402 }
403
404 if (shdr->sh_type == SHT_REL)
405 {
406 Elf_Data *data = elf_getdata (scn, NULL);
407 size_t entries = (shdr->sh_entsize == 0
408 ? 0 : shdr->sh_size / shdr->sh_entsize);
409
410 for (int cnt = 0;
411 (size_t) cnt < entries; ++cnt)
412 {
413 GElf_Rel rel_mem;
414 GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
415 if (rel == NULL)
416 {
417 error (0, 0, _("\
418 cannot get relocation at index %d in section %zu in '%s': %s"),
419 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
420 result = 1;
421 goto next;
422 }
423
424 check_rel (nsegments, segments, rel->r_offset, elf,
425 symscn, dw, fname, more_than_one, &knownsrcs);
426 }
427 }
428 else if (shdr->sh_type == SHT_RELA)
429 {
430 Elf_Data *data = elf_getdata (scn, NULL);
431 size_t entries = (shdr->sh_entsize == 0
432 ? 0 : shdr->sh_size / shdr->sh_entsize);
433
434 for (int cnt = 0; (size_t) cnt < entries; ++cnt)
435 {
436 GElf_Rela rela_mem;
437 GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
438 if (rela == NULL)
439 {
440 error (0, 0, _("\
441 cannot get relocation at index %d in section %zu in '%s': %s"),
442 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
443 result = 1;
444 goto next;
445 }
446
447 check_rel (nsegments, segments, rela->r_offset, elf,
448 symscn, dw, fname, more_than_one, &knownsrcs);
449 }
450 }
451 }
452
453 dwarf_end (dw);
454 }
455
456 next:
457 elf_end (elf);
458 elf_end (elf2);
459 close (fd);
460 if (fd2 != -1)
461 close (fd2);
462
463 free (segments);
464 tdestroy (knownsrcs, noop);
465
466 return result;
467 }
468
469
470 static int
ptrcompare(const void * p1,const void * p2)471 ptrcompare (const void *p1, const void *p2)
472 {
473 if ((uintptr_t) p1 < (uintptr_t) p2)
474 return -1;
475 if ((uintptr_t) p1 > (uintptr_t) p2)
476 return 1;
477 return 0;
478 }
479
480
481 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)482 check_rel (size_t nsegments, struct segments segments[nsegments],
483 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
484 const char *fname, bool more_than_one, void **knownsrcs)
485 {
486 for (size_t cnt = 0; cnt < nsegments; ++cnt)
487 if (segments[cnt].from <= addr && segments[cnt].to > addr)
488 {
489 Dwarf_Die die_mem;
490 Dwarf_Die *die;
491 Dwarf_Line *line;
492 const char *src;
493
494 if (more_than_one)
495 printf ("%s: ", fname);
496
497 if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
498 && (line = dwarf_getsrc_die (die, addr)) != NULL
499 && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
500 {
501 /* There can be more than one relocation against one file.
502 Try to avoid multiple messages. And yes, the code uses
503 pointer comparison. */
504 if (tfind (src, knownsrcs, ptrcompare) == NULL)
505 {
506 printf (_("%s not compiled with -fpic/-fPIC\n"), src);
507 tsearch (src, knownsrcs, ptrcompare);
508 }
509 return;
510 }
511 else
512 {
513 /* At least look at the symbol table to see which function
514 the modified address is in. */
515 Elf_Data *symdata = elf_getdata (symscn, NULL);
516 GElf_Shdr shdr_mem;
517 GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
518 if (shdr != NULL)
519 {
520 GElf_Addr lowaddr = 0;
521 int lowidx = -1;
522 GElf_Addr highaddr = ~0ul;
523 int highidx = -1;
524 GElf_Sym sym_mem;
525 GElf_Sym *sym;
526 size_t entries = (shdr->sh_entsize == 0
527 ? 0 : shdr->sh_size / shdr->sh_entsize);
528
529 for (int i = 0; (size_t) i < entries; ++i)
530 {
531 sym = gelf_getsym (symdata, i, &sym_mem);
532 if (sym == NULL)
533 continue;
534
535 if (sym->st_value < addr && sym->st_value > lowaddr)
536 {
537 lowaddr = sym->st_value;
538 lowidx = i;
539 }
540 if (sym->st_value > addr && sym->st_value < highaddr)
541 {
542 highaddr = sym->st_value;
543 highidx = i;
544 }
545 }
546
547 if (lowidx != -1)
548 {
549 sym = gelf_getsym (symdata, lowidx, &sym_mem);
550 assert (sym != NULL);
551
552 const char *lowstr = elf_strptr (elf, shdr->sh_link,
553 sym->st_name);
554
555 if (sym->st_value + sym->st_size > addr)
556 {
557 /* It is this function. */
558 if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
559 {
560 printf (_("\
561 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
562 lowstr);
563 tsearch (lowstr, knownsrcs, ptrcompare);
564 }
565 }
566 else if (highidx == -1)
567 printf (_("\
568 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
569 lowstr);
570 else
571 {
572 sym = gelf_getsym (symdata, highidx, &sym_mem);
573 assert (sym != NULL);
574
575 printf (_("\
576 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
577 lowstr, elf_strptr (elf, shdr->sh_link,
578 sym->st_name));
579 }
580 return;
581 }
582 else if (highidx != -1)
583 {
584 sym = gelf_getsym (symdata, highidx, &sym_mem);
585 assert (sym != NULL);
586
587 printf (_("\
588 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
589 elf_strptr (elf, shdr->sh_link, sym->st_name));
590 return;
591 }
592 }
593 }
594
595 printf (_("\
596 a relocation modifies memory at offset %llu in a write-protected segment\n"),
597 (unsigned long long int) addr);
598 break;
599 }
600 }
601
602
603 #include "debugpred.h"
604