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