1 /* Locate source files or functions which caused text relocations.
2 Copyright (C) 2005, 2006, 2007 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5
6 Red Hat elfutils is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 2 of the License.
9
10 Red Hat elfutils is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with Red Hat elfutils; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18
19 Red Hat elfutils is an included package of the Open Invention Network.
20 An included package of the Open Invention Network is a package for which
21 Open Invention Network licensees cross-license their patents. No patent
22 license is granted, either expressly or impliedly, by designation as an
23 included package. Should you wish to participate in the Open Invention
24 Network licensing program, please visit www.openinventionnetwork.com
25 <http://www.openinventionnetwork.com>. */
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <argp.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <error.h>
35 #include <fcntl.h>
36 #include <gelf.h>
37 #include <libdw.h>
38 #include <libintl.h>
39 #include <locale.h>
40 #include <search.h>
41 #include <stdbool.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47
48 struct segments
49 {
50 GElf_Addr from;
51 GElf_Addr to;
52 };
53
54
55 /* Name and version of program. */
56 static void print_version (FILE *stream, struct argp_state *state);
57 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
58
59 /* Bug report address. */
60 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
61
62 /* Values for the parameters which have no short form. */
63 #define OPT_DEBUGINFO 0x100
64
65 /* Definitions of arguments for argp functions. */
66 static const struct argp_option options[] =
67 {
68 { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
69 { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
70 { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
71 N_("Use PATH as root of debuginfo hierarchy"), 0 },
72
73 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
74 { NULL, 0, NULL, 0, NULL, 0 }
75 };
76
77 /* Short description of program. */
78 static const char doc[] = N_("\
79 Locate source of text relocations in FILEs (a.out by default).");
80
81 /* Strings for arguments in help texts. */
82 static const char args_doc[] = N_("[FILE...]");
83
84 /* Prototype for option handler. */
85 static error_t parse_opt (int key, char *arg, struct argp_state *state);
86
87 /* Data structure to communicate with argp functions. */
88 static struct argp argp =
89 {
90 options, parse_opt, args_doc, doc, NULL, NULL, NULL
91 };
92
93
94 /* Print symbols in file named FNAME. */
95 static int process_file (const char *fname, bool more_than_one);
96
97 /* Check for text relocations in the given file. The segment
98 information is known. */
99 static void check_rel (size_t nsegments, struct segments segments[nsegments],
100 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
101 const char *fname, bool more_than_one,
102 void **knownsrcs);
103
104
105
106 /* User-provided root directory. */
107 static const char *rootdir = "/";
108
109 /* Root of debuginfo directory hierarchy. */
110 static const char *debuginfo_root;
111
112
113 int
main(int argc,char * argv[])114 main (int argc, char *argv[])
115 {
116 int remaining;
117 int result = 0;
118
119 /* Set locale. */
120 (void) setlocale (LC_ALL, "");
121
122 /* Make sure the message catalog can be found. */
123 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
124
125 /* Initialize the message catalog. */
126 (void) textdomain (PACKAGE_TARNAME);
127
128 /* Parse and process arguments. */
129 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
130
131 /* Tell the library which version we are expecting. */
132 elf_version (EV_CURRENT);
133
134 /* If the user has not specified the root directory for the
135 debuginfo hierarchy, we have to determine it ourselves. */
136 if (debuginfo_root == NULL)
137 {
138 // XXX The runtime should provide this information.
139 #if defined __ia64__ || defined __alpha__
140 debuginfo_root = "/usr/lib/debug";
141 #else
142 debuginfo_root = (sizeof (long int) == 4
143 ? "/usr/lib/debug" : "/usr/lib64/debug");
144 #endif
145 }
146
147 if (remaining == argc)
148 result = process_file ("a.out", false);
149 else
150 {
151 /* Process all the remaining files. */
152 const bool more_than_one = remaining + 1 < argc;
153
154 do
155 result |= process_file (argv[remaining], more_than_one);
156 while (++remaining < argc);
157 }
158
159 return result;
160 }
161
162
163 /* Print the version information. */
164 static void
print_version(FILE * stream,struct argp_state * state)165 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
166 {
167 fprintf (stream, "findtextrel (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
168 fprintf (stream, gettext ("\
169 Copyright (C) %s Red Hat, Inc.\n\
170 This is free software; see the source for copying conditions. There is NO\n\
171 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
172 "), "2008");
173 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
174 }
175
176
177 /* Handle program arguments. */
178 static error_t
parse_opt(int key,char * arg,struct argp_state * state)179 parse_opt (int key, char *arg,
180 struct argp_state *state __attribute__ ((unused)))
181 {
182 switch (key)
183 {
184 case 'r':
185 rootdir = arg;
186 break;
187
188 case OPT_DEBUGINFO:
189 debuginfo_root = arg;
190 break;
191
192 default:
193 return ARGP_ERR_UNKNOWN;
194 }
195 return 0;
196 }
197
198
199 static void
noop(void * arg)200 noop (void *arg __attribute__ ((unused)))
201 {
202 }
203
204
205 static int
process_file(const char * fname,bool more_than_one)206 process_file (const char *fname, bool more_than_one)
207 {
208 int result = 0;
209 void *knownsrcs = NULL;
210
211 size_t fname_len = strlen (fname);
212 size_t rootdir_len = strlen (rootdir);
213 const char *real_fname = fname;
214 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
215 {
216 /* Prepend the user-provided root directory. */
217 char *new_fname = alloca (rootdir_len + fname_len + 2);
218 *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
219 "/"),
220 fname, fname_len)) = '\0';
221 real_fname = new_fname;
222 }
223
224 int fd = open64 (real_fname, O_RDONLY);
225 if (fd == -1)
226 {
227 error (0, errno, gettext ("cannot open '%s'"), fname);
228 return 1;
229 }
230
231 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
232 if (elf == NULL)
233 {
234 error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
235 fname, elf_errmsg (-1));
236 goto err_close;
237 }
238
239 /* Make sure the file is a DSO. */
240 GElf_Ehdr ehdr_mem;
241 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
242 if (ehdr == NULL)
243 {
244 error (0, 0, gettext ("cannot get ELF header '%s': %s"),
245 fname, elf_errmsg (-1));
246 err_elf_close:
247 elf_end (elf);
248 err_close:
249 close (fd);
250 return 1;
251 }
252
253 if (ehdr->e_type != ET_DYN)
254 {
255 error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname);
256 goto err_elf_close;
257 }
258
259 /* Determine whether the DSO has text relocations at all and locate
260 the symbol table. */
261 Elf_Scn *symscn = NULL;
262 Elf_Scn *scn = NULL;
263 while ((scn = elf_nextscn (elf, scn)) != NULL)
264 {
265 /* Handle the section if it is a symbol table. */
266 GElf_Shdr shdr_mem;
267 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
268
269 if (shdr == NULL)
270 {
271 error (0, 0,
272 gettext ("getting get section header of section %zu: %s"),
273 elf_ndxscn (scn), elf_errmsg (-1));
274 goto err_elf_close;
275 }
276
277 if (shdr->sh_type == SHT_DYNAMIC)
278 {
279 Elf_Data *data = elf_getdata (scn, NULL);
280
281 for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize;
282 ++cnt)
283 {
284 GElf_Dyn dynmem;
285 GElf_Dyn *dyn;
286
287 dyn = gelf_getdyn (data, cnt, &dynmem);
288 if (dyn == NULL)
289 {
290 error (0, 0, gettext ("cannot read dynamic section: %s"),
291 elf_errmsg (-1));
292 goto err_elf_close;
293 }
294
295 if (dyn->d_tag == DT_TEXTREL
296 || (dyn->d_tag == DT_FLAGS
297 && (dyn->d_un.d_val & DF_TEXTREL) != 0))
298 goto have_textrel;
299 }
300 }
301 else if (shdr->sh_type == SHT_SYMTAB)
302 symscn = scn;
303 }
304
305 error (0, 0, gettext ("no text relocations reported in '%s'"), fname);
306 return 1;
307
308 have_textrel:;
309 int fd2 = -1;
310 Elf *elf2 = NULL;
311 /* Get the address ranges for the loaded segments. */
312 size_t nsegments_max = 10;
313 size_t nsegments = 0;
314 struct segments *segments
315 = (struct segments *) malloc (nsegments_max * sizeof (segments[0]));
316 if (segments == NULL)
317 error (1, errno, gettext ("while reading ELF file"));
318
319 for (int i = 0; i < ehdr->e_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 gettext ("cannot get program header index at offset %d: %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 = (struct segments *) realloc (segments,
339 nsegments_max
340 * sizeof (segments[0]));
341 if (segments == NULL)
342 {
343 error (0, 0, gettext ("\
344 cannot get program header index at offset %d: %s"),
345 i, elf_errmsg (-1));
346 result = 1;
347 goto next;
348 }
349 }
350
351 segments[nsegments].from = phdr->p_vaddr;
352 segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
353 ++nsegments;
354 }
355 }
356
357 if (nsegments > 0)
358 {
359
360 Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
361 /* Look for debuginfo files if the information is not the in
362 opened file itself. This makes only sense if the input file
363 is specified with an absolute path. */
364 if (dw == NULL && fname[0] == '/')
365 {
366 size_t debuginfo_rootlen = strlen (debuginfo_root);
367 char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
368 + fname_len + 8);
369 strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
370 rootdir_len),
371 debuginfo_root,
372 debuginfo_rootlen),
373 "/"),
374 fname, fname_len),
375 ".debug");
376
377 fd2 = open64 (difname, O_RDONLY);
378 if (fd2 != -1
379 && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
380 dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
381 }
382
383 /* Look at all relocations and determine which modify
384 write-protected segments. */
385 scn = NULL;
386 while ((scn = elf_nextscn (elf, scn)) != NULL)
387 {
388 /* Handle the section if it is a symbol table. */
389 GElf_Shdr shdr_mem;
390 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
391
392 if (shdr == NULL)
393 {
394 error (0, 0,
395 gettext ("cannot get section header of section %Zu: %s"),
396 elf_ndxscn (scn), elf_errmsg (-1));
397 result = 1;
398 goto next;
399 }
400
401 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
402 && symscn == NULL)
403 {
404 symscn = elf_getscn (elf, shdr->sh_link);
405 if (symscn == NULL)
406 {
407 error (0, 0, gettext ("\
408 cannot get symbol table section %zu in '%s': %s"),
409 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
410 result = 1;
411 goto next;
412 }
413 }
414
415 if (shdr->sh_type == SHT_REL)
416 {
417 Elf_Data *data = elf_getdata (scn, NULL);
418
419 for (int cnt = 0;
420 (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
421 ++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, gettext ("\
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
442 for (int cnt = 0;
443 (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
444 ++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, gettext ("\
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 tdestroy (knownsrcs, noop);
474
475 return result;
476 }
477
478
479 static int
ptrcompare(const void * p1,const void * p2)480 ptrcompare (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
490 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)491 check_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 ("\
569 the 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 ("\
576 the 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 ("\
584 either 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 ("\
596 the 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 ("\
604 a 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"
612