summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2018-10-25 13:35:25 +0200
committerMark Wielaard <mark@klomp.org>2018-11-06 12:07:48 +0100
commit1628254ba2157ac2b78fc9e103fe0b16fa288a26 (patch)
tree27085650457a8b678017520b7959c7d0f017e414
parentf2d59180b90b56b32240f0ba106add050a1b7d09 (diff)
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>
-rw-r--r--src/ChangeLog11
-rw-r--r--src/strip.c155
-rw-r--r--tests/ChangeLog4
-rwxr-xr-xtests/run-strip-reloc.sh11
4 files changed, 175 insertions, 6 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 23990c31..79e6872a 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 11512067..e953c4d5 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 d5a06563..23e91133 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-18 Mark Wielaard <mark@klomp.org>
* run-readelf-n.sh: New test.
diff --git a/tests/run-strip-reloc.sh b/tests/run-strip-reloc.sh
index bbc9f586..6e54ab4a 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.