From f6ae0ab9350eae1f879e2239c83b93e0a5887d8e Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Thu, 25 Oct 2018 13:30:19 +0200 Subject: 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 --- src/ChangeLog | 7 + src/strip.c | 541 +++++++++++++++++++++++++++++++--------------------------- 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,3 +1,10 @@ +2018-10-24 Mark Wielaard + + * 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 * strip.c (handle_elf): Always copy over phdrs if there are any 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. */ -- cgit v1.2.3