• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <libintl.h>
30 #include <locale.h>
31 #include <search.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include <printversion.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
process_file(const char * fname,bool more_than_one)184 process_file (const char *fname, bool more_than_one)
185 {
186   int result = 0;
187   void *knownsrcs = NULL;
188 
189   size_t fname_len = strlen (fname);
190   size_t rootdir_len = strlen (rootdir);
191   const char *real_fname = fname;
192   if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
193     {
194       /* Prepend the user-provided root directory.  */
195       char *new_fname = alloca (rootdir_len + fname_len + 2);
196       *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
197 				  "/"),
198 			  fname, fname_len)) = '\0';
199       real_fname = new_fname;
200     }
201 
202   int fd = open (real_fname, O_RDONLY);
203   if (fd == -1)
204     {
205       error (0, errno, _("cannot open '%s'"), fname);
206       return 1;
207     }
208 
209   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
210   if (elf == NULL)
211     {
212       error (0, 0, _("cannot create ELF descriptor for '%s': %s"),
213 	     fname, elf_errmsg (-1));
214       goto err_close;
215     }
216 
217   /* Make sure the file is a DSO.  */
218   GElf_Ehdr ehdr_mem;
219   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
220   if (ehdr == NULL)
221     {
222       error (0, 0, _("cannot get ELF header '%s': %s"),
223 	     fname, elf_errmsg (-1));
224     err_elf_close:
225       elf_end (elf);
226     err_close:
227       close (fd);
228       return 1;
229     }
230 
231   if (ehdr->e_type != ET_DYN)
232     {
233       error (0, 0, _("'%s' is not a DSO or PIE"), fname);
234       goto err_elf_close;
235     }
236 
237   /* Determine whether the DSO has text relocations at all and locate
238      the symbol table.  */
239   Elf_Scn *symscn = NULL;
240   Elf_Scn *scn = NULL;
241   bool seen_dynamic = false;
242   bool have_textrel = false;
243   while ((scn = elf_nextscn (elf, scn)) != NULL
244 	 && (!seen_dynamic || symscn == NULL))
245     {
246       /* Handle the section if it is a symbol table.  */
247       GElf_Shdr shdr_mem;
248       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
249 
250       if (shdr == NULL)
251 	{
252 	  error (0, 0,
253 		 _("getting get section header of section %zu: %s"),
254 		 elf_ndxscn (scn), elf_errmsg (-1));
255 	  goto err_elf_close;
256 	}
257 
258       switch (shdr->sh_type)
259 	{
260 	case SHT_DYNAMIC:
261 	  if (!seen_dynamic)
262 	    {
263 	      seen_dynamic = true;
264 
265 	      Elf_Data *data = elf_getdata (scn, NULL);
266 	      size_t entries = (shdr->sh_entsize == 0
267 				? 0 : shdr->sh_size / shdr->sh_entsize);
268 
269 	      for (size_t cnt = 0; cnt < entries; ++cnt)
270 		{
271 		  GElf_Dyn dynmem;
272 		  GElf_Dyn *dyn;
273 
274 		  dyn = gelf_getdyn (data, cnt, &dynmem);
275 		  if (dyn == NULL)
276 		    {
277 		      error (0, 0, _("cannot read dynamic section: %s"),
278 			     elf_errmsg (-1));
279 		      goto err_elf_close;
280 		    }
281 
282 		  if (dyn->d_tag == DT_TEXTREL
283 		      || (dyn->d_tag == DT_FLAGS
284 			  && (dyn->d_un.d_val & DF_TEXTREL) != 0))
285 		    have_textrel = true;
286 		}
287 	    }
288 	  break;
289 
290 	case SHT_SYMTAB:
291 	  symscn = scn;
292 	  break;
293 	}
294     }
295 
296   if (!have_textrel)
297     {
298       error (0, 0, _("no text relocations reported in '%s'"), fname);
299       goto err_elf_close;
300     }
301 
302   int fd2 = -1;
303   Elf *elf2 = NULL;
304   /* Get the address ranges for the loaded segments.  */
305   size_t nsegments_max = 10;
306   size_t nsegments = 0;
307   struct segments *segments
308     = (struct 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 		= (struct segments *) realloc (segments,
338 					       nsegments_max
339 					       * sizeof (segments[0]));
340 	      if (segments == NULL)
341 		{
342 		  error (0, 0, _("\
343 cannot get program header index at offset %zd: %s"),
344 			 i, elf_errmsg (-1));
345 		  result = 1;
346 		  goto next;
347 		}
348 	    }
349 
350 	  segments[nsegments].from = phdr->p_vaddr;
351 	  segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
352 	  ++nsegments;
353 	}
354     }
355 
356   if (nsegments > 0)
357     {
358 
359       Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
360       /* Look for debuginfo files if the information is not the in
361 	 opened file itself.  This makes only sense if the input file
362 	 is specified with an absolute path.  */
363       if (dw == NULL && fname[0] == '/')
364 	{
365 	  size_t debuginfo_rootlen = strlen (debuginfo_root);
366 	  char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
367 					   + fname_len + 8);
368 	  strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
369 						     rootdir_len),
370 					    debuginfo_root,
371 					    debuginfo_rootlen),
372 				   "/"),
373 			   fname, fname_len),
374 		  ".debug");
375 
376 	  fd2 = open (difname, O_RDONLY);
377 	  if (fd2 != -1
378 	      && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
379 	    dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
380 	}
381 
382       /* Look at all relocations and determine which modify
383 	 write-protected segments.  */
384       scn = NULL;
385       while ((scn = elf_nextscn (elf, scn)) != NULL)
386 	{
387 	  /* Handle the section if it is a symbol table.  */
388 	  GElf_Shdr shdr_mem;
389 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
390 
391 	  if (shdr == NULL)
392 	    {
393 	      error (0, 0,
394 		     _("cannot get section header of section %zu: %s"),
395 		     elf_ndxscn (scn), elf_errmsg (-1));
396 	      result = 1;
397 	      goto next;
398 	    }
399 
400 	  if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
401 	      && symscn == NULL)
402 	    {
403 	      symscn = elf_getscn (elf, shdr->sh_link);
404 	      if (symscn == NULL)
405 		{
406 		  error (0, 0, _("\
407 cannot get symbol table section %zu in '%s': %s"),
408 			 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
409 		  result = 1;
410 		  goto next;
411 		}
412 	    }
413 
414 	  if (shdr->sh_type == SHT_REL)
415 	    {
416 	      Elf_Data *data = elf_getdata (scn, NULL);
417 	      size_t entries = (shdr->sh_entsize == 0
418 				? 0 : shdr->sh_size / shdr->sh_entsize);
419 
420 	      for (int cnt = 0;
421 		   (size_t) cnt < entries; ++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, _("\
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 	      size_t entries = (shdr->sh_entsize == 0
442 				? 0 : shdr->sh_size / shdr->sh_entsize);
443 
444 	      for (int cnt = 0; (size_t) cnt < entries; ++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, _("\
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   free (segments);
474   tdestroy (knownsrcs, noop);
475 
476   return result;
477 }
478 
479 
480 static int
ptrcompare(const void * p1,const void * p2)481 ptrcompare (const void *p1, const void *p2)
482 {
483   if ((uintptr_t) p1 < (uintptr_t) p2)
484     return -1;
485   if ((uintptr_t) p1 > (uintptr_t) p2)
486     return 1;
487   return 0;
488 }
489 
490 
491 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)492 check_rel (size_t nsegments, struct segments segments[nsegments],
493 	   GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
494 	   const char *fname, bool more_than_one, void **knownsrcs)
495 {
496   for (size_t cnt = 0; cnt < nsegments; ++cnt)
497     if (segments[cnt].from <= addr && segments[cnt].to > addr)
498       {
499 	Dwarf_Die die_mem;
500 	Dwarf_Die *die;
501 	Dwarf_Line *line;
502 	const char *src;
503 
504 	if (more_than_one)
505 	  printf ("%s: ", fname);
506 
507 	if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
508 	    && (line = dwarf_getsrc_die (die, addr)) != NULL
509 	    && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
510 	  {
511 	    /* There can be more than one relocation against one file.
512 	       Try to avoid multiple messages.  And yes, the code uses
513 	       pointer comparison.  */
514 	    if (tfind (src, knownsrcs, ptrcompare) == NULL)
515 	      {
516 		printf (_("%s not compiled with -fpic/-fPIC\n"), src);
517 		tsearch (src, knownsrcs, ptrcompare);
518 	      }
519 	    return;
520 	  }
521 	else
522 	  {
523 	    /* At least look at the symbol table to see which function
524 	       the modified address is in.  */
525 	    Elf_Data *symdata = elf_getdata (symscn, NULL);
526 	    GElf_Shdr shdr_mem;
527 	    GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
528 	    if (shdr != NULL)
529 	      {
530 		GElf_Addr lowaddr = 0;
531 		int lowidx = -1;
532 		GElf_Addr highaddr = ~0ul;
533 		int highidx = -1;
534 		GElf_Sym sym_mem;
535 		GElf_Sym *sym;
536 		size_t entries = (shdr->sh_entsize == 0
537 				  ? 0 : shdr->sh_size / shdr->sh_entsize);
538 
539 		for (int i = 0; (size_t) i < entries; ++i)
540 		  {
541 		    sym = gelf_getsym (symdata, i, &sym_mem);
542 		    if (sym == NULL)
543 		      continue;
544 
545 		    if (sym->st_value < addr && sym->st_value > lowaddr)
546 		      {
547 			lowaddr = sym->st_value;
548 			lowidx = i;
549 		      }
550 		    if (sym->st_value > addr && sym->st_value < highaddr)
551 		      {
552 			highaddr = sym->st_value;
553 			highidx = i;
554 		      }
555 		  }
556 
557 		if (lowidx != -1)
558 		  {
559 		    sym = gelf_getsym (symdata, lowidx, &sym_mem);
560 		    assert (sym != NULL);
561 
562 		    const char *lowstr = elf_strptr (elf, shdr->sh_link,
563 						     sym->st_name);
564 
565 		    if (sym->st_value + sym->st_size > addr)
566 		      {
567 			/* It is this function.  */
568 			if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
569 			  {
570 			    printf (_("\
571 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
572 				    lowstr);
573 			    tsearch (lowstr, knownsrcs, ptrcompare);
574 			  }
575 		      }
576 		    else if (highidx == -1)
577 		      printf (_("\
578 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
579 			      lowstr);
580 		    else
581 		      {
582 			sym = gelf_getsym (symdata, highidx, &sym_mem);
583 			assert (sym != NULL);
584 
585 			printf (_("\
586 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
587 				lowstr, elf_strptr (elf, shdr->sh_link,
588 						    sym->st_name));
589 		      }
590 		    return;
591 		  }
592 		else if (highidx != -1)
593 		  {
594 		    sym = gelf_getsym (symdata, highidx, &sym_mem);
595 		    assert (sym != NULL);
596 
597 		    printf (_("\
598 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
599 			    elf_strptr (elf, shdr->sh_link, sym->st_name));
600 		    return;
601 		  }
602 	      }
603 	  }
604 
605 	printf (_("\
606 a relocation modifies memory at offset %llu in a write-protected segment\n"),
607 		(unsigned long long int) addr);
608 	break;
609       }
610 }
611 
612 
613 #include "debugpred.h"
614