summaryrefslogtreecommitdiffstats
path: root/src/unstrip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/unstrip.c')
-rw-r--r--src/unstrip.c102
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 = &sections[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. */