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

This commit is contained in:
Mark Wielaard
2011-05-16 11:33:11 +02:00
parent 7a125b9306
commit 1662bc3880
8 changed files with 350 additions and 3 deletions
+9
View 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
+213 -2
View 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)
+10
View 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.
+4 -1
View 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 \
Binary file not shown.
Binary file not shown.
Binary file not shown.
+114
View File
@@ -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