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