This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 4/4] strip: Add --reloc-debug-sections-only option.


This option does the same thing as --reloc-debug-sections without doing
any other strip operation. This is useful when you want to remove the
debug section relocations in a separate ET_REL debug file that was created
without --reloc-debug-sections, or for a file (like the linux debug vmlinux)
that you don't want to strip, but for which the debug section relocations
can be resolved already.

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 src/ChangeLog            |  11 ++++
 src/strip.c              | 155 +++++++++++++++++++++++++++++++++++++++++++++--
 tests/ChangeLog          |   4 ++
 tests/run-strip-reloc.sh |  11 ++++
 4 files changed, 175 insertions(+), 6 deletions(-)

diff --git a/src/ChangeLog b/src/ChangeLog
index 766c839..0eed9ae 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,14 @@
+2018-10-26  Mark Wielaard  <mark@klomp.org>
+
+	* strip.c (OPT_RELOC_DEBUG_ONLY): New define.
+	(options): Add reloc-debug-sections-only.
+	(reloc_debug_only): New static bool.
+	(main): Check reloc_debug_only is the only strip option used.
+	(parse_opt): Handle OPT_RELOC_DEBUG_ONLY.
+	(handle_debug_relocs): New function.
+	(handle_elf): Add local variables lastsec_offset and lastsec_size.
+	Handle reloc_debug_only.
+
 2018-10-24  Mark Wielaard  <mark@klomp.org>
 
 	* strip.c (handle_elf): Extract code to update shdrstrndx into...
diff --git a/src/strip.c b/src/strip.c
index 1151206..e953c4d 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -1,5 +1,5 @@
 /* Discard section not used at runtime from object files.
-   Copyright (C) 2000-2012, 2014, 2015, 2016, 2017 Red Hat, Inc.
+   Copyright (C) 2000-2012, 2014, 2015, 2016, 2017, 2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2000.
 
@@ -61,6 +61,7 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
 #define OPT_STRIP_SECTIONS	0x102
 #define OPT_RELOC_DEBUG 	0x103
 #define OPT_KEEP_SECTION 	0x104
+#define OPT_RELOC_DEBUG_ONLY    0x105
 
 
 /* Definitions of arguments for argp functions.  */
@@ -82,6 +83,8 @@ static const struct argp_option options[] =
     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 },
+  { "reloc-debug-sections-only", OPT_RELOC_DEBUG_ONLY, NULL, 0,
+    N_("Similar to --reloc-debug-sections, but resolve all trivial relocations between debug sections in place.  No other stripping is performed (operation is not reversable, incompatible with -f, -g, --remove-comment and --remove-section)"), 0 },
   { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0,
     N_("Remove .comment section"), 0 },
   { "remove-section", 'R', "SECTION", 0, N_("Remove the named section.  SECTION is an extended wildcard pattern.  May be given more than once.  Only non-allocated sections can be removed."), 0 },
@@ -159,6 +162,9 @@ static bool permissive;
 /* If true perform relocations between debug sections.  */
 static bool reloc_debug;
 
+/* If true perform relocations between debug sections only.  */
+static bool reloc_debug_only;
+
 /* Sections the user explicitly wants to keep or remove.  */
 struct section_pattern
 {
@@ -240,6 +246,12 @@ main (int argc, char *argv[])
     error (EXIT_FAILURE, 0,
 	   gettext ("--reloc-debug-sections used without -f"));
 
+  if (reloc_debug_only &&
+      (debug_fname != NULL || remove_secs != NULL
+       || remove_comment == true || remove_debug == true))
+    error (EXIT_FAILURE, 0,
+	   gettext ("--reloc-debug-sections-only incompatible with -f, -g, --remove-comment and --remove-section"));
+
   /* Tell the library which version we are expecting.  */
   elf_version (EV_CURRENT);
 
@@ -307,6 +319,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       reloc_debug = true;
       break;
 
+    case OPT_RELOC_DEBUG_ONLY:
+      reloc_debug_only = true;
+      break;
+
     case OPT_REMOVE_COMMENT:
       remove_comment = true;
       break;
@@ -774,6 +790,116 @@ process_file (const char *fname)
   return result;
 }
 
+/* Processing for --reloc-debug-sections-only.  */
+static int
+handle_debug_relocs (Elf *elf, Ebl *ebl, Elf *new_elf,
+		     GElf_Ehdr *ehdr, const char *fname, size_t shstrndx,
+		     GElf_Off *last_offset, GElf_Xword *last_size)
+{
+
+  /* Copy over the ELF header.  */
+  if (gelf_update_ehdr (new_elf, ehdr) == 0)
+    {
+      error (0, 0, "couldn't update new ehdr: %s", elf_errmsg (-1));
+      return 1;
+    }
+
+  /* Copy over sections and record end of allocated sections.  */
+  GElf_Off lastoffset = 0;
+  Elf_Scn *scn = NULL;
+  while ((scn = elf_nextscn (elf, scn)) != NULL)
+    {
+      /* Get the header.  */
+      GElf_Shdr shdr;
+      if (gelf_getshdr (scn, &shdr) == NULL)
+	{
+	  error (0, 0, "couldn't get shdr: %s", elf_errmsg (-1));
+	  return 1;
+	}
+
+      /* Create new section.  */
+      Elf_Scn *new_scn = elf_newscn (new_elf);
+      if (new_scn == NULL)
+	{
+	  error (0, 0, "couldn't create new section: %s", elf_errmsg (-1));
+	  return 1;
+	}
+
+      if (gelf_update_shdr (new_scn, &shdr) == 0)
+	{
+	  error (0, 0, "couldn't update shdr: %s", elf_errmsg (-1));
+	  return 1;
+	}
+
+      /* Copy over section data.  */
+      Elf_Data *data = NULL;
+      while ((data = elf_getdata (scn, data)) != NULL)
+	{
+	  Elf_Data *new_data = elf_newdata (new_scn);
+	  if (new_data == NULL)
+	    {
+	      error (0, 0, "couldn't create new section data: %s",
+		     elf_errmsg (-1));
+	      return 1;
+	    }
+	  *new_data = *data;
+	}
+
+      /* Record last offset of allocated section.  */
+      if ((shdr.sh_flags & SHF_ALLOC) != 0)
+	{
+	  GElf_Off filesz = (shdr.sh_type != SHT_NOBITS
+			     ? shdr.sh_size : 0);
+	  if (lastoffset < shdr.sh_offset + filesz)
+	    lastoffset = shdr.sh_offset + filesz;
+	}
+    }
+
+  /* Make sure section header name table is setup correctly, we'll
+     need it to determine whether to relocate sections.  */
+  if (update_shdrstrndx (new_elf, shstrndx) != 0)
+    {
+      error (0, 0, "error updating shdrstrndx: %s", elf_errmsg (-1));
+      return 1;
+    }
+
+  /* Adjust the relocation sections.  */
+  remove_debug_relocations (ebl, new_elf, ehdr, fname, shstrndx);
+
+  /* Adjust the offsets of the non-allocated sections, so they come after
+     the allocated sections.  */
+  scn = NULL;
+  while ((scn = elf_nextscn (new_elf, scn)) != NULL)
+    {
+      /* Get the header.  */
+      GElf_Shdr shdr;
+      if (gelf_getshdr (scn, &shdr) == NULL)
+	{
+	  error (0, 0, "couldn't get shdr: %s", elf_errmsg (-1));
+	  return 1;
+	}
+
+      /* Adjust non-allocated section offsets to be after any allocated.  */
+      if ((shdr.sh_flags & SHF_ALLOC) == 0)
+	{
+	  shdr.sh_offset = ((lastoffset + shdr.sh_addralign - 1)
+			    & ~((GElf_Off) (shdr.sh_addralign - 1)));
+	  if (gelf_update_shdr (scn, &shdr) == 0)
+	    {
+	      error (0, 0, "couldn't update shdr: %s", elf_errmsg (-1));
+	      return 1;
+	    }
+
+	  GElf_Off filesz = (shdr.sh_type != SHT_NOBITS
+			     ? shdr.sh_size : 0);
+	  lastoffset = shdr.sh_offset + filesz;
+	  *last_offset = shdr.sh_offset;
+	  *last_size = filesz;
+	}
+    }
+
+  return 0;
+}
 
 /* Maximum size of array allocated on stack.  */
 #define MAX_STACK_ALLOC	(400 * 1024)
@@ -790,6 +916,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
   tmp_debug_fname = NULL;
   int result = 0;
   size_t shdridx = 0;
+  GElf_Off lastsec_offset = 0;
+  Elf64_Xword lastsec_size = 0;
   size_t shstrndx;
   struct shdr_info
   {
@@ -848,7 +976,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
      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 || reloc_debug)
+  if (remove_debug || reloc_debug || reloc_debug_only)
     {
       ebl = ebl_openbackend (elf);
       if (ebl == NULL)
@@ -937,6 +1065,18 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 	}
     }
 
+  if (reloc_debug_only)
+    {
+      if (handle_debug_relocs (elf, ebl, newelf, ehdr, fname, shstrndx,
+			       &lastsec_offset, &lastsec_size) != 0)
+	{
+	  result = 1;
+	  goto fail_close;
+	}
+      idx = shstrndx;
+      goto done; /* Skip all actual stripping operations.  */
+    }
+
   if (debug_fname != NULL)
     {
       /* Also create an ELF descriptor for the debug file */
@@ -2339,6 +2479,10 @@ while computing checksum for debug information"));
 	}
     }
 
+  lastsec_offset = shdr_info[shdridx].shdr.sh_offset;
+  lastsec_size = shdr_info[shdridx].shdr.sh_size;
+
+ done:
   /* Finally finish the ELF header.  Fill in the fields not handled by
      libelf from the old file.  */
   newehdr = gelf_getehdr (newelf, &newehdr_mem);
@@ -2355,8 +2499,7 @@ while computing checksum for debug information"));
 
   /* We need to position the section header table.  */
   const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
-  newehdr->e_shoff = ((shdr_info[shdridx].shdr.sh_offset
-		       + shdr_info[shdridx].shdr.sh_size + offsize - 1)
+  newehdr->e_shoff = ((lastsec_offset + lastsec_size + offsize - 1)
 		      & ~((GElf_Off) (offsize - 1)));
   newehdr->e_shentsize = gelf_fsize (elf, ELF_T_SHDR, 1, EV_CURRENT);
 
@@ -2418,7 +2561,7 @@ while computing checksum for debug information"));
 	      || (pwrite_retry (fd, zero, sizeof zero,
 				offsetof (Elf32_Ehdr, e_shentsize))
 		  != sizeof zero)
-	      || ftruncate (fd, shdr_info[shdridx].shdr.sh_offset) < 0)
+	      || ftruncate (fd, lastsec_offset) < 0)
 	    {
 	      error (0, errno, gettext ("while writing '%s'"),
 		     output_fname ?: fname);
@@ -2438,7 +2581,7 @@ while computing checksum for debug information"));
 	      || (pwrite_retry (fd, zero, sizeof zero,
 				offsetof (Elf64_Ehdr, e_shentsize))
 		  != sizeof zero)
-	      || ftruncate (fd, shdr_info[shdridx].shdr.sh_offset) < 0)
+	      || ftruncate (fd, lastsec_offset) < 0)
 	    {
 	      error (0, errno, gettext ("while writing '%s'"),
 		     output_fname ?: fname);
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 751a081..0870d4c 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2018-10-26  Mark Wielaard  <mark@klomp.org>
+
+	* run-strip-reloc.sh: Add a test for --reloc-debug-sections-only.
+
 2018-10-12  Mark Wielaard  <mark@klomp.org>
 
 	* run-readelf-zdebug.sh: Adjust flags output.
diff --git a/tests/run-strip-reloc.sh b/tests/run-strip-reloc.sh
index bbc9f58..6e54ab4 100755
--- a/tests/run-strip-reloc.sh
+++ b/tests/run-strip-reloc.sh
@@ -32,6 +32,8 @@ runtest() {
   outfile2=out.stripped2
   debugfile2=out.debug2
 
+  echo "runtest $infile"
+
   rm -f $outfile1 $debugfile1 $outfile2 $debugfile2
 
   testrun ${abs_top_builddir}/src/strip -o $outfile1 -f $debugfile1 $infile ||
@@ -67,6 +69,15 @@ runtest() {
 
   testrun_compare cat readelf.out1 < readelf.out2 ||
   { echo "*** failure readelf -w compare $infile"; status=1; }
+
+  testrun ${abs_top_builddir}/src/strip --reloc-debug-sections-only \
+	  $debugfile1 ||
+  { echo "*** failure strip --reloc-debug-sections-only $debugfile1"; \
+    status=1; }
+
+  cmp $debugfile1 $debugfile2 ||
+  { echo "*** failure --reloc-debug-sections[-only] $debugfile1 $debugfile2"; \
+    status=1; }
 }
 
 # Most simple hello world kernel module for various architectures.
-- 
1.8.3.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]