commit 1662bc3880ae5acae6aa2a3013d193223c36f189
Author: Mark Wielaard <mjw@redhat.com>
Date:   Mon May 16 11:33:11 2011 +0200

    strip: Add --reloc-debug-sections option.

diff --git a/src/strip.c b/src/strip.c
index 8c3c57d..f680061 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -53,6 +53,7 @@
 #include <libebl.h>
 #include <system.h>
 
+typedef uint8_t GElf_Byte;
 
 /* Name and version of program.  */
 static void print_version (FILE *stream, struct argp_state *state);
@@ -66,6 +67,7 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
 #define OPT_REMOVE_COMMENT	0x100
 #define OPT_PERMISSIVE		0x101
 #define OPT_STRIP_SECTIONS	0x102
+#define OPT_RELOC_DEBUG 	0x103
 
 
 /* Definitions of arguments for argp functions.  */
@@ -85,6 +87,8 @@ static const struct argp_option options[] =
     N_("Remove section headers (not recommended)"), 0 },
   { "preserve-dates", 'p', NULL, 0,
     N_("Copy modified/access timestamps to the output"), 0 },
+  { "reloc-debug-sections", OPT_RELOC_DEBUG, NULL, 0,
+    N_("Resolve all trivial relocations between debug sections if the removed sections are placed in a debug file (only relevant for ET_REL files, operation is not reversable, needs -f)"), 0 },
   { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0,
     N_("Remove .comment section"), 0 },
   { "remove-section", 'R', "SECTION", OPTION_HIDDEN, NULL, 0 },
@@ -149,6 +153,9 @@ static bool remove_shdrs;
 /* If true relax some ELF rules for input files.  */
 static bool permissive;
 
+/* If true perform relocations between debug sections.  */
+static bool reloc_debug;
+
 
 int
 main (int argc, char *argv[])
@@ -177,6 +184,10 @@ main (int argc, char *argv[])
   if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
     return EXIT_FAILURE;
 
+  if (reloc_debug && debug_fname == NULL)
+    error (EXIT_FAILURE, 0,
+	   gettext ("--reloc-debug-sections used without -f"));
+
   /* Tell the library which version we are expecting.  */
   elf_version (EV_CURRENT);
 
@@ -253,6 +264,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       preserve_dates = true;
       break;
 
+    case OPT_RELOC_DEBUG:
+      reloc_debug = true;
+      break;
+
     case OPT_REMOVE_COMMENT:
       remove_comment = true;
       break;
@@ -447,10 +462,12 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 
   int debug_fd = -1;
 
-  /* Get the EBL handling.  The -g option is currently the only reason
+  /* Get the EBL handling.  Removing all debugging symbols with the -g
+     option or resolving all relocations between debug sections with
+     the --reloc-debug-sections option are currently the only reasons
      we need EBL so don't open the backend unless necessary.  */
   Ebl *ebl = NULL;
-  if (remove_debug)
+  if (remove_debug || reloc_debug)
     {
       ebl = ebl_openbackend (elf);
       if (ebl == NULL)
@@ -1609,6 +1626,200 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 	  }
       }
 
+  /* Remove any relocations between debug sections in ET_REL
+     for the debug file when requested.  These relocations are always
+     zero based between the unallocated sections.  */
+  if (debug_fname != NULL && reloc_debug && ehdr->e_type == ET_REL)
+    {
+      scn = NULL;
+      cnt = 0;
+      while ((scn = elf_nextscn (debugelf, scn)) != NULL)
+	{
+	  cnt++;
+	  /* We need the actual section and header from the debugelf
+	     not just the cached original in shdr_info because we
+	     might want to change the size.  */
+	  GElf_Shdr shdr_mem;
+	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+	  if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+	    {
+	      /* Make sure that this relocation section points to a
+		 section to relocate with contents, that isn't
+		 allocated and that is a debug section.  */
+	      Elf_Scn *tscn = elf_getscn (debugelf, shdr->sh_info);
+	      GElf_Shdr tshdr_mem;
+	      GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
+	      if (tshdr->sh_type == SHT_NOBITS
+		  || tshdr->sh_size == 0
+		  || (tshdr->sh_flags & SHF_ALLOC) != 0)
+		continue;
+
+	      const char *tname =  elf_strptr (debugelf, shstrndx,
+					       tshdr->sh_name);
+	      if (! tname || ! ebl_debugscn_p (ebl, tname))
+		continue;
+
+	      /* OK, lets relocate all trivial cross debug section
+		 relocations. */
+	      Elf_Data *reldata = elf_getdata (scn, NULL);
+	      /* We actually wanted the rawdata, but since we already
+		 accessed it earlier as elf_getdata () that won't
+		 work. But debug sections are all ELF_T_BYTE, so it
+		 doesn't really matter.  */
+	      Elf_Data *tdata = elf_getdata (tscn, NULL);
+	      if (tdata->d_type != ELF_T_BYTE)
+		INTERNAL_ERROR (fname);
+
+	      /* Pick up the symbol table and shndx table to
+		 resolve relocation symbol indexes.  */
+	      Elf64_Word symt = shdr->sh_link;
+	      Elf_Data *symdata, *xndxdata;
+	      symdata = (shdr_info[symt].debug_data
+			 ?: shdr_info[symt].data);
+	      xndxdata = (shdr_info[shdr_info[symt].symtab_idx].debug_data
+			  ?: shdr_info[shdr_info[symt].symtab_idx].data);
+
+	      /* Apply one relocation.  Returns true when trivial
+		 relocation actually done.  */
+	      bool relocate (GElf_Addr offset, const GElf_Sxword addend,
+			     int rtype, int symndx)
+	      {
+		/* R_*_NONE relocs can always just be removed.  */
+		if (rtype == 0)
+		  return true;
+
+		/* We only do simple absolute relocations.  */
+		Elf_Type type = ebl_reloc_simple_type (ebl, rtype);
+		if (type == ELF_T_NUM)
+		  return false;
+
+		/* These are the types we can relocate.  */
+#define TYPES   DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half);		\
+		DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword);		\
+		DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
+
+		/* And only for relocations against other debug sections.  */
+		GElf_Sym sym_mem;
+		Elf32_Word xndx;
+		GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
+						  symndx, &sym_mem,
+						  &xndx);
+		if (GELF_ST_TYPE (sym->st_info) == STT_SECTION)
+		  {
+		    Elf32_Word sec = (sym->st_shndx == SHN_XINDEX
+				      ? xndx : sym->st_shndx);
+		    if (ebl_debugscn_p (ebl, shdr_info[sec].name))
+		      {
+			size_t size;
+			switch (type)
+			  {
+#define DO_TYPE(NAME, Name)					\
+			    case ELF_T_##NAME:			\
+			      size = sizeof (GElf_##Name);	\
+			      break;
+			    TYPES;
+#undef DO_TYPE
+			  default:
+			    return false;
+			  }
+
+			if (offset + size > tdata->d_size)
+			  error (0, 0, gettext ("bad relocation"));
+
+			/* For SHT_REL sections this is all that needs
+			   to be checked.  The addend is contained in
+			   the original data at the offset already.
+			   And the (section) symbol address is zero.
+			   So just remove the relocation, it isn't
+			   needed anymore.  */
+			if (addend == 0)
+			  return true;
+
+#define DO_TYPE(NAME, Name) GElf_##Name Name;
+			union { TYPES; } tmpbuf;
+#undef DO_TYPE
+			Elf_Data tmpdata =
+			  {
+			    .d_type = type,
+			    .d_buf = &tmpbuf,
+			    .d_size = size,
+			    .d_version = EV_CURRENT,
+			  };
+			Elf_Data rdata =
+			  {
+			    .d_type = type,
+			    .d_buf = tdata->d_buf + offset,
+			    .d_size = size,
+			    .d_version = EV_CURRENT,
+			  };
+
+			/* For SHT_RELA sections we just take the
+			   addend and put it into the relocation slot.
+			   The (section) symbol address can be
+			   ignored, since it is zero.  */
+			switch (type)
+			  {
+#define DO_TYPE(NAME, Name)				\
+			    case ELF_T_##NAME:		\
+			      tmpbuf.Name = addend;	\
+			      break;
+			    TYPES;
+#undef DO_TYPE
+			  default:
+			    abort ();
+			  }
+
+			Elf_Data *s = gelf_xlatetof (debugelf, &rdata,
+						     &tmpdata,
+						     ehdr->e_ident[EI_DATA]);
+			if (s == NULL)
+			  INTERNAL_ERROR (fname);
+			assert (s == &rdata);
+
+			return true;
+		      }
+		  }
+		return false;
+	      }
+
+	      size_t nrels = shdr->sh_size / shdr->sh_entsize;
+	      size_t next = 0;
+	      if (shdr->sh_type == SHT_REL)
+		for (size_t relidx = 0; relidx < nrels; ++relidx)
+		  {
+		    GElf_Rel rel_mem;
+		    GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
+		    if (! relocate (r->r_offset, 0,
+				    GELF_R_TYPE (r->r_info),
+				    GELF_R_SYM (r->r_info)))
+		      {
+			if (relidx != next)
+			  gelf_update_rel (reldata, next, r);
+			++next;
+		      }
+		  }
+	      else
+		for (size_t relidx = 0; relidx < nrels; ++relidx)
+		  {
+		    GElf_Rela rela_mem;
+		    GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
+		    if (! relocate (r->r_offset, r->r_addend,
+				    GELF_R_TYPE (r->r_info),
+				    GELF_R_SYM (r->r_info)))
+		      {
+			if (relidx != next)
+			  gelf_update_rela (reldata, next, r);
+			++next;
+		      }
+		  }
+
+	      nrels = next;
+	      shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize;
+	      gelf_update_shdr (scn, shdr);
+	    }
+	}
+    }
+
   /* Now that we have done all adjustments to the data,
      we can actually write out the debug file.  */
   if (debug_fname != NULL)
