Squashed 'third_party/elfutils/' content from commit 555e15e

Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/src/findtextrel.c b/src/findtextrel.c
new file mode 100644
index 0000000..8f1e239
--- /dev/null
+++ b/src/findtextrel.c
@@ -0,0 +1,611 @@
+/* Locate source files or functions which caused text relocations.
+   Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc.
+   This file is part of elfutils.
+   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libdw.h>
+#include <libintl.h>
+#include <locale.h>
+#include <search.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <printversion.h>
+
+
+struct segments
+{
+  GElf_Addr from;
+  GElf_Addr to;
+};
+
+
+/* Name and version of program.  */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address.  */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* Values for the parameters which have no short form.  */
+#define OPT_DEBUGINFO 0x100
+
+/* Definitions of arguments for argp functions.  */
+static const struct argp_option options[] =
+{
+  { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
+  { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
+  { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
+    N_("Use PATH as root of debuginfo hierarchy"), 0 },
+
+  { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+  { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program.  */
+static const char doc[] = N_("\
+Locate source of text relocations in FILEs (a.out by default).");
+
+/* Strings for arguments in help texts.  */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler.  */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions.  */
+static struct argp argp =
+{
+  options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* Print symbols in file named FNAME.  */
+static int process_file (const char *fname, bool more_than_one);
+
+/* Check for text relocations in the given file.  The segment
+   information is known.  */
+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);
+
+
+
+/* User-provided root directory.  */
+static const char *rootdir = "/";
+
+/* Root of debuginfo directory hierarchy.  */
+static const char *debuginfo_root;
+
+
+int
+main (int argc, char *argv[])
+{
+  int remaining;
+  int result = 0;
+
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  /* Make sure the message catalog can be found.  */
+  (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+  /* Initialize the message catalog.  */
+  (void) textdomain (PACKAGE_TARNAME);
+
+  /* Parse and process arguments.  */
+  (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+  /* Tell the library which version we are expecting.  */
+  elf_version (EV_CURRENT);
+
+  /* If the user has not specified the root directory for the
+     debuginfo hierarchy, we have to determine it ourselves.  */
+  if (debuginfo_root == NULL)
+    {
+      // XXX The runtime should provide this information.
+#if defined __ia64__ || defined __alpha__
+      debuginfo_root = "/usr/lib/debug";
+#else
+      debuginfo_root = (sizeof (long int) == 4
+			? "/usr/lib/debug" : "/usr/lib64/debug");
+#endif
+    }
+
+  if (remaining == argc)
+    result = process_file ("a.out", false);
+  else
+    {
+      /* Process all the remaining files.  */
+      const bool more_than_one = remaining + 1 < argc;
+
+      do
+	result |= process_file (argv[remaining], more_than_one);
+      while (++remaining < argc);
+    }
+
+  return result;
+}
+
+
+/* Handle program arguments.  */
+static error_t
+parse_opt (int key, char *arg,
+	   struct argp_state *state __attribute__ ((unused)))
+{
+  switch (key)
+    {
+    case 'r':
+      rootdir = arg;
+      break;
+
+    case OPT_DEBUGINFO:
+      debuginfo_root = arg;
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+
+static void
+noop (void *arg __attribute__ ((unused)))
+{
+}
+
+
+static int
+process_file (const char *fname, bool more_than_one)
+{
+  int result = 0;
+  void *knownsrcs = NULL;
+
+  size_t fname_len = strlen (fname);
+  size_t rootdir_len = strlen (rootdir);
+  const char *real_fname = fname;
+  if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
+    {
+      /* Prepend the user-provided root directory.  */
+      char *new_fname = alloca (rootdir_len + fname_len + 2);
+      *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
+				  "/"),
+			  fname, fname_len)) = '\0';
+      real_fname = new_fname;
+    }
+
+  int fd = open (real_fname, O_RDONLY);
+  if (fd == -1)
+    {
+      error (0, errno, gettext ("cannot open '%s'"), fname);
+      return 1;
+    }
+
+  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+  if (elf == NULL)
+    {
+      error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
+	     fname, elf_errmsg (-1));
+      goto err_close;
+    }
+
+  /* Make sure the file is a DSO.  */
+  GElf_Ehdr ehdr_mem;
+  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+  if (ehdr == NULL)
+    {
+      error (0, 0, gettext ("cannot get ELF header '%s': %s"),
+	     fname, elf_errmsg (-1));
+    err_elf_close:
+      elf_end (elf);
+    err_close:
+      close (fd);
+      return 1;
+    }
+
+  if (ehdr->e_type != ET_DYN)
+    {
+      error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname);
+      goto err_elf_close;
+    }
+
+  /* Determine whether the DSO has text relocations at all and locate
+     the symbol table.  */
+  Elf_Scn *symscn = NULL;
+  Elf_Scn *scn = NULL;
+  bool seen_dynamic = false;
+  bool have_textrel = false;
+  while ((scn = elf_nextscn (elf, scn)) != NULL
+	 && (!seen_dynamic || symscn == NULL))
+    {
+      /* Handle the section if it is a symbol table.  */
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+      if (shdr == NULL)
+	{
+	  error (0, 0,
+		 gettext ("getting get section header of section %zu: %s"),
+		 elf_ndxscn (scn), elf_errmsg (-1));
+	  goto err_elf_close;
+	}
+
+      switch (shdr->sh_type)
+	{
+	case SHT_DYNAMIC:
+	  if (!seen_dynamic)
+	    {
+	      seen_dynamic = true;
+
+	      Elf_Data *data = elf_getdata (scn, NULL);
+
+	      for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize;
+		   ++cnt)
+		{
+		  GElf_Dyn dynmem;
+		  GElf_Dyn *dyn;
+
+		  dyn = gelf_getdyn (data, cnt, &dynmem);
+		  if (dyn == NULL)
+		    {
+		      error (0, 0, gettext ("cannot read dynamic section: %s"),
+			     elf_errmsg (-1));
+		      goto err_elf_close;
+		    }
+
+		  if (dyn->d_tag == DT_TEXTREL
+		      || (dyn->d_tag == DT_FLAGS
+			  && (dyn->d_un.d_val & DF_TEXTREL) != 0))
+		    have_textrel = true;
+		}
+	    }
+	  break;
+
+	case SHT_SYMTAB:
+	  symscn = scn;
+	  break;
+	}
+    }
+
+  if (!have_textrel)
+    {
+      error (0, 0, gettext ("no text relocations reported in '%s'"), fname);
+      goto err_elf_close;
+    }
+
+  int fd2 = -1;
+  Elf *elf2 = NULL;
+  /* Get the address ranges for the loaded segments.  */
+  size_t nsegments_max = 10;
+  size_t nsegments = 0;
+  struct segments *segments
+    = (struct segments *) malloc (nsegments_max * sizeof (segments[0]));
+  if (segments == NULL)
+    error (1, errno, gettext ("while reading ELF file"));
+
+  size_t phnum;
+  if (elf_getphdrnum (elf, &phnum) != 0)
+    error (1, 0, gettext ("cannot get program header count: %s"),
+           elf_errmsg (-1));
+
+
+  for (size_t i = 0; i < phnum; ++i)
+    {
+      GElf_Phdr phdr_mem;
+      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
+      if (phdr == NULL)
+	{
+	  error (0, 0,
+		 gettext ("cannot get program header index at offset %zd: %s"),
+		 i, elf_errmsg (-1));
+	  result = 1;
+	  goto next;
+	}
+
+      if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
+	{
+	  if (nsegments == nsegments_max)
+	    {
+	      nsegments_max *= 2;
+	      segments
+		= (struct segments *) realloc (segments,
+					       nsegments_max
+					       * sizeof (segments[0]));
+	      if (segments == NULL)
+		{
+		  error (0, 0, gettext ("\
+cannot get program header index at offset %zd: %s"),
+			 i, elf_errmsg (-1));
+		  result = 1;
+		  goto next;
+		}
+	    }
+
+	  segments[nsegments].from = phdr->p_vaddr;
+	  segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
+	  ++nsegments;
+	}
+    }
+
+  if (nsegments > 0)
+    {
+
+      Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
+      /* Look for debuginfo files if the information is not the in
+	 opened file itself.  This makes only sense if the input file
+	 is specified with an absolute path.  */
+      if (dw == NULL && fname[0] == '/')
+	{
+	  size_t debuginfo_rootlen = strlen (debuginfo_root);
+	  char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
+					   + fname_len + 8);
+	  strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
+						     rootdir_len),
+					    debuginfo_root,
+					    debuginfo_rootlen),
+				   "/"),
+			   fname, fname_len),
+		  ".debug");
+
+	  fd2 = open (difname, O_RDONLY);
+	  if (fd2 != -1
+	      && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
+	    dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
+	}
+
+      /* Look at all relocations and determine which modify
+	 write-protected segments.  */
+      scn = NULL;
+      while ((scn = elf_nextscn (elf, scn)) != NULL)
+	{
+	  /* Handle the section if it is a symbol table.  */
+	  GElf_Shdr shdr_mem;
+	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+	  if (shdr == NULL)
+	    {
+	      error (0, 0,
+		     gettext ("cannot get section header of section %zu: %s"),
+		     elf_ndxscn (scn), elf_errmsg (-1));
+	      result = 1;
+	      goto next;
+	    }
+
+	  if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+	      && symscn == NULL)
+	    {
+	      symscn = elf_getscn (elf, shdr->sh_link);
+	      if (symscn == NULL)
+		{
+		  error (0, 0, gettext ("\
+cannot get symbol table section %zu in '%s': %s"),
+			 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
+		  result = 1;
+		  goto next;
+		}
+	    }
+
+	  if (shdr->sh_type == SHT_REL)
+	    {
+	      Elf_Data *data = elf_getdata (scn, NULL);
+
+	      for (int cnt = 0;
+		   (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
+		   ++cnt)
+		{
+		  GElf_Rel rel_mem;
+		  GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
+		  if (rel == NULL)
+		    {
+		      error (0, 0, gettext ("\
+cannot get relocation at index %d in section %zu in '%s': %s"),
+			     cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
+		      result = 1;
+		      goto next;
+		    }
+
+		  check_rel (nsegments, segments, rel->r_offset, elf,
+			     symscn, dw, fname, more_than_one, &knownsrcs);
+		}
+	    }
+	  else if (shdr->sh_type == SHT_RELA)
+	    {
+	      Elf_Data *data = elf_getdata (scn, NULL);
+
+	      for (int cnt = 0;
+		   (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
+		   ++cnt)
+		{
+		  GElf_Rela rela_mem;
+		  GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
+		  if (rela == NULL)
+		    {
+		      error (0, 0, gettext ("\
+cannot get relocation at index %d in section %zu in '%s': %s"),
+			     cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
+		      result = 1;
+		      goto next;
+		    }
+
+		  check_rel (nsegments, segments, rela->r_offset, elf,
+			     symscn, dw, fname, more_than_one, &knownsrcs);
+		}
+	    }
+	}
+
+      dwarf_end (dw);
+    }
+
+ next:
+  elf_end (elf);
+  elf_end (elf2);
+  close (fd);
+  if (fd2 != -1)
+    close (fd2);
+
+  free (segments);
+  tdestroy (knownsrcs, noop);
+
+  return result;
+}
+
+
+static int
+ptrcompare (const void *p1, const void *p2)
+{
+  if ((uintptr_t) p1 < (uintptr_t) p2)
+    return -1;
+  if ((uintptr_t) p1 > (uintptr_t) p2)
+    return 1;
+  return 0;
+}
+
+
+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)
+{
+  for (size_t cnt = 0; cnt < nsegments; ++cnt)
+    if (segments[cnt].from <= addr && segments[cnt].to > addr)
+      {
+	Dwarf_Die die_mem;
+	Dwarf_Die *die;
+	Dwarf_Line *line;
+	const char *src;
+
+	if (more_than_one)
+	  printf ("%s: ", fname);
+
+	if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
+	    && (line = dwarf_getsrc_die (die, addr)) != NULL
+	    && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
+	  {
+	    /* There can be more than one relocation against one file.
+	       Try to avoid multiple messages.  And yes, the code uses
+	       pointer comparison.  */
+	    if (tfind (src, knownsrcs, ptrcompare) == NULL)
+	      {
+		printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src);
+		tsearch (src, knownsrcs, ptrcompare);
+	      }
+	    return;
+	  }
+	else
+	  {
+	    /* At least look at the symbol table to see which function
+	       the modified address is in.  */
+	    Elf_Data *symdata = elf_getdata (symscn, NULL);
+	    GElf_Shdr shdr_mem;
+	    GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
+	    if (shdr != NULL)
+	      {
+		GElf_Addr lowaddr = 0;
+		int lowidx = -1;
+		GElf_Addr highaddr = ~0ul;
+		int highidx = -1;
+		GElf_Sym sym_mem;
+		GElf_Sym *sym;
+
+		for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize;
+		     ++i)
+		  {
+		    sym = gelf_getsym (symdata, i, &sym_mem);
+		    if (sym == NULL)
+		      continue;
+
+		    if (sym->st_value < addr && sym->st_value > lowaddr)
+		      {
+			lowaddr = sym->st_value;
+			lowidx = i;
+		      }
+		    if (sym->st_value > addr && sym->st_value < highaddr)
+		      {
+			highaddr = sym->st_value;
+			highidx = i;
+		      }
+		  }
+
+		if (lowidx != -1)
+		  {
+		    sym = gelf_getsym (symdata, lowidx, &sym_mem);
+		    assert (sym != NULL);
+
+		    const char *lowstr = elf_strptr (elf, shdr->sh_link,
+						     sym->st_name);
+
+		    if (sym->st_value + sym->st_size > addr)
+		      {
+			/* It is this function.  */
+			if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
+			  {
+			    printf (gettext ("\
+the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
+				    lowstr);
+			    tsearch (lowstr, knownsrcs, ptrcompare);
+			  }
+		      }
+		    else if (highidx == -1)
+		      printf (gettext ("\
+the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
+			      lowstr);
+		    else
+		      {
+			sym = gelf_getsym (symdata, highidx, &sym_mem);
+			assert (sym != NULL);
+
+			printf (gettext ("\
+either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
+				lowstr, elf_strptr (elf, shdr->sh_link,
+						    sym->st_name));
+		      }
+		    return;
+		  }
+		else if (highidx != -1)
+		  {
+		    sym = gelf_getsym (symdata, highidx, &sym_mem);
+		    assert (sym != NULL);
+
+		    printf (gettext ("\
+the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
+			    elf_strptr (elf, shdr->sh_link, sym->st_name));
+		    return;
+		  }
+	      }
+	  }
+
+	printf (gettext ("\
+a relocation modifies memory at offset %llu in a write-protected segment\n"),
+		(unsigned long long int) addr);
+	break;
+      }
+}
+
+
+#include "debugpred.h"