• 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 "libeu.h"
40 #include "system.h"
41 
42 struct segments
43 {
44   GElf_Addr from;
45   GElf_Addr to;
46 };
47 
48 
49 /* Name and version of program.  */
50 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
51 
52 /* Bug report address.  */
53 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
54 
55 /* Values for the parameters which have no short form.  */
56 #define OPT_DEBUGINFO 0x100
57 
58 /* Definitions of arguments for argp functions.  */
59 static const struct argp_option options[] =
60 {
61   { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
62   { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
63   { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
64     N_("Use PATH as root of debuginfo hierarchy"), 0 },
65 
66   { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
67   { NULL, 0, NULL, 0, NULL, 0 }
68 };
69 
70 /* Short description of program.  */
71 static const char doc[] = N_("\
72 Locate source of text relocations in FILEs (a.out by default).");
73 
74 /* Strings for arguments in help texts.  */
75 static const char args_doc[] = N_("[FILE...]");
76 
77 /* Prototype for option handler.  */
78 static error_t parse_opt (int key, char *arg, struct argp_state *state);
79 
80 /* Data structure to communicate with argp functions.  */
81 static struct argp argp =
82 {
83   options, parse_opt, args_doc, doc, NULL, NULL, NULL
84 };
85 
86 
87 /* Print symbols in file named FNAME.  */
88 static int process_file (const char *fname, bool more_than_one);
89 
90 /* Check for text relocations in the given file.  The segment
91    information is known.  */
92 static void check_rel (size_t nsegments, struct segments segments[nsegments],
93 		       GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
94 		       const char *fname, bool more_than_one,
95 		       void **knownsrcs);
96 
97 
98 
99 /* User-provided root directory.  */
100 static const char *rootdir = "/";
101 
102 /* Root of debuginfo directory hierarchy.  */
103 static const char *debuginfo_root;
104 
105 
106 int
main(int argc,char * argv[])107 main (int argc, char *argv[])
108 {
109   int remaining;
110   int result = 0;
111 
112   /* Set locale.  */
113   (void) setlocale (LC_ALL, "");
114 
115   /* Make sure the message catalog can be found.  */
116   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
117 
118   /* Initialize the message catalog.  */
119   (void) textdomain (PACKAGE_TARNAME);
120 
121   /* Parse and process arguments.  */
122   (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
123 
124   /* Tell the library which version we are expecting.  */
125   elf_version (EV_CURRENT);
126 
127   /* If the user has not specified the root directory for the
128      debuginfo hierarchy, we have to determine it ourselves.  */
129   if (debuginfo_root == NULL)
130     {
131       // XXX The runtime should provide this information.
132 #if defined __ia64__ || defined __alpha__
133       debuginfo_root = "/usr/lib/debug";
134 #else
135       debuginfo_root = (sizeof (long int) == 4
136 			? "/usr/lib/debug" : "/usr/lib64/debug");
137 #endif
138     }
139 
140   if (remaining == argc)
141     result = process_file ("a.out", false);
142   else
143     {
144       /* Process all the remaining files.  */
145       const bool more_than_one = remaining + 1 < argc;
146 
147       do
148 	result |= process_file (argv[remaining], more_than_one);
149       while (++remaining < argc);
150     }
151 
152   return result;
153 }
154 
155 
156 /* Handle program arguments.  */
157 static error_t
parse_opt(int key,char * arg,struct argp_state * state)158 parse_opt (int key, char *arg,
159 	   struct argp_state *state __attribute__ ((unused)))
160 {
161   switch (key)
162     {
163     case 'r':
164       rootdir = arg;
165       break;
166 
167     case OPT_DEBUGINFO:
168       debuginfo_root = arg;
169       break;
170 
171     default:
172       return ARGP_ERR_UNKNOWN;
173     }
174   return 0;
175 }
176 
177 
178 static void
noop(void * arg)179 noop (void *arg __attribute__ ((unused)))
180 {
181 }
182 
183 
184 static int
open_rootdir_file(const char * fname)185 open_rootdir_file (const char *fname)
186 {
187   char *new_fname = NULL;
188   const char *real_fname = fname;
189 
190   if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
191     real_fname = new_fname = xasprintf ("%s/%s", rootdir, fname);
192 
193   int fd = open (real_fname, O_RDONLY);
194   if (fd == -1)
195     error (0, errno, _("cannot open '%s'"), fname);
196 
197   free (new_fname);
198   return fd;
199 }
200 
201 
202 static int
process_file(const char * fname,bool more_than_one)203 process_file (const char *fname, bool more_than_one)
204 {
205   int result = 0;
206   void *knownsrcs = NULL;
207   int fd = open_rootdir_file (fname);
208   if (fd == -1)
209     return 1;
210 
211   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
212   if (elf == NULL)
213     {
214       error (0, 0, _("cannot create ELF descriptor for '%s': %s"),
215 	     fname, elf_errmsg (-1));
216       goto err_close;
217     }
218 
219   /* Make sure the file is a DSO.  */
220   GElf_Ehdr ehdr_mem;
221   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
222   if (ehdr == NULL)
223     {
224       error (0, 0, _("cannot get ELF header '%s': %s"),
225 	     fname, elf_errmsg (-1));
226     err_elf_close:
227       elf_end (elf);
228     err_close:
229       close (fd);
230       return 1;
231     }
232 
233   if (ehdr->e_type != ET_DYN)
234     {
235       error (0, 0, _("'%s' is not a DSO or PIE"), fname);
236       goto err_elf_close;
237     }
238 
239   /* Determine whether the DSO has text relocations at all and locate
240      the symbol table.  */
241   Elf_Scn *symscn = NULL;
242   Elf_Scn *scn = NULL;
243   bool seen_dynamic = false;
244   bool have_textrel = false;
245   while ((scn = elf_nextscn (elf, scn)) != NULL
246 	 && (!seen_dynamic || symscn == NULL))
247     {
248       /* Handle the section if it is a symbol table.  */
249       GElf_Shdr shdr_mem;
250       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
251 
252       if (shdr == NULL)
253 	{
254 	  error (0, 0,
255 		 _("getting get section header of section %zu: %s"),
256 		 elf_ndxscn (scn), elf_errmsg (-1));
257 	  goto err_elf_close;
258 	}
259 
260       switch (shdr->sh_type)
261 	{
262 	case SHT_DYNAMIC:
263 	  if (!seen_dynamic)
264 	    {
265 	      seen_dynamic = true;
266 
267 	      Elf_Data *data = elf_getdata (scn, NULL);
268 	      size_t entries = (shdr->sh_entsize == 0
269 				? 0 : shdr->sh_size / shdr->sh_entsize);
270 
271 	      for (size_t cnt = 0; cnt < entries; ++cnt)
272 		{
273 		  GElf_Dyn dynmem;
274 		  GElf_Dyn *dyn;
275 
276 		  dyn = gelf_getdyn (data, cnt, &dynmem);
277 		  if (dyn == NULL)
278 		    {
279 		      error (0, 0, _("cannot read dynamic section: %s"),
280 			     elf_errmsg (-1));
281 		      goto err_elf_close;
282 		    }
283 
284 		  if (dyn->d_tag == DT_TEXTREL
285 		      || (dyn->d_tag == DT_FLAGS
286 			  && (dyn->d_un.d_val & DF_TEXTREL) != 0))
287 		    have_textrel = true;
288 		}
289 	    }
290 	  break;
291 
292 	case SHT_SYMTAB:
293 	  symscn = scn;
294 	  break;
295 	}
296     }
297 
298   if (!have_textrel)
299     {
300       error (0, 0, _("no text relocations reported in '%s'"), fname);
301       goto err_elf_close;
302     }
303 
304   int fd2 = -1;
305   Elf *elf2 = NULL;
306   /* Get the address ranges for the loaded segments.  */
307   size_t nsegments_max = 10;
308   size_t nsegments = 0;
309   struct segments *segments = malloc (nsegments_max * sizeof (segments[0]));
310   if (segments == NULL)
311     error (1, errno, _("while reading ELF file"));
312 
313   size_t phnum;
314   if (elf_getphdrnum (elf, &phnum) != 0)
315     error (1, 0, _("cannot get program header count: %s"),
316            elf_errmsg (-1));
317 
318 
319   for (size_t i = 0; i < 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 		 _("cannot get program header index at offset %zd: %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 		= realloc (segments, nsegments_max * sizeof (segments[0]));
339 	      if (segments == NULL)
340 		{
341 		  error (0, 0, _("\
342 cannot get program header index at offset %zd: %s"),
343 			 i, elf_errmsg (-1));
344 		  result = 1;
345 		  goto next;
346 		}
347 	    }
348 
349 	  segments[nsegments].from = phdr->p_vaddr;
350 	  segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
351 	  ++nsegments;
352 	}
353     }
354 
355   if (nsegments > 0)
356     {
357 
358       Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
359       /* Look for debuginfo files if the information is not the in
360 	 opened file itself.  This makes only sense if the input file
361 	 is specified with an absolute path.  */
362       if (dw == NULL && fname[0] == '/')
363 	{
364 	  char *difname =
365 	    xasprintf("%s%s/%s.debug", rootdir, debuginfo_root, fname);
366 	  fd2 = open (difname, O_RDONLY);
367 	  free (difname);
368 	  if (fd2 != -1
369 	      && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
370 	    dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
371 	}
372 
373       /* Look at all relocations and determine which modify
374 	 write-protected segments.  */
375       scn = NULL;
376       while ((scn = elf_nextscn (elf, scn)) != NULL)
377 	{
378 	  /* Handle the section if it is a symbol table.  */
379 	  GElf_Shdr shdr_mem;
380 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
381 
382 	  if (shdr == NULL)
383 	    {
384 	      error (0, 0,
385 		     _("cannot get section header of section %zu: %s"),
386 		     elf_ndxscn (scn), elf_errmsg (-1));
387 	      result = 1;
388 	      goto next;
389 	    }
390 
391 	  if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
392 	      && symscn == NULL)
393 	    {
394 	      symscn = elf_getscn (elf, shdr->sh_link);
395 	      if (symscn == NULL)
396 		{
397 		  error (0, 0, _("\
398 cannot get symbol table section %zu in '%s': %s"),
399 			 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
400 		  result = 1;
401 		  goto next;
402 		}
403 	    }
404 
405 	  if (shdr->sh_type == SHT_REL)
406 	    {
407 	      Elf_Data *data = elf_getdata (scn, NULL);
408 	      size_t entries = (shdr->sh_entsize == 0
409 				? 0 : shdr->sh_size / shdr->sh_entsize);
410 
411 	      for (int cnt = 0;
412 		   (size_t) cnt < entries; ++cnt)
413 		{
414 		  GElf_Rel rel_mem;
415 		  GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
416 		  if (rel == NULL)
417 		    {
418 		      error (0, 0, _("\
419 cannot get relocation at index %d in section %zu in '%s': %s"),
420 			     cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
421 		      result = 1;
422 		      goto next;
423 		    }
424 
425 		  check_rel (nsegments, segments, rel->r_offset, elf,
426 			     symscn, dw, fname, more_than_one, &knownsrcs);
427 		}
428 	    }
429 	  else if (shdr->sh_type == SHT_RELA)
430 	    {
431 	      Elf_Data *data = elf_getdata (scn, NULL);
432 	      size_t entries = (shdr->sh_entsize == 0
433 				? 0 : shdr->sh_size / shdr->sh_entsize);
434 
435 	      for (int cnt = 0; (size_t) cnt < entries; ++cnt)
436 		{
437 		  GElf_Rela rela_mem;
438 		  GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
439 		  if (rela == NULL)
440 		    {
441 		      error (0, 0, _("\
442 cannot get relocation at index %d in section %zu in '%s': %s"),
443 			     cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
444 		      result = 1;
445 		      goto next;
446 		    }
447 
448 		  check_rel (nsegments, segments, rela->r_offset, elf,
449 			     symscn, dw, fname, more_than_one, &knownsrcs);
450 		}
451 	    }
452 	}
453 
454       dwarf_end (dw);
455     }
456 
457  next:
458   elf_end (elf);
459   elf_end (elf2);
460   close (fd);
461   if (fd2 != -1)
462     close (fd2);
463 
464   free (segments);
465   tdestroy (knownsrcs, noop);
466 
467   return result;
468 }
469 
470 
471 static int
ptrcompare(const void * p1,const void * p2)472 ptrcompare (const void *p1, const void *p2)
473 {
474   if ((uintptr_t) p1 < (uintptr_t) p2)
475     return -1;
476   if ((uintptr_t) p1 > (uintptr_t) p2)
477     return 1;
478   return 0;
479 }
480 
481 
482 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)483 check_rel (size_t nsegments, struct segments segments[nsegments],
484 	   GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
485 	   const char *fname, bool more_than_one, void **knownsrcs)
486 {
487   for (size_t cnt = 0; cnt < nsegments; ++cnt)
488     if (segments[cnt].from <= addr && segments[cnt].to > addr)
489       {
490 	Dwarf_Die die_mem;
491 	Dwarf_Die *die;
492 	Dwarf_Line *line;
493 	const char *src;
494 
495 	if (more_than_one)
496 	  printf ("%s: ", fname);
497 
498 	if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
499 	    && (line = dwarf_getsrc_die (die, addr)) != NULL
500 	    && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
501 	  {
502 	    /* There can be more than one relocation against one file.
503 	       Try to avoid multiple messages.  And yes, the code uses
504 	       pointer comparison.  */
505 	    if (tfind (src, knownsrcs, ptrcompare) == NULL)
506 	      {
507 		printf (_("%s not compiled with -fpic/-fPIC\n"), src);
508 		tsearch (src, knownsrcs, ptrcompare);
509 	      }
510 	    return;
511 	  }
512 	else
513 	  {
514 	    /* At least look at the symbol table to see which function
515 	       the modified address is in.  */
516 	    Elf_Data *symdata = elf_getdata (symscn, NULL);
517 	    GElf_Shdr shdr_mem;
518 	    GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
519 	    if (shdr != NULL)
520 	      {
521 		GElf_Addr lowaddr = 0;
522 		int lowidx = -1;
523 		GElf_Addr highaddr = ~0ul;
524 		int highidx = -1;
525 		GElf_Sym sym_mem;
526 		GElf_Sym *sym;
527 		size_t entries = (shdr->sh_entsize == 0
528 				  ? 0 : shdr->sh_size / shdr->sh_entsize);
529 
530 		for (int i = 0; (size_t) i < entries; ++i)
531 		  {
532 		    sym = gelf_getsym (symdata, i, &sym_mem);
533 		    if (sym == NULL)
534 		      continue;
535 
536 		    if (sym->st_value < addr && sym->st_value > lowaddr)
537 		      {
538 			lowaddr = sym->st_value;
539 			lowidx = i;
540 		      }
541 		    if (sym->st_value > addr && sym->st_value < highaddr)
542 		      {
543 			highaddr = sym->st_value;
544 			highidx = i;
545 		      }
546 		  }
547 
548 		if (lowidx != -1)
549 		  {
550 		    sym = gelf_getsym (symdata, lowidx, &sym_mem);
551 		    assert (sym != NULL);
552 
553 		    const char *lowstr = elf_strptr (elf, shdr->sh_link,
554 						     sym->st_name);
555 
556 		    if (sym->st_value + sym->st_size > addr)
557 		      {
558 			/* It is this function.  */
559 			if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
560 			  {
561 			    printf (_("\
562 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
563 				    lowstr);
564 			    tsearch (lowstr, knownsrcs, ptrcompare);
565 			  }
566 		      }
567 		    else if (highidx == -1)
568 		      printf (_("\
569 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
570 			      lowstr);
571 		    else
572 		      {
573 			sym = gelf_getsym (symdata, highidx, &sym_mem);
574 			assert (sym != NULL);
575 
576 			printf (_("\
577 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
578 				lowstr, elf_strptr (elf, shdr->sh_link,
579 						    sym->st_name));
580 		      }
581 		    return;
582 		  }
583 		else if (highidx != -1)
584 		  {
585 		    sym = gelf_getsym (symdata, highidx, &sym_mem);
586 		    assert (sym != NULL);
587 
588 		    printf (_("\
589 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
590 			    elf_strptr (elf, shdr->sh_link, sym->st_name));
591 		    return;
592 		  }
593 	      }
594 	  }
595 
596 	printf (_("\
597 a relocation modifies memory at offset %llu in a write-protected segment\n"),
598 		(unsigned long long int) addr);
599 	break;
600       }
601 }
602 
603 
604 #include "debugpred.h"
605