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