diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 149 | ||||
-rw-r--r-- | src/addr2line.c | 6 | ||||
-rw-r--r-- | src/ar.c | 10 | ||||
-rw-r--r-- | src/arlib.c | 3 | ||||
-rw-r--r-- | src/elflint.c | 50 | ||||
-rw-r--r-- | src/findtextrel.c | 23 | ||||
-rw-r--r-- | src/readelf.c | 153 | ||||
-rw-r--r-- | src/size.c | 6 | ||||
-rw-r--r-- | src/strip.c | 874 | ||||
-rw-r--r-- | src/unstrip.c | 102 |
10 files changed, 982 insertions, 394 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index b0db6fbd..b6fd9d22 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -27,6 +27,155 @@ * ar.c: Open files in O_BINARY. +2018-11-10 Mark Wielaard <mark@klomp.org> + + * elflint.c (check_program_header): Allow PT_GNU_EH_FRAME segment + to be matched against SHT_X86_64_UNWIND section. + +2018-11-09 Mark Wielaard <mark@klomp.org> + + * strip.c (remove_debug_relocations): Check if section is gnu + compressed and decompress and recompress it. + +2018-11-12 Mark Wielaard <mark@klomp.org> + + * elflint.c (check_note_data): Recognize NT_GNU_BUILD_ATTRIBUTE_OPEN + and NT_GNU_BUILD_ATTRIBUTE_OPEN. + * readelf.c (handle_notes_data): Handle + ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX. Pass nhdr.n_namesz to + ebl_object_note. + +2018-11-11 Mark Wielaard <mark@klomp.org> + + * readelf.c (handle_notes_data): Pass n_descsz to + ebl_object_note_type_name. + * elflint.c (check_note_data): Recognize NT_VERSION, add owner + name to unknown note error. + +2018-10-20 Mark Wielaard <mark@klomp.org> + + * readelf.c (process_elf_file): Use dwelf_elf_begin to open pure_elf. + +2018-10-26 Mark Wielaard <mark@klomp.org> + + * strip.c (OPT_RELOC_DEBUG_ONLY): New define. + (options): Add reloc-debug-sections-only. + (reloc_debug_only): New static bool. + (main): Check reloc_debug_only is the only strip option used. + (parse_opt): Handle OPT_RELOC_DEBUG_ONLY. + (handle_debug_relocs): New function. + (handle_elf): Add local variables lastsec_offset and lastsec_size. + Handle reloc_debug_only. + +2018-10-24 Mark Wielaard <mark@klomp.org> + + * strip.c (handle_elf): Extract code to update shdrstrndx into... + (update_shdrstrndx): ... this new function. + +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. + +2018-11-02 Mark Wielaard <mark@klomp.org> + + * unstrip.c (copy_elf): Add ELF_CHECK to make sure gelf_getehdr () + doesn't return NULL. + +2018-10-18 Mark Wielaard <mark@klomp.org> + + * elflint.c (check_note_data): Recognize NT_GNU_PROPERTY_TYPE_0. + (check_note): Use p_align to pass either ELF_T_NHDR or ELF_T_NHDR8 to + elf_getdata_rawchunk. + * readelf (handle_notes): Likewise. + +2018-10-24 Mark Wielaard <mark@klomp.org> + + * addr2line.c (print_addrsym): Use elf_getshdrstrndx instead of + Ehdr field. + +2018-10-24 Mark Wielaard <mark@klomp.org> + + * readelf.c (section_name): Remove ehdr argument, lookup shstrndx. + (print_debug_abbrev_section): Don't pass ehdr. + (print_debug_addr_section): Likewise. + (print_decoded_aranges_section): Likewise. + (print_debug_aranges_section): Likewise. + (print_debug_rnglists_section): Likewise. + (print_debug_ranges_section): Likewise. + (print_debug_units): Likewise. + (print_decoded_line_section): Likewise. + (print_debug_line_section): Likewise. + (print_debug_loclists_section): Likewise. + (print_debug_loc_section): Likewise. + (print_debug_macinfo_section): Likewise. + (print_debug_macro_section): Likewise. + (print_debug_pubnames_section): Likewise. + (print_debug_str_section): Liekwise. + (print_debug_str_offsets_section): Likewise. + (print_gdb_index_section): Likewise. + +2018-10-16 Mark Wielaard <mark@klomp.org> + + * readelf.c (print_debug_frame_section): Make sure readp is never + greater than cieend. + +2018-10-19 Mark Wielaard <mark@klomp.org> + + * dwfl_module_getdwarf.c (adjust_relocs): Check sh_entsize is not + zero. + (add_new_section_symbols): Likewise. + (copy_elided_sections): Likewise. + +2018-10-18 Mark Wielaard <mark@klomp.org> + + * size.c (handle_ar): Only close elf if prefix was NULL. + +2018-10-18 Mark Wielaard <mark@klomp.org> + + * arlib.c (arlib_add_symbols): Check that sh_entsize is not zero. + +2018-10-14 Mark Wielaard <mark@klomp.org> + + * ar.c (do_oper_extract): Assume epoch if ar_date is bogus. + +2018-10-14 Mark Wielaard <mark@klomp.org> + + * findtextrel.c (process_file): Check that sh_entsize is not zero. + +2018-10-13 Mark Wielaard <mark@klomp.org> + + * readelf.c (print_debug_macro_section): Use elf_getdata. Print + decoded flag string. + +2018-10-19 Mark Wielaard <mark@klomp.org> + + * unstrip.c (copy_elided_sections): Renumber group section indexes. + +2018-10-12 Mark Wielaard <mark@klomp.org> + + * strip.c (handle_elf): Don't remove SHF_GROUP flag from sections. + Skip group section flag when renumbering section indexes. + * unstrip.c (struct section): Add sig field. + (compare_unalloc_sections): Take and use sig1 and sig2 as arguments. + (compare_sections): Pass signatures to compare_unalloc_sections. + (get_group_sig): New function. + (find_alloc_sections_prelink): Set signature. + (copy_elided_sections): Likewise and pass them on. + (find_unalloc_section): Take and pass signatures. + +2018-10-02 Andreas Schwab <schwab@suse.de> + + * strip.c (handle_elf): Handle ADD/SUB relocation. + 2018-09-13 Mark Wielaard <mark@klomp.org> * readelf.c (print_shdr): Get number of section with elf_getshdrnum. diff --git a/src/addr2line.c b/src/addr2line.c index 32660f7a..8bd974cb 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -446,9 +446,9 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr) if (shdr != NULL) { Elf *elf = dwfl_module_getelf (mod, &ebias); - GElf_Ehdr ehdr; - if (gelf_getehdr (elf, &ehdr) != NULL) - printf (" (%s)", elf_strptr (elf, ehdr.e_shstrndx, + size_t shstrndx; + if (elf_getshdrstrndx (elf, &shstrndx) >= 0) + printf (" (%s)", elf_strptr (elf, shstrndx, shdr->sh_name)); } } @@ -519,8 +519,14 @@ do_oper_extract (int oper, const char *arfname, char **argv, int argc, else if (oper == oper_list) { char datestr[100]; - strftime (datestr, sizeof (datestr), "%b %d %H:%M %Y", - localtime (&arhdr->ar_date)); + struct tm *tp = localtime (&arhdr->ar_date); + if (tp == NULL) + { + time_t time = 0; + tp = localtime (&time); + } + + strftime (datestr, sizeof (datestr), "%b %d %H:%M %Y", tp); printf ("%c%c%c%c%c%c%c%c%c %u/%u %6ju %s %s\n", (arhdr->ar_mode & S_IRUSR) ? 'r' : '-', diff --git a/src/arlib.c b/src/arlib.c index 778e0878..a6521e30 100644 --- a/src/arlib.c +++ b/src/arlib.c @@ -252,6 +252,9 @@ arlib_add_symbols (Elf *elf, const char *arfname, const char *membername, if (data == NULL) continue; + if (shdr->sh_entsize == 0) + continue; + int nsyms = shdr->sh_size / shdr->sh_entsize; for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx) { diff --git a/src/elflint.c b/src/elflint.c index e6efda56..e0ef95ce 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -1,5 +1,5 @@ /* Pedantic checking of ELF files compliance with gABI/psABI spec. - Copyright (C) 2001-2015, 2017 Red Hat, Inc. + Copyright (C) 2001-2015, 2017, 2018 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. @@ -4331,7 +4331,31 @@ section [%2d] '%s': unknown core file note type %" PRIu32 case NT_GNU_HWCAP: case NT_GNU_BUILD_ID: case NT_GNU_GOLD_VERSION: - break; + case NT_GNU_PROPERTY_TYPE_0: + if (nhdr.n_namesz == sizeof ELF_NOTE_GNU + && strcmp (data->d_buf + name_offset, ELF_NOTE_GNU) == 0) + break; + else + { + /* NT_VERSION is 1, same as NT_GNU_ABI_TAG. It has no + descriptor and (ab)uses the name as version string. */ + if (nhdr.n_descsz == 0 && nhdr.n_type == NT_VERSION) + break; + } + goto unknown_note; + + case NT_GNU_BUILD_ATTRIBUTE_OPEN: + case NT_GNU_BUILD_ATTRIBUTE_FUNC: + /* GNU Build Attributes store most data in the owner + name, which must start with the + ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX "GA". */ + if (nhdr.n_namesz >= sizeof ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX + && strncmp (data->d_buf + name_offset, + ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX, + strlen (ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX)) == 0) + break; + else + goto unknown_note; case 0: /* Linux vDSOs use a type 0 note for the kernel version word. */ @@ -4340,16 +4364,21 @@ section [%2d] '%s': unknown core file note type %" PRIu32 break; FALLTHROUGH; default: + { + unknown_note: if (shndx == 0) ERROR (gettext ("\ -phdr[%d]: unknown object file note type %" PRIu32 " at offset %zu\n"), - phndx, (uint32_t) nhdr.n_type, offset); +phdr[%d]: unknown object file note type %" PRIu32 " with owner name '%s' at offset %zu\n"), + phndx, (uint32_t) nhdr.n_type, + (char *) data->d_buf + name_offset, offset); else ERROR (gettext ("\ section [%2d] '%s': unknown object file note type %" PRIu32 - " at offset %zu\n"), + " with owner name '%s' at offset %zu\n"), shndx, section_name (ebl, shndx), - (uint32_t) nhdr.n_type, offset); + (uint32_t) nhdr.n_type, + (char *) data->d_buf + name_offset, offset); + } } } @@ -4376,7 +4405,8 @@ phdr[%d]: no note entries defined for the type of file\n"), GElf_Off notes_size = 0; Elf_Data *data = elf_getdata_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz, - ELF_T_NHDR); + (phdr->p_align == 8 + ? ELF_T_NHDR8 : ELF_T_NHDR)); if (data != NULL && data->d_buf != NULL) notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset); @@ -4603,8 +4633,10 @@ program header offset in ELF header and PHDR entry do not match")); any = true; shdr = gelf_getshdr (scn, &shdr_mem); if (shdr != NULL - && shdr->sh_type == (is_debuginfo - ? SHT_NOBITS : SHT_PROGBITS) + && ((is_debuginfo && shdr->sh_type == SHT_NOBITS) + || (! is_debuginfo + && (shdr->sh_type == SHT_PROGBITS + || shdr->sh_type == SHT_X86_64_UNWIND))) && elf_strptr (ebl->elf, shstrndx, shdr->sh_name) != NULL && ! strcmp (".eh_frame_hdr", elf_strptr (ebl->elf, shstrndx, shdr->sh_name))) diff --git a/src/findtextrel.c b/src/findtextrel.c index 76456195..5c26715e 100644 --- a/src/findtextrel.c +++ b/src/findtextrel.c @@ -1,5 +1,5 @@ /* Locate source files or functions which caused text relocations. - Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc. + Copyright (C) 2005-2010, 2012, 2014, 2018 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2005. @@ -263,9 +263,10 @@ process_file (const char *fname, bool more_than_one) seen_dynamic = true; Elf_Data *data = elf_getdata (scn, NULL); + size_t entries = (shdr->sh_entsize == 0 + ? 0 : shdr->sh_size / shdr->sh_entsize); - for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; - ++cnt) + for (size_t cnt = 0; cnt < entries; ++cnt) { GElf_Dyn dynmem; GElf_Dyn *dyn; @@ -413,10 +414,11 @@ cannot get symbol table section %zu in '%s': %s"), if (shdr->sh_type == SHT_REL) { Elf_Data *data = elf_getdata (scn, NULL); + size_t entries = (shdr->sh_entsize == 0 + ? 0 : shdr->sh_size / shdr->sh_entsize); for (int cnt = 0; - (size_t) cnt < shdr->sh_size / shdr->sh_entsize; - ++cnt) + (size_t) cnt < entries; ++cnt) { GElf_Rel rel_mem; GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem); @@ -436,10 +438,10 @@ cannot get relocation at index %d in section %zu in '%s': %s"), else if (shdr->sh_type == SHT_RELA) { Elf_Data *data = elf_getdata (scn, NULL); + size_t entries = (shdr->sh_entsize == 0 + ? 0 : shdr->sh_size / shdr->sh_entsize); - for (int cnt = 0; - (size_t) cnt < shdr->sh_size / shdr->sh_entsize; - ++cnt) + for (int cnt = 0; (size_t) cnt < entries; ++cnt) { GElf_Rela rela_mem; GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); @@ -531,9 +533,10 @@ check_rel (size_t nsegments, struct segments segments[nsegments], int highidx = -1; GElf_Sym sym_mem; GElf_Sym *sym; + size_t entries = (shdr->sh_entsize == 0 + ? 0 : shdr->sh_size / shdr->sh_entsize); - for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; - ++i) + for (int i = 0; (size_t) i < entries; ++i) { sym = gelf_getsym (symdata, i, &sym_mem); if (sym == NULL) diff --git a/src/readelf.c b/src/readelf.c index 90a14061..c8c9851b 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -910,7 +910,6 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) if (ehdr == NULL) { - elf_error: error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1)); return; } @@ -953,7 +952,7 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) { /* Read the file afresh. */ off_t aroff = elf_getaroff (elf); - pure_elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + pure_elf = dwelf_elf_begin (fd); if (aroff > 0) { /* Archive member. */ @@ -963,7 +962,10 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) pure_elf = armem; } if (pure_elf == NULL) - goto elf_error; + { + error (0, 0, gettext ("cannot read ELF: %s"), elf_errmsg (-1)); + return; + } pure_ebl = ebl_openbackend (pure_elf); if (pure_ebl == NULL) goto ebl_error; @@ -1535,9 +1537,12 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) static const char * -section_name (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr) +section_name (Ebl *ebl, GElf_Shdr *shdr) { - return elf_strptr (ebl->elf, ehdr->e_shstrndx, shdr->sh_name) ?: "???"; + size_t shstrndx; + if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0) + return "???"; + return elf_strptr (ebl->elf, shstrndx, shdr->sh_name) ?: "???"; } @@ -5008,7 +5013,7 @@ listptr_cu (struct listptr_table *table, size_t *idxp, static void print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { const size_t sh_size = (dbg->sectiondata[IDX_debug_abbrev] ? @@ -5016,7 +5021,7 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n" " [ Code]\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); Dwarf_Off offset = 0; @@ -5086,7 +5091,7 @@ print_debug_addr_section (Dwfl_Module *dwflmod __attribute__ ((unused)), { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); if (shdr->sh_size == 0) @@ -5334,7 +5339,7 @@ print_decoded_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, "\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entries:\n", cnt), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset, cnt); /* Compute floor(log16(cnt)). */ @@ -5395,7 +5400,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); const unsigned char *readp = data->d_buf; @@ -5411,7 +5416,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), { invalid_data: error (0, 0, gettext ("invalid data in section [%zu] '%s'"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + elf_ndxscn (scn), section_name (ebl, shdr)); return; } @@ -5555,13 +5560,14 @@ split_dwarf_cu_base (Dwarf *dbg, Dwarf_CU **cu, Dwarf_Addr *cu_base) /* Print content of DWARF .debug_rnglists section. */ static void print_debug_rnglists_section (Dwfl_Module *dwflmod, - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg __attribute__((unused))) { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); Elf_Data *data =(dbg->sectiondata[IDX_debug_rnglists] @@ -5586,7 +5592,7 @@ print_debug_rnglists_section (Dwfl_Module *dwflmod, { invalid_data: error (0, 0, gettext ("invalid data in section [%zu] '%s'"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + elf_ndxscn (scn), section_name (ebl, shdr)); return; } @@ -5946,7 +5952,7 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); sort_listptr (&known_rangelistptr, "rangelistptr"); @@ -6603,18 +6609,24 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, ptrdiff_t start = readp - (unsigned char *) data->d_buf; const unsigned char *const cieend = readp + unit_length; - if (unlikely (cieend > dataend || readp + 8 > dataend)) + if (unlikely (cieend > dataend)) goto invalid_data; Dwarf_Off cie_id; if (length == 4) { + if (unlikely (cieend - readp < 4)) + goto invalid_data; cie_id = read_4ubyte_unaligned_inc (dbg, readp); if (!is_eh_frame && cie_id == DW_CIE_ID_32) cie_id = DW_CIE_ID_64; } else - cie_id = read_8ubyte_unaligned_inc (dbg, readp); + { + if (unlikely (cieend - readp < 8)) + goto invalid_data; + cie_id = read_8ubyte_unaligned_inc (dbg, readp); + } uint_fast8_t version = 2; unsigned int code_alignment_factor; @@ -6626,6 +6638,8 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (cie_id == (is_eh_frame ? 0 : DW_CIE_ID_64)) { + if (unlikely (cieend - readp < 2)) + goto invalid_data; version = *readp++; const char *const augmentation = (const char *) readp; readp = memchr (readp, '\0', cieend - readp); @@ -7532,12 +7546,12 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) static void print_debug_units (Dwfl_Module *dwflmod, - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg, bool debug_types) { const bool silent = !(print_debug_sections & section_info) && !debug_types; - const char *secname = section_name (ebl, ehdr, shdr); + const char *secname = section_name (ebl, shdr); if (!silent) printf (gettext ("\ @@ -7834,12 +7848,13 @@ print_debug_types_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, static void -print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, +print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); size_t address_size @@ -8188,7 +8203,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); if (shdr->sh_size == 0) @@ -8225,7 +8240,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, { invalid_data: error (0, 0, gettext ("invalid data in section [%zu] '%s'"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + elf_ndxscn (scn), section_name (ebl, shdr)); return; } unit_length = read_8ubyte_unaligned_inc (dbg, linep); @@ -8352,7 +8367,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, error (0, 0, gettext ("invalid data at offset %tu in section [%zu] '%s'"), linep - (const unsigned char *) data->d_buf, - elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + elf_ndxscn (scn), section_name (ebl, shdr)); linep = lineendp; continue; } @@ -8852,13 +8867,14 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, static void print_debug_loclists_section (Dwfl_Module *dwflmod, - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); Elf_Data *data = (dbg->sectiondata[IDX_debug_loclists] @@ -8883,7 +8899,7 @@ print_debug_loclists_section (Dwfl_Module *dwflmod, { invalid_data: error (0, 0, gettext ("invalid data in section [%zu] '%s'"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + elf_ndxscn (scn), section_name (ebl, shdr)); return; } @@ -9294,7 +9310,7 @@ print_debug_loc_section (Dwfl_Module *dwflmod, printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); sort_listptr (&known_locsptr, "loclistptr"); @@ -9533,12 +9549,13 @@ mac_compare (const void *p1, const void *p2) static void print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); putc_unlocked ('\n', stdout); @@ -9698,17 +9715,17 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), static void print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); putc_unlocked ('\n', stdout); - Elf_Data *data = (dbg->sectiondata[IDX_debug_macro] - ?: elf_rawdata (scn, NULL)); + Elf_Data *data = elf_getdata (scn, NULL); if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get macro information section data: %s"), @@ -9777,7 +9794,33 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), if (readp + 1 > readendp) goto invalid_data; const unsigned char flag = *readp++; - printf (gettext (" Flag: 0x%" PRIx8 "\n"), flag); + printf (gettext (" Flag: 0x%" PRIx8), flag); + if (flag != 0) + { + printf (" ("); + if ((flag & 0x01) != 0) + { + printf ("offset_size"); + if ((flag & 0xFE) != 0) + printf (", "); + } + if ((flag & 0x02) != 0) + { + printf ("debug_line_offset"); + if ((flag & 0xFC) != 0) + printf (", "); + } + if ((flag & 0x04) != 0) + { + printf ("operands_table"); + if ((flag & 0xF8) != 0) + printf (", "); + } + if ((flag & 0xF8) != 0) + printf ("unknown"); + printf (")"); + } + printf ("\n"); unsigned int offset_len = (flag & 0x01) ? 8 : 4; printf (gettext (" Offset length: %" PRIu8 "\n"), offset_len); @@ -10059,11 +10102,12 @@ print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, /* Print the known exported symbols in the DWARF section '.debug_pubnames'. */ static void print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); int n = 0; @@ -10073,7 +10117,8 @@ print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), /* Print the content of the DWARF string section '.debug_str'. */ static void print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg __attribute__ ((unused))) { @@ -10093,7 +10138,7 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n" " %*s String\n"), elf_ndxscn (scn), - section_name (ebl, ehdr, shdr), (uint64_t) shdr->sh_offset, + section_name (ebl, shdr), (uint64_t) shdr->sh_offset, /* TRANS: the debugstr| prefix makes the string unique. */ digits + 2, sgettext ("debugstr|Offset")); @@ -10117,12 +10162,13 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), static void print_debug_str_offsets_section (Dwfl_Module *dwflmod __attribute__ ((unused)), - Ebl *ebl, GElf_Ehdr *ehdr, + Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\ \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset); if (shdr->sh_size == 0) @@ -10586,12 +10632,13 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), http://sourceware.org/gdb/current/onlinedocs/gdb/Index-Section-Format.html */ static void -print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, +print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, + GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) { printf (gettext ("\nGDB section [%2zu] '%s' at offset %#" PRIx64 " contains %" PRId64 " bytes :\n"), - elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + elf_ndxscn (scn), section_name (ebl, shdr), (uint64_t) shdr->sh_offset, (uint64_t) shdr->sh_size); Elf_Data *data = elf_rawdata (scn, NULL); @@ -12150,14 +12197,26 @@ handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr, const char *name = nhdr.n_namesz == 0 ? "" : data->d_buf + name_offset; const char *desc = data->d_buf + desc_offset; + /* GNU Build Attributes are weird, they store most of their data + into the owner name field. Extract just the owner name + prefix here, then use the rest later as data. */ + bool is_gnu_build_attr + = strncmp (name, ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX, + strlen (ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX)) == 0; + const char *print_name = (is_gnu_build_attr + ? ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX : name); + size_t print_namesz = (is_gnu_build_attr + ? strlen (print_name) : nhdr.n_namesz); + char buf[100]; char buf2[100]; printf (gettext (" %-13.*s %9" PRId32 " %s\n"), - (int) nhdr.n_namesz, name, nhdr.n_descsz, + (int) print_namesz, print_name, nhdr.n_descsz, ehdr->e_type == ET_CORE ? ebl_core_note_type_name (ebl, nhdr.n_type, buf, sizeof (buf)) : ebl_object_note_type_name (ebl, name, nhdr.n_type, + nhdr.n_descsz, buf2, sizeof (buf2))); /* Filter out invalid entries. */ @@ -12193,7 +12252,8 @@ handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr, handle_core_note (ebl, &nhdr, name, desc); } else - ebl_object_note (ebl, name, nhdr.n_type, nhdr.n_descsz, desc); + ebl_object_note (ebl, nhdr.n_namesz, name, nhdr.n_type, + nhdr.n_descsz, desc); } } @@ -12259,7 +12319,8 @@ handle_notes (Ebl *ebl, GElf_Ehdr *ehdr) handle_notes_data (ebl, ehdr, phdr->p_offset, elf_getdata_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz, - ELF_T_NHDR)); + (phdr->p_align == 8 + ? ELF_T_NHDR8 : ELF_T_NHDR))); } } @@ -374,8 +374,10 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) INTERNAL_ERROR (fname); } - if (unlikely (elf_end (elf) != 0)) - INTERNAL_ERROR (fname); + /* Only close ELF handle if this was a "top level" ar file. */ + if (prefix == NULL) + if (unlikely (elf_end (elf) != 0)) + INTERNAL_ERROR (fname); return result; } 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); diff --git a/src/unstrip.c b/src/unstrip.c index ae37d576..723cc746 100644 --- a/src/unstrip.c +++ b/src/unstrip.c @@ -245,6 +245,7 @@ copy_elf (Elf *outelf, Elf *inelf) GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (inelf, &ehdr_mem); + ELF_CHECK (ehdr != NULL, _("cannot get ELF header: %s")); if (shstrndx < SHN_LORESERVE) ehdr->e_shstrndx = shstrndx; else @@ -447,6 +448,9 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr, switch (shdr->sh_type) { case SHT_REL: + if (shdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, "REL section cannot have zero sh_entsize"); + for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i) { GElf_Rel rel_mem; @@ -458,6 +462,9 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr, break; case SHT_RELA: + if (shdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, "RELA section cannot have zero sh_entsize"); + for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i) { GElf_Rela rela_mem; @@ -484,6 +491,10 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr, case SHT_HASH: /* We must expand the table and rejigger its contents. */ { + if (shdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, "HASH section cannot have zero sh_entsize"); + if (symshdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, "Symbol table cannot have zero sh_entsize"); const size_t nsym = symshdr->sh_size / symshdr->sh_entsize; const size_t onent = shdr->sh_size / shdr->sh_entsize; assert (data->d_size == shdr->sh_size); @@ -539,6 +550,11 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr, case SHT_GNU_versym: /* We must expand the table and move its elements around. */ { + if (shdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, + "GNU_versym section cannot have zero sh_entsize"); + if (symshdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, "Symbol table cannot have zero sh_entsize"); const size_t nent = symshdr->sh_size / symshdr->sh_entsize; const size_t onent = shdr->sh_size / shdr->sh_entsize; assert (nent >= onent); @@ -604,6 +620,8 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum, GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + if (shdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, "Symbol table section cannot have zero sh_entsize"); const size_t nsym = shdr->sh_size / shdr->sh_entsize; size_t symndx_map[nsym - 1]; @@ -697,6 +715,7 @@ struct section { Elf_Scn *scn; const char *name; + const char *sig; Elf_Scn *outscn; Dwelf_Strent *strent; GElf_Shdr shdr; @@ -721,7 +740,8 @@ compare_alloc_sections (const struct section *s1, const struct section *s2, static int compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2, - const char *name1, const char *name2) + const char *name1, const char *name2, + const char *sig1, const char *sig2) { /* Sort by sh_flags as an arbitrary ordering. */ if (shdr1->sh_flags < shdr2->sh_flags) @@ -735,6 +755,10 @@ compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2, if (shdr1->sh_size > shdr2->sh_size) return 1; + /* Are they both SHT_GROUP sections? Then compare signatures. */ + if (sig1 != NULL && sig2 != NULL) + return strcmp (sig1, sig2); + /* Sort by name as last resort. */ return strcmp (name1, name2); } @@ -752,7 +776,8 @@ compare_sections (const void *a, const void *b, bool rel) return ((s1->shdr.sh_flags & SHF_ALLOC) ? compare_alloc_sections (s1, s2, rel) : compare_unalloc_sections (&s1->shdr, &s2->shdr, - s1->name, s2->name)); + s1->name, s2->name, + s1->sig, s2->sig)); } static int @@ -987,6 +1012,44 @@ get_section_name (size_t ndx, const GElf_Shdr *shdr, const Elf_Data *shstrtab) return shstrtab->d_buf + shdr->sh_name; } +/* Returns the signature of a group section, or NULL if the given + section isn't a group. */ +static const char * +get_group_sig (Elf *elf, GElf_Shdr *shdr) +{ + if (shdr->sh_type != SHT_GROUP) + return NULL; + + Elf_Scn *symscn = elf_getscn (elf, shdr->sh_link); + if (symscn == NULL) + error (EXIT_FAILURE, 0, _("bad sh_link for group section: %s"), + elf_errmsg (-1)); + + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + if (symshdr == NULL) + error (EXIT_FAILURE, 0, _("couldn't get shdr for group section: %s"), + elf_errmsg (-1)); + + Elf_Data *symdata = elf_getdata (symscn, NULL); + if (symdata == NULL) + error (EXIT_FAILURE, 0, _("bad data for group symbol section: %s"), + elf_errmsg (-1)); + + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, shdr->sh_info, &sym_mem); + if (sym == NULL) + error (EXIT_FAILURE, 0, _("couldn't get symbol for group section: %s"), + elf_errmsg (-1)); + + const char *sig = elf_strptr (elf, symshdr->sh_link, sym->st_name); + if (sig == NULL) + error (EXIT_FAILURE, 0, _("bad symbol name for group section: %s"), + elf_errmsg (-1)); + + return sig; +} + /* Fix things up when prelink has moved some allocated sections around and the debuginfo file's section headers no longer match up. This fills in SECTIONS[0..NALLOC-1].outscn or exits. @@ -1112,6 +1175,7 @@ find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab, sec->scn = elf_getscn (main, i + 1); /* Really just for ndx. */ sec->outscn = NULL; sec->strent = NULL; + sec->sig = get_group_sig (main, &sec->shdr); ++undo_nalloc; } } @@ -1337,6 +1401,7 @@ more sections in stripped file than debug file -- arguments reversed?")); sections[i].scn = scn; sections[i].outscn = NULL; sections[i].strent = NULL; + sections[i].sig = get_group_sig (stripped, shdr); } const struct section *stripped_symtab = NULL; @@ -1355,7 +1420,8 @@ more sections in stripped file than debug file -- arguments reversed?")); /* Locate a matching unallocated section in SECTIONS. */ inline struct section *find_unalloc_section (const GElf_Shdr *shdr, - const char *name) + const char *name, + const char *sig) { size_t l = nalloc, u = stripped_shnum - 1; while (l < u) @@ -1363,7 +1429,8 @@ more sections in stripped file than debug file -- arguments reversed?")); size_t i = (l + u) / 2; struct section *sec = §ions[i]; int cmp = compare_unalloc_sections (shdr, &sec->shdr, - name, sec->name); + name, sec->name, + sig, sec->sig); if (cmp < 0) u = i; else if (cmp > 0) @@ -1436,7 +1503,8 @@ more sections in stripped file than debug file -- arguments reversed?")); else { /* Look for the section that matches. */ - sec = find_unalloc_section (shdr, name); + sec = find_unalloc_section (shdr, name, + get_group_sig (unstripped, shdr)); if (sec == NULL) { /* An additional unallocated section is fine if not SHT_NOBITS. @@ -1622,6 +1690,9 @@ more sections in stripped file than debug file -- arguments reversed?")); Elf_Data *shndxdata = NULL; /* XXX */ + if (shdr_mem.sh_entsize == 0) + error (EXIT_FAILURE, 0, + "SYMTAB section cannot have zero sh_entsize"); for (size_t i = 1; i < shdr_mem.sh_size / shdr_mem.sh_entsize; ++i) { GElf_Sym sym_mem; @@ -1659,6 +1730,20 @@ more sections in stripped file than debug file -- arguments reversed?")); if (shdr_mem.sh_type == SHT_DYNSYM) stripped_dynsym = sec; } + + if (shdr_mem.sh_type == SHT_GROUP) + { + /* We must adjust all the section indices in the group. + Skip the first word, which is the section group flag. + Everything else is a section index. */ + Elf32_Word *shndx = (Elf32_Word *) outdata->d_buf; + for (size_t i = 1; i < shdr_mem.sh_size / sizeof (Elf32_Word); ++i) + if (shndx[i] == SHN_UNDEF || shndx[i] >= stripped_shnum) + error (EXIT_FAILURE, 0, + _("group has invalid section index [%zd]"), i); + else + shndx[i] = ndx_section[shndx[i] - 1]; + } } /* We may need to update the symbol table. */ @@ -1673,11 +1758,16 @@ more sections in stripped file than debug file -- arguments reversed?")); /* Merge the stripped file's symbol table into the unstripped one. */ const size_t stripped_nsym = (stripped_symtab == NULL ? 1 : (stripped_symtab->shdr.sh_size - / stripped_symtab->shdr.sh_entsize)); + / (stripped_symtab->shdr.sh_entsize == 0 + ? 1 + : stripped_symtab->shdr.sh_entsize))); GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (unstripped_symtab, &shdr_mem); ELF_CHECK (shdr != NULL, _("cannot get section header: %s")); + if (shdr->sh_entsize == 0) + error (EXIT_FAILURE, 0, + "unstripped SYMTAB section cannot have zero sh_entsize"); const size_t unstripped_nsym = shdr->sh_size / shdr->sh_entsize; /* First collect all the symbols from both tables. */ |