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