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