#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);
#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. */
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 },
/* 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[])
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);
preserve_dates = true;
break;
+ case OPT_RELOC_DEBUG:
+ reloc_debug = true;
+ break;
+
case OPT_REMOVE_COMMENT:
remove_comment = true;
break;
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)
}
}
+ /* 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)
--- /dev/null
+#! /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