1 /* Locate source files or functions which caused text relocations.
2 Copyright (C) 2005-2010, 2012 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2005.
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.
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.
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.
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>. */
57 /* Name and version of program. */
58 static void print_version (FILE *stream, struct argp_state *state);
59 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
61 /* Bug report address. */
62 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
64 /* Values for the parameters which have no short form. */
65 #define OPT_DEBUGINFO 0x100
67 /* Definitions of arguments for argp functions. */
68 static const struct argp_option options[] =
70 { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
71 { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
72 { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
73 N_("Use PATH as root of debuginfo hierarchy"), 0 },
75 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
76 { NULL, 0, NULL, 0, NULL, 0 }
79 /* Short description of program. */
80 static const char doc[] = N_("\
81 Locate source of text relocations in FILEs (a.out by default).");
83 /* Strings for arguments in help texts. */
84 static const char args_doc[] = N_("[FILE...]");
86 /* Prototype for option handler. */
87 static error_t parse_opt (int key, char *arg, struct argp_state *state);
89 /* Data structure to communicate with argp functions. */
90 static struct argp argp =
92 options, parse_opt, args_doc, doc, NULL, NULL, NULL
96 /* Print symbols in file named FNAME. */
97 static int process_file (const char *fname, bool more_than_one);
99 /* Check for text relocations in the given file. The segment
100 information is known. */
101 static void check_rel (size_t nsegments, struct segments segments[nsegments],
102 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
103 const char *fname, bool more_than_one,
108 /* User-provided root directory. */
109 static const char *rootdir = "/";
111 /* Root of debuginfo directory hierarchy. */
112 static const char *debuginfo_root;
116 main (int argc, char *argv[])
122 (void) setlocale (LC_ALL, "");
124 /* Make sure the message catalog can be found. */
125 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
127 /* Initialize the message catalog. */
128 (void) textdomain (PACKAGE_TARNAME);
130 /* Parse and process arguments. */
131 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
133 /* Tell the library which version we are expecting. */
134 elf_version (EV_CURRENT);
136 /* If the user has not specified the root directory for the
137 debuginfo hierarchy, we have to determine it ourselves. */
138 if (debuginfo_root == NULL)
140 // XXX The runtime should provide this information.
141 #if defined __ia64__ || defined __alpha__
142 debuginfo_root = "/usr/lib/debug";
144 debuginfo_root = (sizeof (long int) == 4
145 ? "/usr/lib/debug" : "/usr/lib64/debug");
149 if (remaining == argc)
150 result = process_file ("a.out", false);
153 /* Process all the remaining files. */
154 const bool more_than_one = remaining + 1 < argc;
157 result |= process_file (argv[remaining], more_than_one);
158 while (++remaining < argc);
165 /* Print the version information. */
167 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
169 fprintf (stream, "findtextrel (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
170 fprintf (stream, gettext ("\
171 Copyright (C) %s Red Hat, Inc.\n\
172 This is free software; see the source for copying conditions. There is NO\n\
173 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
175 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
179 /* Handle program arguments. */
181 parse_opt (int key, char *arg,
182 struct argp_state *state __attribute__ ((unused)))
191 debuginfo_root = arg;
195 return ARGP_ERR_UNKNOWN;
202 noop (void *arg __attribute__ ((unused)))
208 process_file (const char *fname, bool more_than_one)
211 void *knownsrcs = NULL;
213 size_t fname_len = strlen (fname);
214 size_t rootdir_len = strlen (rootdir);
215 const char *real_fname = fname;
216 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
218 /* Prepend the user-provided root directory. */
219 char *new_fname = alloca (rootdir_len + fname_len + 2);
220 *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
222 fname, fname_len)) = '\0';
223 real_fname = new_fname;
226 int fd = open64 (real_fname, O_RDONLY);
229 error (0, errno, gettext ("cannot open '%s'"), fname);
233 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
236 error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
237 fname, elf_errmsg (-1));
241 /* Make sure the file is a DSO. */
243 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
246 error (0, 0, gettext ("cannot get ELF header '%s': %s"),
247 fname, elf_errmsg (-1));
255 if (ehdr->e_type != ET_DYN)
257 error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname);
261 /* Determine whether the DSO has text relocations at all and locate
263 Elf_Scn *symscn = NULL;
265 bool seen_dynamic = false;
266 bool have_textrel = false;
267 while ((scn = elf_nextscn (elf, scn)) != NULL
268 && (!seen_dynamic || symscn == NULL))
270 /* Handle the section if it is a symbol table. */
272 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
277 gettext ("getting get section header of section %zu: %s"),
278 elf_ndxscn (scn), elf_errmsg (-1));
282 switch (shdr->sh_type)
289 Elf_Data *data = elf_getdata (scn, NULL);
291 for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize;
297 dyn = gelf_getdyn (data, cnt, &dynmem);
300 error (0, 0, gettext ("cannot read dynamic section: %s"),
305 if (dyn->d_tag == DT_TEXTREL
306 || (dyn->d_tag == DT_FLAGS
307 && (dyn->d_un.d_val & DF_TEXTREL) != 0))
321 error (0, 0, gettext ("no text relocations reported in '%s'"), fname);
327 /* Get the address ranges for the loaded segments. */
328 size_t nsegments_max = 10;
329 size_t nsegments = 0;
330 struct segments *segments
331 = (struct segments *) malloc (nsegments_max * sizeof (segments[0]));
332 if (segments == NULL)
333 error (1, errno, gettext ("while reading ELF file"));
335 for (int i = 0; i < ehdr->e_phnum; ++i)
338 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
342 gettext ("cannot get program header index at offset %d: %s"),
348 if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
350 if (nsegments == nsegments_max)
354 = (struct segments *) realloc (segments,
356 * sizeof (segments[0]));
357 if (segments == NULL)
359 error (0, 0, gettext ("\
360 cannot get program header index at offset %d: %s"),
367 segments[nsegments].from = phdr->p_vaddr;
368 segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
376 Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
377 /* Look for debuginfo files if the information is not the in
378 opened file itself. This makes only sense if the input file
379 is specified with an absolute path. */
380 if (dw == NULL && fname[0] == '/')
382 size_t debuginfo_rootlen = strlen (debuginfo_root);
383 char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
385 strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
393 fd2 = open64 (difname, O_RDONLY);
395 && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
396 dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
399 /* Look at all relocations and determine which modify
400 write-protected segments. */
402 while ((scn = elf_nextscn (elf, scn)) != NULL)
404 /* Handle the section if it is a symbol table. */
406 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
411 gettext ("cannot get section header of section %Zu: %s"),
412 elf_ndxscn (scn), elf_errmsg (-1));
417 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
420 symscn = elf_getscn (elf, shdr->sh_link);
423 error (0, 0, gettext ("\
424 cannot get symbol table section %zu in '%s': %s"),
425 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
431 if (shdr->sh_type == SHT_REL)
433 Elf_Data *data = elf_getdata (scn, NULL);
436 (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
440 GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
443 error (0, 0, gettext ("\
444 cannot get relocation at index %d in section %zu in '%s': %s"),
445 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
450 check_rel (nsegments, segments, rel->r_offset, elf,
451 symscn, dw, fname, more_than_one, &knownsrcs);
454 else if (shdr->sh_type == SHT_RELA)
456 Elf_Data *data = elf_getdata (scn, NULL);
459 (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
463 GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
466 error (0, 0, gettext ("\
467 cannot get relocation at index %d in section %zu in '%s': %s"),
468 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
473 check_rel (nsegments, segments, rela->r_offset, elf,
474 symscn, dw, fname, more_than_one, &knownsrcs);
489 tdestroy (knownsrcs, noop);
496 ptrcompare (const void *p1, const void *p2)
498 if ((uintptr_t) p1 < (uintptr_t) p2)
500 if ((uintptr_t) p1 > (uintptr_t) p2)
507 check_rel (size_t nsegments, struct segments segments[nsegments],
508 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
509 const char *fname, bool more_than_one, void **knownsrcs)
511 for (size_t cnt = 0; cnt < nsegments; ++cnt)
512 if (segments[cnt].from <= addr && segments[cnt].to > addr)
520 printf ("%s: ", fname);
522 if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
523 && (line = dwarf_getsrc_die (die, addr)) != NULL
524 && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
526 /* There can be more than one relocation against one file.
527 Try to avoid multiple messages. And yes, the code uses
528 pointer comparison. */
529 if (tfind (src, knownsrcs, ptrcompare) == NULL)
531 printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src);
532 tsearch (src, knownsrcs, ptrcompare);
538 /* At least look at the symbol table to see which function
539 the modified address is in. */
540 Elf_Data *symdata = elf_getdata (symscn, NULL);
542 GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
545 GElf_Addr lowaddr = 0;
547 GElf_Addr highaddr = ~0ul;
552 for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize;
555 sym = gelf_getsym (symdata, i, &sym_mem);
559 if (sym->st_value < addr && sym->st_value > lowaddr)
561 lowaddr = sym->st_value;
564 if (sym->st_value > addr && sym->st_value < highaddr)
566 highaddr = sym->st_value;
573 sym = gelf_getsym (symdata, lowidx, &sym_mem);
574 assert (sym != NULL);
576 const char *lowstr = elf_strptr (elf, shdr->sh_link,
579 if (sym->st_value + sym->st_size > addr)
581 /* It is this function. */
582 if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
585 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
587 tsearch (lowstr, knownsrcs, ptrcompare);
590 else if (highidx == -1)
592 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
596 sym = gelf_getsym (symdata, highidx, &sym_mem);
597 assert (sym != NULL);
600 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
601 lowstr, elf_strptr (elf, shdr->sh_link,
606 else if (highidx != -1)
608 sym = gelf_getsym (symdata, highidx, &sym_mem);
609 assert (sym != NULL);
612 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
613 elf_strptr (elf, shdr->sh_link, sym->st_name));
620 a relocation modifies memory at offset %llu in a write-protected segment\n"),
621 (unsigned long long int) addr);
627 #include "debugpred.h"