From: Mark Wielaard Date: Mon, 16 May 2011 09:33:11 +0000 (+0200) Subject: strip: Add --reloc-debug-sections option. X-Git-Tag: elfutils-0.153~40 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1662bc3880ae5acae6aa2a3013d193223c36f189;p=thirdparty%2Felfutils.git strip: Add --reloc-debug-sections option. --- diff --git a/src/ChangeLog b/src/ChangeLog index 154b38e22..0d28013f0 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2011-05-23 Mark Wielaard + + * 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 * strip.c (handle_elf): Make sure all sections of a removed group diff --git a/src/strip.c b/src/strip.c index 8c3c57de6..f6800614e 100644 --- a/src/strip.c +++ b/src/strip.c @@ -53,6 +53,7 @@ #include #include +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) diff --git a/tests/ChangeLog b/tests/ChangeLog index 2599b0ca8..7cf816ed8 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,13 @@ +2011-05-23 Mark Wielaard + + * 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 * run-strip-groups.sh: New test. diff --git a/tests/Makefile.am b/tests/Makefile.am index 16a47e2e0..07ae929d6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 000000000..f89b292cb 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 index 000000000..f4d3ff203 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 index 000000000..ba06f91e0 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 index 000000000..1c6a8bd0f --- /dev/null +++ b/tests/run-strip-reloc.sh @@ -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 +# . + +. $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 +# #include +# +# int init_module(void) +# { +# printk(KERN_INFO "Hello, world!\n"); +# return 0; +# } +# :::::::::::::: +# exit.c +# :::::::::::::: +# #include +# #include +# +# 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