• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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