summaryrefslogtreecommitdiffstats
path: root/src/strip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/strip.c')
-rw-r--r--src/strip.c874
1 files changed, 558 insertions, 316 deletions
diff --git a/src/strip.c b/src/strip.c
index a85a2306..15a43ff7 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;
@@ -354,6 +370,345 @@ parse_opt (int key, char *arg, struct argp_state *state)
return 0;
}
+static const char *
+secndx_name (Elf *elf, size_t ndx)
+{
+ size_t shstrndx;
+ GElf_Shdr mem;
+ Elf_Scn *sec = elf_getscn (elf, ndx);
+ GElf_Shdr *shdr = gelf_getshdr (sec, &mem);
+ if (shdr == NULL || elf_getshdrstrndx (elf, &shstrndx) < 0)
+ return "???";
+ return elf_strptr (elf, shstrndx, shdr->sh_name) ?: "???";
+}
+
+/* Get the extended section index table data for a symbol table section. */
+static Elf_Data *
+get_xndxdata (Elf *elf, Elf_Scn *symscn)
+{
+ Elf_Data *xndxdata = NULL;
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
+ if (shdr != NULL && shdr->sh_type == SHT_SYMTAB)
+ {
+ size_t scnndx = elf_ndxscn (symscn);
+ Elf_Scn *xndxscn = NULL;
+ while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL)
+ {
+ GElf_Shdr xndxshdr_mem;
+ GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
+
+ if (xndxshdr != NULL
+ && xndxshdr->sh_type == SHT_SYMTAB_SHNDX
+ && xndxshdr->sh_link == scnndx)
+ {
+ xndxdata = elf_getdata (xndxscn, NULL);
+ break;
+ }
+ }
+ }
+
+ return xndxdata;
+}
+
+/* Updates the shdrstrndx for the given Elf by updating the Ehdr and
+ possibly the section zero extension field. Returns zero on success. */
+static int
+update_shdrstrndx (Elf *elf, size_t shdrstrndx)
+{
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == 0)
+ return 1;
+
+ if (shdrstrndx < SHN_LORESERVE)
+ ehdr.e_shstrndx = shdrstrndx;
+ else
+ {
+ ehdr.e_shstrndx = SHN_XINDEX;
+ Elf_Scn *scn0 = elf_getscn (elf, 0);
+ GElf_Shdr shdr0_mem;
+ GElf_Shdr *shdr0 = gelf_getshdr (scn0, &shdr0_mem);
+ if (shdr0 == NULL)
+ return 1;
+
+ shdr0->sh_link = shdrstrndx;
+ if (gelf_update_shdr (scn0, shdr0) == 0)
+ return 1;
+ }
+
+ if (unlikely (gelf_update_ehdr (elf, &ehdr) == 0))
+ return 1;
+
+ return 0;
+}
+
+/* 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. */
+static void
+remove_debug_relocations (Ebl *ebl, Elf *elf, GElf_Ehdr *ehdr,
+ const char *fname, size_t shstrndx)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ /* We need the actual section and header from the elf
+ 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 (elf, 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 (elf, 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);
+ if (reldata == NULL || reldata->d_buf == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Make sure we adjust the uncompressed debug data
+ (and recompress if necessary at the end). */
+ GElf_Chdr tchdr;
+ int tcompress_type = 0;
+ bool is_gnu_compressed = false;
+ if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0)
+ {
+ is_gnu_compressed = true;
+ if (elf_compress_gnu (tscn, 0, 0) != 1)
+ INTERNAL_ERROR (fname);
+ }
+ else
+ {
+ if (gelf_getchdr (tscn, &tchdr) != NULL)
+ {
+ tcompress_type = tchdr.ch_type;
+ if (elf_compress (tscn, 0, 0) != 1)
+ INTERNAL_ERROR (fname);
+ }
+ }
+
+ Elf_Data *tdata = elf_getdata (tscn, NULL);
+ if (tdata == NULL || tdata->d_buf == NULL
+ || 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;
+ Elf_Scn * symscn = elf_getscn (elf, symt);
+ symdata = elf_getdata (symscn, NULL);
+ xndxdata = get_xndxdata (elf, symscn);
+ if (symdata == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Apply one relocation. Returns true when trivial
+ relocation actually done. */
+ bool relocate (GElf_Addr offset, const GElf_Sxword addend,
+ bool is_rela, int rtype, int symndx)
+ {
+ /* R_*_NONE relocs can always just be removed. */
+ if (rtype == 0)
+ return true;
+
+ /* We only do simple absolute relocations. */
+ int addsub = 0;
+ Elf_Type type = ebl_reloc_simple_type (ebl, rtype, &addsub);
+ 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);
+ Elf32_Word sec = (sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx);
+
+ if (ebl_debugscn_p (ebl, secndx_name (elf, sec)))
+ {
+ size_t size;
+
+#define DO_TYPE(NAME, Name) GElf_##Name Name;
+ union { TYPES; } tmpbuf;
+#undef DO_TYPE
+
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ size = sizeof (GElf_##Name); \
+ tmpbuf.Name = 0; \
+ break;
+ TYPES;
+#undef DO_TYPE
+ default:
+ return false;
+ }
+
+ if (offset > tdata->d_size
+ || tdata->d_size - offset < size)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0, gettext ("bad relocation"));
+ }
+
+ /* When the symbol value is zero then for SHT_REL
+ sections this is all that needs to be checked.
+ The addend is contained in the original data at
+ the offset already. So if the (section) symbol
+ address is zero and the given addend is zero
+ just remove the relocation, it isn't needed
+ anymore. */
+ if (addend == 0 && sym->st_value == 0)
+ return true;
+
+ 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,
+ };
+
+ GElf_Addr value = sym->st_value;
+ if (is_rela)
+ {
+ /* For SHT_RELA sections we just take the
+ given addend and add it to the value. */
+ value += addend;
+ /* For ADD/SUB relocations we need to fetch the
+ current section contents. */
+ if (addsub != 0)
+ {
+ Elf_Data *d = gelf_xlatetom (elf, &tmpdata,
+ &rdata,
+ ehdr->e_ident[EI_DATA]);
+ if (d == NULL)
+ INTERNAL_ERROR (fname);
+ assert (d == &tmpdata);
+ }
+ }
+ else
+ {
+ /* For SHT_REL sections we have to peek at
+ what is already in the section at the given
+ offset to get the addend. */
+ Elf_Data *d = gelf_xlatetom (elf, &tmpdata,
+ &rdata,
+ ehdr->e_ident[EI_DATA]);
+ if (d == NULL)
+ INTERNAL_ERROR (fname);
+ assert (d == &tmpdata);
+ }
+
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ if (addsub < 0) \
+ tmpbuf.Name -= (GElf_##Name) value; \
+ else \
+ tmpbuf.Name += (GElf_##Name) value; \
+ break;
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+
+ /* Now finally put in the new value. */
+ Elf_Data *s = gelf_xlatetof (elf, &rdata,
+ &tmpdata,
+ ehdr->e_ident[EI_DATA]);
+ if (s == NULL)
+ INTERNAL_ERROR (fname);
+ assert (s == &rdata);
+
+ return true;
+ }
+ return false;
+ }
+
+ if (shdr->sh_entsize == 0)
+ INTERNAL_ERROR (fname);
+
+ 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, false,
+ 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, true,
+ 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);
+
+ if (is_gnu_compressed)
+ {
+ if (elf_compress_gnu (tscn, 1, ELF_CHF_FORCE) != 1)
+ INTERNAL_ERROR (fname);
+ }
+ else if (tcompress_type != 0)
+ {
+ if (elf_compress (tscn, tcompress_type, ELF_CHF_FORCE) != 1)
+ INTERNAL_ERROR (fname);
+ }
+ }
+ }
+}
static int
process_file (const char *fname)
@@ -444,6 +799,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)
@@ -460,6 +925,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
{
@@ -518,7 +985,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)
@@ -580,49 +1047,75 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
else
newelf = elf_clone (elf, ELF_C_EMPTY);
- if (unlikely (gelf_newehdr (newelf, gelf_getclass (elf)) == 0)
- || (ehdr->e_type != ET_REL
- && unlikely (gelf_newphdr (newelf, phnum) == 0)))
+ if (unlikely (gelf_newehdr (newelf, gelf_getclass (elf)) == 0))
{
- error (0, 0, gettext ("cannot create new file '%s': %s"),
+ error (0, 0, gettext ("cannot create new ehdr for file '%s': %s"),
output_fname ?: fname, elf_errmsg (-1));
goto fail;
}
/* Copy over the old program header if needed. */
- if (ehdr->e_type != ET_REL)
- for (cnt = 0; cnt < phnum; ++cnt)
- {
- GElf_Phdr phdr_mem;
- GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
- if (phdr == NULL
- || unlikely (gelf_update_phdr (newelf, cnt, phdr) == 0))
- INTERNAL_ERROR (fname);
- }
+ if (phnum > 0)
+ {
+ if (unlikely (gelf_newphdr (newelf, phnum) == 0))
+ {
+ error (0, 0, gettext ("cannot create new phdr for file '%s': %s"),
+ output_fname ?: fname, elf_errmsg (-1));
+ goto fail;
+ }
+
+ for (cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL
+ || unlikely (gelf_update_phdr (newelf, cnt, phdr) == 0))
+ INTERNAL_ERROR (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 */
debugelf = elf_begin (debug_fd, ELF_C_WRITE_MMAP, NULL);
- if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0)
- || (ehdr->e_type != ET_REL
- && unlikely (gelf_newphdr (debugelf, phnum) == 0)))
+ if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0))
{
- error (0, 0, gettext ("cannot create new file '%s': %s"),
+ error (0, 0, gettext ("cannot create new ehdr for file '%s': %s"),
debug_fname, elf_errmsg (-1));
goto fail_close;
}
/* Copy over the old program header if needed. */
- if (ehdr->e_type != ET_REL)
- for (cnt = 0; cnt < phnum; ++cnt)
- {
- GElf_Phdr phdr_mem;
- GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
- if (phdr == NULL
- || unlikely (gelf_update_phdr (debugelf, cnt, phdr) == 0))
- INTERNAL_ERROR (fname);
- }
+ if (phnum > 0)
+ {
+ if (unlikely (gelf_newphdr (debugelf, phnum) == 0))
+ {
+ error (0, 0, gettext ("cannot create new phdr for file '%s': %s"),
+ debug_fname, elf_errmsg (-1));
+ goto fail_close;
+ }
+
+ for (cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL
+ || unlikely (gelf_update_phdr (debugelf, cnt, phdr) == 0))
+ INTERNAL_ERROR (fname);
+ }
+ }
}
/* Number of sections. */
@@ -730,7 +1223,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
to keep the layout of all allocated sections as similar as
possible to the original file. In relocatable object files
everything can be moved. */
- if (ehdr->e_type == ET_REL
+ if (phnum == 0
|| (shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) == 0)
shdr_info[cnt].shdr.sh_offset = 0;
@@ -784,9 +1277,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
if (shdr_info[shdr_info[cnt].group_idx].idx == 0)
{
- /* The section group section will be removed. */
+ /* The section group section might be removed.
+ Don't remove the SHF_GROUP flag. The section is
+ either also removed, in which case the flag doesn't matter.
+ Or it moves with the group into the debug file, then
+ it will be reconnected with the new group and should
+ still have the flag set. */
shdr_info[cnt].group_idx = 0;
- shdr_info[cnt].shdr.sh_flags &= ~SHF_GROUP;
}
}
@@ -1127,6 +1624,14 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
debugehdr->e_entry = ehdr->e_entry;
debugehdr->e_flags = ehdr->e_flags;
+ if (unlikely (gelf_update_ehdr (debugelf, debugehdr) == 0))
+ {
+ error (0, 0, gettext ("%s: error while updating ELF header: %s"),
+ debug_fname, elf_errmsg (-1));
+ result = 1;
+ goto fail_close;
+ }
+
size_t shdrstrndx;
if (elf_getshdrstrndx (elf, &shdrstrndx) < 0)
{
@@ -1136,36 +1641,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
goto fail_close;
}
- if (shstrndx < SHN_LORESERVE)
- debugehdr->e_shstrndx = shdrstrndx;
- else
+ if (update_shdrstrndx (debugelf, shdrstrndx) != 0)
{
- debugehdr->e_shstrndx = SHN_XINDEX;
- Elf_Scn *scn0 = elf_getscn (debugelf, 0);
- GElf_Shdr shdr0_mem;
- GElf_Shdr *shdr0 = gelf_getshdr (scn0, &shdr0_mem);
- if (shdr0 == NULL)
- {
- error (0, 0, gettext ("%s: error getting zero section: %s"),
- debug_fname, elf_errmsg (-1));
- result = 1;
- goto fail_close;
- }
-
- shdr0->sh_link = shdrstrndx;
- if (gelf_update_shdr (scn0, shdr0) == 0)
- {
- error (0, 0, gettext ("%s: error while updating zero section: %s"),
- debug_fname, elf_errmsg (-1));
- result = 1;
- goto fail_close;
- }
-
- }
-
- if (unlikely (gelf_update_ehdr (debugelf, debugehdr) == 0))
- {
- error (0, 0, gettext ("%s: error while creating ELF header: %s"),
+ error (0, 0, gettext ("%s: error updating shdrstrndx: %s"),
debug_fname, elf_errmsg (-1));
result = 1;
goto fail_close;
@@ -1360,7 +1838,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
&& shdr_info[cnt].data->d_buf != NULL);
Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf;
- for (size_t inner = 0;
+ /* First word is the section group flag.
+ Followed by section indexes, that need to be renumbered. */
+ for (size_t inner = 1;
inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word);
++inner)
if (grpref[inner] < shnum)
@@ -1950,240 +2430,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
zero based between the unallocated sections. */
if (debug_fname != NULL && removing_sections
&& 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);
- if (reldata == NULL || reldata->d_buf == NULL)
- INTERNAL_ERROR (fname);
-
- /* Make sure we adjust the uncompressed debug data
- (and recompress if necessary at the end). */
- GElf_Chdr tchdr;
- int tcompress_type = 0;
- if (gelf_getchdr (tscn, &tchdr) != NULL)
- {
- tcompress_type = tchdr.ch_type;
- if (elf_compress (tscn, 0, 0) != 1)
- INTERNAL_ERROR (fname);
- }
-
- Elf_Data *tdata = elf_getdata (tscn, NULL);
- if (tdata == NULL || tdata->d_buf == NULL
- || 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;
- elf_assert (symt < shnum + 2);
- elf_assert (shdr_info[symt].symtab_idx < shnum + 2);
- 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,
- bool is_rela, 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);
- Elf32_Word sec = (sym->st_shndx == SHN_XINDEX
- ? xndx : sym->st_shndx);
- if (sec >= shnum + 2)
- INTERNAL_ERROR (fname);
-
- if (ebl_debugscn_p (ebl, shdr_info[sec].name))
- {
- size_t size;
-
-#define DO_TYPE(NAME, Name) GElf_##Name Name;
- union { TYPES; } tmpbuf;
-#undef DO_TYPE
-
- switch (type)
- {
-#define DO_TYPE(NAME, Name) \
- case ELF_T_##NAME: \
- size = sizeof (GElf_##Name); \
- tmpbuf.Name = 0; \
- break;
- TYPES;
-#undef DO_TYPE
- default:
- return false;
- }
-
- if (offset > tdata->d_size
- || tdata->d_size - offset < size)
- {
- cleanup_debug ();
- error (EXIT_FAILURE, 0, gettext ("bad relocation"));
- }
-
- /* When the symbol value is zero then for SHT_REL
- sections this is all that needs to be checked.
- The addend is contained in the original data at
- the offset already. So if the (section) symbol
- address is zero and the given addend is zero
- just remove the relocation, it isn't needed
- anymore. */
- if (addend == 0 && sym->st_value == 0)
- return true;
-
- 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,
- };
-
- GElf_Addr value = sym->st_value;
- if (is_rela)
- {
- /* For SHT_RELA sections we just take the
- given addend and add it to the value. */
- value += addend;
- }
- else
- {
- /* For SHT_REL sections we have to peek at
- what is already in the section at the given
- offset to get the addend. */
- Elf_Data *d = gelf_xlatetom (debugelf, &tmpdata,
- &rdata,
- ehdr->e_ident[EI_DATA]);
- if (d == NULL)
- INTERNAL_ERROR (fname);
- assert (d == &tmpdata);
- }
-
- switch (type)
- {
-#define DO_TYPE(NAME, Name) \
- case ELF_T_##NAME: \
- tmpbuf.Name += (GElf_##Name) value; \
- break;
- TYPES;
-#undef DO_TYPE
- default:
- abort ();
- }
-
- /* Now finally put in the new value. */
- 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;
- }
-
- if (shdr->sh_entsize == 0)
- INTERNAL_ERROR (fname);
-
- 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, false,
- 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, true,
- 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);
-
- if (tcompress_type != 0)
- if (elf_compress (tscn, tcompress_type, ELF_CHF_FORCE) != 1)
- INTERNAL_ERROR (fname);
- }
- }
- }
+ remove_debug_relocations (ebl, debugelf, ehdr, fname, shstrndx);
/* Now that we have done all adjustments to the data,
we can actually write out the debug file. */
@@ -2247,6 +2494,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);
@@ -2263,31 +2514,22 @@ 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);
- /* The new section header string table index. */
- if (likely (idx < SHN_HIRESERVE) && likely (idx != SHN_XINDEX))
- newehdr->e_shstrndx = idx;
- else
+ if (gelf_update_ehdr (newelf, newehdr) == 0)
{
- /* The index does not fit in the ELF header field. */
- shdr_info[0].scn = elf_getscn (elf, 0);
-
- if (gelf_getshdr (shdr_info[0].scn, &shdr_info[0].shdr) == NULL)
- INTERNAL_ERROR (fname);
-
- shdr_info[0].shdr.sh_link = idx;
- (void) gelf_update_shdr (shdr_info[0].scn, &shdr_info[0].shdr);
-
- newehdr->e_shstrndx = SHN_XINDEX;
+ error (0, 0, gettext ("%s: error while creating ELF header: %s"),
+ output_fname ?: fname, elf_errmsg (-1));
+ cleanup_debug ();
+ return 1;
}
- if (gelf_update_ehdr (newelf, newehdr) == 0)
+ /* The new section header string table index. */
+ if (update_shdrstrndx (newelf, idx) != 0)
{
- error (0, 0, gettext ("%s: error while creating ELF header: %s"),
+ error (0, 0, gettext ("%s: error updating shdrstrndx: %s"),
output_fname ?: fname, elf_errmsg (-1));
cleanup_debug ();
return 1;
@@ -2305,7 +2547,7 @@ while computing checksum for debug information"));
/* The ELF library better follows our layout when this is not a
relocatable object file. */
elf_flagelf (newelf, ELF_C_SET,
- (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0)
+ (phnum > 0 ? ELF_F_LAYOUT : 0)
| (permissive ? ELF_F_PERMISSIVE : 0));
/* Finally write the file. */
@@ -2334,7 +2576,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);
@@ -2354,7 +2596,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);