]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
strip: Add --reloc-debug-sections option.
authorMark Wielaard <mjw@redhat.com>
Mon, 16 May 2011 09:33:11 +0000 (11:33 +0200)
committerMark Wielaard <mjw@redhat.com>
Mon, 23 May 2011 19:34:01 +0000 (21:34 +0200)
src/ChangeLog
src/strip.c
tests/ChangeLog
tests/Makefile.am
tests/hello_i386.ko.bz2 [new file with mode: 0644]
tests/hello_ppc64.ko.bz2 [new file with mode: 0644]
tests/hello_x86_64.ko.bz2 [new file with mode: 0644]
tests/run-strip-reloc.sh [new file with mode: 0755]

index 154b38e22e1c12e1b368e9bd8a0dc07ce150c8a8..0d28013f002a9cb4069319a5f1d4f0e53e47263c 100644 (file)
@@ -1,3 +1,12 @@
+2011-05-23  Mark Wielaard  <mjw@redhat.com>
+
+       * strip.c (OPT_RELOC_DEBUG): New option.
+       (argp_option): Add new --reloc-debug-sections option.
+       (main): Check new option.
+       (parse_opt): Likewise.
+       (handle_elf): Remove any relocations between debug sections
+       in ET_REL for the debug file when requested.
+
 2011-05-18  Mark Wielaard  <mjw@redhat.com>
 
        * strip.c (handle_elf): Make sure all sections of a removed group
index 8c3c57de6e2e12c502b12cb35948f1ad726f7b17..f6800614e204f1e132cfa5a7f54c4e8d67c9c72c 100644 (file)
@@ -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)
index 2599b0ca85d888439b553c84fbf14a6d97382aff..7cf816ed85d147b4f568aa5e6f722cc1d778157c 100644 (file)
@@ -1,3 +1,13 @@
+2011-05-23  Mark Wielaard  <mjw@redhat.com>
+
+       * Makefile.am (TESTS): Add run-strip-reloc.sh.
+       (EXTRA_DIST): Add run-strip-reloc.sh, hello_i386.ko.bz2
+       hello_x86_64.ko.bz2 and hello_ppc64.ko.bz2
+       * run-strip-reloc.sh: New test.
+       * hello_i386.ko.bz2: New test file.
+       * hello_x86_64.ko.bz2: Likewise.
+       * hello_ppc64.ko.bz2: Likewise.
+
 2011-05-18  Mark Wielaard  <mjw@redhat.com>
 
        * run-strip-groups.sh: New test.
index 16a47e2e01ffec083e540eee9d0d95d2fc58903d..07ae929d6ca575e7d198f767f6a4199bc2bd88d2 100644 (file)
@@ -70,7 +70,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        newscn run-strip-test.sh run-strip-test2.sh \
        run-strip-test3.sh run-strip-test4.sh run-strip-test5.sh \
        run-strip-test6.sh run-strip-test7.sh run-strip-test8.sh \
-       run-strip-groups.sh run-unstrip-test.sh run-unstrip-test2.sh \
+       run-strip-groups.sh run-strip-reloc.sh \
+       run-unstrip-test.sh run-unstrip-test2.sh \
        run-ecp-test.sh run-ecp-test2.sh run-alldts.sh \
        run-elflint-test.sh run-elflint-self.sh run-ranlib-test.sh \
        run-ranlib-test2.sh run-ranlib-test3.sh run-ranlib-test4.sh \
@@ -111,6 +112,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             run-line2addr.sh run-elflint-test.sh testfile14.bz2 \
             run-strip-test4.sh run-strip-test5.sh run-strip-test6.sh \
             run-strip-test7.sh run-strip-test8.sh run-strip-groups.sh \
+            run-strip-reloc.sh hello_i386.ko.bz2 hello_x86_64.ko.bz2 \
+            hello_ppc64.ko.bz2 \
             run-unstrip-test.sh run-unstrip-test2.sh \
             run-elflint-self.sh run-ranlib-test.sh run-ranlib-test2.sh \
             run-ranlib-test3.sh run-ranlib-test4.sh \
diff --git a/tests/hello_i386.ko.bz2 b/tests/hello_i386.ko.bz2
new file mode 100644 (file)
index 0000000..f89b292
Binary files /dev/null and b/tests/hello_i386.ko.bz2 differ
diff --git a/tests/hello_ppc64.ko.bz2 b/tests/hello_ppc64.ko.bz2
new file mode 100644 (file)
index 0000000..f4d3ff2
Binary files /dev/null and b/tests/hello_ppc64.ko.bz2 differ
diff --git a/tests/hello_x86_64.ko.bz2 b/tests/hello_x86_64.ko.bz2
new file mode 100644 (file)
index 0000000..ba06f91
Binary files /dev/null and b/tests/hello_x86_64.ko.bz2 differ
diff --git a/tests/run-strip-reloc.sh b/tests/run-strip-reloc.sh
new file mode 100755 (executable)
index 0000000..1c6a8bd
--- /dev/null
@@ -0,0 +1,114 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents.  No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package.  Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+testfiles hello_i386.ko hello_x86_64.ko hello_ppc64.ko
+
+status=0
+runtest() {
+  infile=$1
+  is_ET_REL=$2
+  outfile1=out.stripped1
+  debugfile1=out.debug1
+  outfile2=out.stripped2
+  debugfile2=out.debug2
+
+  testrun ../src/strip -o $outfile1 -f $debugfile1 $infile ||
+  { echo "*** failure strip $infile"; status=1; }
+
+  testrun ../src/strip --reloc-debug-sections -o $outfile2 \
+       -f $debugfile2 $infile ||
+  { echo "*** failure strip --reloc-debug-sections $infile"; status=1; }
+
+  # shouldn't make any difference for stripped files.
+  testrun ../src/readelf -a $outfile1 > readelf.out ||
+  { echo "*** failure readelf -a outfile1 $infile"; status=1; }
+
+  testrun_compare ../src/readelf -a $outfile2 < readelf.out ||
+  { echo "*** failure compare stripped files $infile"; status=1; }
+
+  # debug files however should be smaller, when ET_REL.
+  SIZE1=$(stat -c%s $debugfile1)
+  SIZE2=$(stat -c%s $debugfile2)
+  test \( \( $is_ET_REL -eq 1 \) -a \( $SIZE1 -gt $SIZE2 \) \) \
+       -o \( \( $is_ET_REL -eq 0 \) -a \( $SIZE1 -eq $SIZE2 \) \) ||
+  { echo "*** failure --reloc-debug-sections not smaller $infile"; status=1; }
+
+  # Strip of DWARF section lines, offset will not match.
+  # Everything else should match.
+  testrun ../src/readelf -w $debugfile1 \
+       | grep -v ^DWARF\ section > readelf.out1 ||
+  { echo "*** failure readelf -w debugfile1 $infile"; status=1; }
+
+  testrun ../src/readelf -w $debugfile2 \
+       | grep -v ^DWARF\ section > readelf.out2 ||
+  { echo "*** failure readelf -w debugfile2 $infile"; status=1; }
+
+  testrun_compare cat readelf.out1 < readelf.out2 ||
+  { echo "*** failure readelf -w compare $infile"; status=1; }
+
+  rm -f $outfile1 $debugfile1 $outfile2 $debugfile2 readelf.out*
+}
+
+# Most simple hello world kernel module for various architectures.
+# ::::::::::::::
+# Makefile
+# ::::::::::::::
+# obj-m        := hello.o
+# hello-y := init.o exit.o
+# 
+# all:
+#      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+# ::::::::::::::
+# init.c
+# ::::::::::::::
+# #include <linux/kernel.h>
+# #include <linux/module.h>
+# 
+# int init_module(void)
+# {
+#   printk(KERN_INFO "Hello, world!\n");
+#   return 0;
+# }
+# ::::::::::::::
+# exit.c
+# ::::::::::::::
+# #include <linux/kernel.h>
+# #include <linux/module.h>
+# 
+# void cleanup_module()
+# {
+#   printk(KERN_INFO "Goodbye, World!\n");
+# }
+runtest hello_i386.ko 1
+runtest hello_x86_64.ko 1
+runtest hello_ppc64.ko 1
+
+# self test, shouldn't impact non-ET_REL files at all.
+runtest ../src/strip 0
+runtest ../src/strip.o 1
+
+exit $status