summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2018-10-25 13:30:19 +0200
committerMark Wielaard <mark@klomp.org>2018-11-06 12:07:48 +0100
commitf6ae0ab9350eae1f879e2239c83b93e0a5887d8e (patch)
tree3a5dc1057cb72d5af2ade8565a116797bde5fcbd
parentb15ee95bcee45482e5d864c0048804117c1ada63 (diff)
strip: Split out debug section relocation into separate helper functions.
Extract a couple of helper functions out of handle_elf (secndx_name, get_xndxdata and remove_debug_relocations) so they can be reused more easily in the future. Signed-off-by: Mark Wielaard <mark@klomp.org>
-rw-r--r--src/ChangeLog7
-rw-r--r--src/strip.c541
2 files changed, 299 insertions, 249 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 3a893b71..5832714d 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,12 @@
2018-10-24 Mark Wielaard <mark@klomp.org>
+ * strip.c (handle_elf): Extract code into separate functions...
+ (secndx_name): ... new function.
+ (get_xndxdata): Likewise.
+ (remove_debug_relocations): Likewise.
+
+2018-10-24 Mark Wielaard <mark@klomp.org>
+
* strip.c (handle_elf): Always copy over phdrs if there are any
and check phnum instead of e_type to determine whether to move
allocated sections.
diff --git a/src/strip.c b/src/strip.c
index cb479deb..a7b69a6a 100644
--- a/src/strip.c
+++ b/src/strip.c
@@ -354,6 +354,297 @@ 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;
+}
+
+/* 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;
+ 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 (tcompress_type != 0)
+ if (elf_compress (tscn, tcompress_type, ELF_CHF_FORCE) != 1)
+ INTERNAL_ERROR (fname);
+ }
+ }
+}
static int
process_file (const char *fname)
@@ -1978,255 +2269,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. */
- 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 (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;
- /* For ADD/SUB relocations we need to fetch the
- current section contents. */
- if (addsub != 0)
- {
- Elf_Data *d = gelf_xlatetom (debugelf, &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 (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: \
- 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 (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. */