diff options
| author | Mark Wielaard <[email protected]> | 2011-05-16 11:33:11 +0200 |
|---|---|---|
| committer | Mark Wielaard <[email protected]> | 2011-05-23 21:34:01 +0200 |
| commit | 1662bc3880ae5acae6aa2a3013d193223c36f189 (patch) | |
| tree | a753f581ab934007e6285db98abde372b2d0ccfe /src/strip.c | |
| parent | 7a125b9306c4743eaee062bdab301f890e3c6309 (diff) | |
strip: Add --reloc-debug-sections option.
Diffstat (limited to 'src/strip.c')
| -rw-r--r-- | src/strip.c | 215 |
1 files changed, 213 insertions, 2 deletions
diff --git a/src/strip.c b/src/strip.c index 8c3c57de..f6800614 100644 --- a/src/strip.c +++ b/src/strip.c @@ -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) |
