diff options
Diffstat (limited to 'src/unstrip.c')
-rw-r--r-- | src/unstrip.c | 102 |
1 files changed, 96 insertions, 6 deletions
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. */ |