aboutsummaryrefslogtreecommitdiff
path: root/libiberty/simple-object-elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'libiberty/simple-object-elf.c')
-rw-r--r--libiberty/simple-object-elf.c384
1 files changed, 239 insertions, 145 deletions
diff --git a/libiberty/simple-object-elf.c b/libiberty/simple-object-elf.c
index b249146..7468a1a 100644
--- a/libiberty/simple-object-elf.c
+++ b/libiberty/simple-object-elf.c
@@ -126,6 +126,7 @@ typedef struct {
#define SHN_LORESERVE 0xFF00 /* Begin range of reserved indices */
#define SHN_COMMON 0xFFF2 /* Associated symbol is in common */
#define SHN_XINDEX 0xFFFF /* Section index is held elsewhere */
+#define SHN_HIRESERVE 0xffff /* End of reserved indices */
/* 32-bit ELF program header. */
@@ -193,9 +194,11 @@ typedef struct {
#define SHT_RELA 4 /* Relocation entries with addends */
#define SHT_REL 9 /* Relocation entries, no addends */
#define SHT_GROUP 17 /* Section contains a section group */
+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
/* Values for sh_flags field. */
+#define SHF_INFO_LINK 0x00000040 /* `sh_info' contains SHT index */
#define SHF_EXECINSTR 0x00000004 /* Executable section. */
#define SHF_EXCLUDE 0x80000000 /* Link editor is to exclude this
section from executable and
@@ -1070,7 +1073,7 @@ simple_object_elf_release_write (void *data)
static const char *
simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
simple_object_write *dobj,
- int (*pfn) (const char **),
+ char *(*pfn) (const char *),
int *err)
{
struct simple_object_elf_read *eor =
@@ -1091,7 +1094,10 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
int changed;
int *pfnret;
const char **pfnname;
+ unsigned new_i;
+ unsigned *sh_map;
unsigned first_shndx = 0;
+ unsigned int *symtab_indices_shndx;
shdr_size = (ei_class == ELFCLASS32
? sizeof (Elf32_External_Shdr)
@@ -1130,18 +1136,20 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
return errmsg;
}
- eow->shdrs = XNEWVEC (unsigned char, shdr_size * (shnum - 1));
pfnret = XNEWVEC (int, shnum);
pfnname = XNEWVEC (const char *, shnum);
+ /* Map of symtab to index section. */
+ symtab_indices_shndx = XCNEWVEC (unsigned int, shnum - 1);
+
/* First perform the callbacks to know which sections to preserve and
what name to use for those. */
for (i = 1; i < shnum; ++i)
{
unsigned char *shdr;
- unsigned int sh_name;
+ unsigned int sh_name, sh_type;
const char *name;
- int ret;
+ char *ret;
shdr = shdrs + (i - 1) * shdr_size;
sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
@@ -1156,12 +1164,28 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
name = (const char *) names + sh_name;
- ret = (*pfn) (&name);
- pfnret[i - 1] = ret == 1 ? 0 : -1;
- pfnname[i - 1] = name;
+ ret = (*pfn) (name);
+ pfnret[i - 1] = ret == NULL ? -1 : 0;
+ pfnname[i - 1] = ret == NULL ? name : ret;
if (first_shndx == 0
&& pfnret[i - 1] == 0)
first_shndx = i;
+
+ /* Remember the indexes of existing SHT_SYMTAB_SHNDX sections. */
+ sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_type, Elf_Word);
+ if (sh_type == SHT_SYMTAB_SHNDX)
+ {
+ unsigned int sh_link;
+ sh_link = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_link, Elf_Word);
+ symtab_indices_shndx[sh_link - 1] = i;
+ /* Always discard the extended index sections, after
+ copying it will not be needed. This way we don't need to
+ update it and deal with the ordering constraints of
+ processing the existing symtab and changing the index. */
+ pfnret[i - 1] = -1;
+ }
}
/* Mark sections as preserved that are required by to be preserved
@@ -1244,7 +1268,26 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
}
while (changed);
+ /* Compute a mapping of old -> new section numbers. */
+ sh_map = XNEWVEC (unsigned, shnum);
+ sh_map[0] = 0;
+ new_i = 1;
+ for (i = 1; i < shnum; ++i)
+ {
+ if (pfnret[i - 1] == -1)
+ sh_map[i] = 0;
+ else
+ sh_map[i] = new_i++;
+ }
+ if (new_i - 1 >= SHN_LORESERVE)
+ {
+ *err = ENOTSUP;
+ return "Too many copied sections";
+ }
+ eow->shdrs = XNEWVEC (unsigned char, shdr_size * (new_i - 1));
+
/* Then perform the actual copying. */
+ new_i = 0;
for (i = 1; i < shnum; ++i)
{
unsigned char *shdr;
@@ -1252,11 +1295,14 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
const char *name;
off_t offset;
off_t length;
- int ret;
simple_object_write_section *dest;
off_t flags;
unsigned char *buf;
+ if (pfnret[i - 1])
+ continue;
+
+ new_i++;
shdr = shdrs + (i - 1) * shdr_size;
sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
shdr, sh_name, Elf_Word);
@@ -1265,10 +1311,11 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
*err = 0;
XDELETEVEC (names);
XDELETEVEC (shdrs);
+ XDELETEVEC (symtab_indices_shndx);
return "ELF section name out of range";
}
- name = (const char *) names + sh_name;
+ name = pfnname[i - 1];
offset = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
shdr, sh_offset, Elf_Addr);
length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
@@ -1276,178 +1323,223 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
shdr, sh_type, Elf_Word);
- ret = pfnret[i - 1];
- name = ret == 0 ? pfnname[i - 1] : "";
-
- dest = simple_object_write_create_section (dobj, name, 0, &errmsg, err);
+ dest = simple_object_write_create_section (dobj, pfnname[i - 1],
+ 0, &errmsg, err);
if (dest == NULL)
{
XDELETEVEC (names);
XDELETEVEC (shdrs);
+ XDELETEVEC (symtab_indices_shndx);
return errmsg;
}
/* Record the SHDR of the source. */
- memcpy (eow->shdrs + (i - 1) * shdr_size, shdr, shdr_size);
- shdr = eow->shdrs + (i - 1) * shdr_size;
+ memcpy (eow->shdrs + (new_i - 1) * shdr_size, shdr, shdr_size);
+ shdr = eow->shdrs + (new_i - 1) * shdr_size;
/* Copy the data.
??? This is quite wasteful and ideally would be delayed until
write_to_file (). Thus it questions the interfacing
which eventually should contain destination creation plus
writing. */
- /* Keep empty sections for sections we should discard. This avoids
- the need to rewrite section indices in symtab and relocation
+ buf = XNEWVEC (unsigned char, length);
+ if (!simple_object_internal_read (sobj->descriptor,
+ sobj->offset + offset, buf,
+ (size_t) length, &errmsg, err))
+ {
+ XDELETEVEC (buf);
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ XDELETEVEC (symtab_indices_shndx);
+ return errmsg;
+ }
+
+ /* If we are processing .symtab purge __gnu_lto_v1 and
+ __gnu_lto_slim symbols from it and any symbols in discarded
sections. */
- if (ret == 0)
+ if (sh_type == SHT_SYMTAB)
{
- buf = XNEWVEC (unsigned char, length);
- if (!simple_object_internal_read (sobj->descriptor,
- sobj->offset + offset, buf,
- (size_t) length, &errmsg, err))
+ unsigned entsize = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_entsize, Elf_Addr);
+ unsigned strtab = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_link, Elf_Word);
+ unsigned char *strshdr = shdrs + (strtab - 1) * shdr_size;
+ off_t stroff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ strshdr, sh_offset, Elf_Addr);
+ size_t strsz = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ strshdr, sh_size, Elf_Addr);
+ char *strings = XNEWVEC (char, strsz);
+ char *gnu_lto = strings;
+ unsigned char *ent;
+ unsigned *shndx_table = NULL;
+ simple_object_internal_read (sobj->descriptor,
+ sobj->offset + stroff,
+ (unsigned char *)strings,
+ strsz, &errmsg, err);
+ /* Find gnu_lto_ in strings. */
+ while ((gnu_lto = (char *) memchr (gnu_lto, 'g',
+ strings + strsz - gnu_lto)))
+ if (strncmp (gnu_lto, "gnu_lto_v1",
+ strings + strsz - gnu_lto) == 0)
+ break;
+ else
+ gnu_lto++;
+ /* Read the section index table if present. */
+ if (symtab_indices_shndx[i - 1] != 0)
{
- XDELETEVEC (buf);
- XDELETEVEC (names);
- XDELETEVEC (shdrs);
- return errmsg;
+ unsigned char *sidxhdr = shdrs + (strtab - 1) * shdr_size;
+ off_t sidxoff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ sidxhdr, sh_offset, Elf_Addr);
+ size_t sidxsz = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ sidxhdr, sh_size, Elf_Addr);
+ shndx_table = (unsigned *)XNEWVEC (char, sidxsz);
+ simple_object_internal_read (sobj->descriptor,
+ sobj->offset + sidxoff,
+ (unsigned char *)shndx_table,
+ sidxsz, &errmsg, err);
}
-
- /* If we are processing .symtab purge __gnu_lto_v1 and
- __gnu_lto_slim symbols from it. */
- if (sh_type == SHT_SYMTAB)
+ for (ent = buf; ent < buf + length; ent += entsize)
{
- unsigned entsize = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
- shdr, sh_entsize, Elf_Addr);
- unsigned strtab = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
- shdr, sh_link, Elf_Word);
- unsigned char *strshdr = shdrs + (strtab - 1) * shdr_size;
- off_t stroff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
- strshdr, sh_offset, Elf_Addr);
- size_t strsz = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
- strshdr, sh_size, Elf_Addr);
- char *strings = XNEWVEC (char, strsz);
- unsigned char *ent;
- simple_object_internal_read (sobj->descriptor,
- sobj->offset + stroff,
- (unsigned char *)strings,
- strsz, &errmsg, err);
- /* Find gnu_lto_ in strings. */
- char *gnu_lto = strings;
- while ((gnu_lto = memchr (gnu_lto, 'g',
- strings + strsz - gnu_lto)))
- if (strncmp (gnu_lto, "gnu_lto_v1",
- strings + strsz - gnu_lto) == 0)
- break;
- else
- gnu_lto++;
- for (ent = buf; ent < buf + length; ent += entsize)
+ unsigned st_shndx = ELF_FETCH_FIELD (type_functions, ei_class,
+ Sym, ent,
+ st_shndx, Elf_Half);
+ unsigned raw_st_shndx = st_shndx;
+ unsigned char *st_info;
+ unsigned char *st_other;
+ int discard = 0;
+ if (ei_class == ELFCLASS32)
{
- unsigned st_shndx = ELF_FETCH_FIELD (type_functions, ei_class,
- Sym, ent,
- st_shndx, Elf_Half);
- unsigned char *st_info;
- unsigned char *st_other;
- int discard = 0;
- if (ei_class == ELFCLASS32)
+ st_info = &((Elf32_External_Sym *)ent)->st_info;
+ st_other = &((Elf32_External_Sym *)ent)->st_other;
+ }
+ else
+ {
+ st_info = &((Elf64_External_Sym *)ent)->st_info;
+ st_other = &((Elf64_External_Sym *)ent)->st_other;
+ }
+ if (st_shndx == SHN_XINDEX)
+ st_shndx = type_functions->fetch_Elf_Word
+ ((unsigned char *)(shndx_table + (ent - buf) / entsize));
+ /* Eliminate all COMMONs - this includes __gnu_lto_v1
+ and __gnu_lto_slim which otherwise cause endless
+ LTO plugin invocation. */
+ if (st_shndx == SHN_COMMON)
+ discard = 1;
+ /* We also need to remove symbols refering to sections
+ we'll eventually remove as with fat LTO objects
+ we otherwise get duplicate symbols at final link
+ (with GNU ld, gold is fine and ignores symbols in
+ sections marked as EXCLUDE). ld/20513 */
+ else if (st_shndx != SHN_UNDEF
+ && st_shndx < shnum
+ && pfnret[st_shndx - 1] == -1)
+ discard = 1;
+
+ if (discard)
+ {
+ /* Make discarded symbols undefined and unnamed
+ in case it is local. */
+ int bind = ELF_ST_BIND (*st_info);
+ int other = STV_DEFAULT;
+ if (bind == STB_LOCAL)
{
- st_info = &((Elf32_External_Sym *)ent)->st_info;
- st_other = &((Elf32_External_Sym *)ent)->st_other;
+ /* Make discarded local symbols unnamed and
+ defined in the first prevailing section. */
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_name, Elf_Word, 0);
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_shndx, Elf_Half,
+ sh_map[first_shndx]);
}
else
{
- st_info = &((Elf64_External_Sym *)ent)->st_info;
- st_other = &((Elf64_External_Sym *)ent)->st_other;
- }
- /* Eliminate all COMMONs - this includes __gnu_lto_v1
- and __gnu_lto_slim which otherwise cause endless
- LTO plugin invocation. */
- if (st_shndx == SHN_COMMON)
- discard = 1;
- /* We also need to remove symbols refering to sections
- we'll eventually remove as with fat LTO objects
- we otherwise get duplicate symbols at final link
- (with GNU ld, gold is fine and ignores symbols in
- sections marked as EXCLUDE). ld/20513 */
- else if (st_shndx != SHN_UNDEF
- && st_shndx < shnum
- && pfnret[st_shndx - 1] == -1)
- discard = 1;
-
- if (discard)
- {
- /* Make discarded symbols undefined and unnamed
- in case it is local. */
- int bind = ELF_ST_BIND (*st_info);
- int other = STV_DEFAULT;
- if (bind == STB_LOCAL)
- {
- /* Make discarded local symbols unnamed and
- defined in the first prevailing section. */
- ELF_SET_FIELD (type_functions, ei_class, Sym,
- ent, st_name, Elf_Word, 0);
- ELF_SET_FIELD (type_functions, ei_class, Sym,
- ent, st_shndx, Elf_Half, first_shndx);
- }
- else
- {
- /* Make discarded global symbols hidden weak
- undefined and sharing the gnu_lto_ name. */
- bind = STB_WEAK;
- other = STV_HIDDEN;
- if (gnu_lto)
- ELF_SET_FIELD (type_functions, ei_class, Sym,
- ent, st_name, Elf_Word,
- gnu_lto - strings);
- ELF_SET_FIELD (type_functions, ei_class, Sym,
- ent, st_shndx, Elf_Half, SHN_UNDEF);
- }
- *st_other = other;
- *st_info = ELF_ST_INFO (bind, STT_NOTYPE);
- ELF_SET_FIELD (type_functions, ei_class, Sym,
- ent, st_value, Elf_Addr, 0);
+ /* Make discarded global symbols hidden weak
+ undefined and sharing the gnu_lto_ name. */
+ bind = STB_WEAK;
+ other = STV_HIDDEN;
+ if (gnu_lto)
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_name, Elf_Word,
+ gnu_lto - strings);
ELF_SET_FIELD (type_functions, ei_class, Sym,
- ent, st_size, Elf_Word, 0);
+ ent, st_shndx, Elf_Half, SHN_UNDEF);
}
+ *st_other = other;
+ *st_info = ELF_ST_INFO (bind, STT_NOTYPE);
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_value, Elf_Addr, 0);
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_size, Elf_Word, 0);
}
- XDELETEVEC (strings);
+ else if (raw_st_shndx < SHN_LORESERVE
+ || raw_st_shndx == SHN_XINDEX)
+ /* Remap the section reference. */
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_shndx, Elf_Half, sh_map[st_shndx]);
}
-
- errmsg = simple_object_write_add_data (dobj, dest,
- buf, length, 1, err);
- XDELETEVEC (buf);
- if (errmsg)
+ XDELETEVEC (strings);
+ XDELETEVEC (shndx_table);
+ }
+ else if (sh_type == SHT_GROUP)
+ {
+ /* Remap section indices in groups and remove removed members. */
+ unsigned char *ent, *dst;
+ for (dst = ent = buf + 4; ent < buf + length; ent += 4)
{
- XDELETEVEC (names);
- XDELETEVEC (shdrs);
- return errmsg;
+ unsigned shndx = type_functions->fetch_Elf_Word (ent);
+ if (pfnret[shndx - 1] == -1)
+ ;
+ else
+ {
+ type_functions->set_Elf_Word (dst, sh_map[shndx]);
+ dst += 4;
+ }
}
+ /* Adjust the length. */
+ length = dst - buf;
}
- else
+
+ errmsg = simple_object_write_add_data (dobj, dest,
+ buf, length, 1, err);
+ XDELETEVEC (buf);
+ if (errmsg)
{
- /* For deleted sections mark the section header table entry as
- unused. That allows the link editor to remove it in a partial
- link. */
- ELF_SET_FIELD (type_functions, ei_class, Shdr,
- shdr, sh_type, Elf_Word, SHT_NULL);
- ELF_SET_FIELD (type_functions, ei_class, Shdr,
- shdr, sh_info, Elf_Word, 0);
- ELF_SET_FIELD (type_functions, ei_class, Shdr,
- shdr, sh_link, Elf_Word, 0);
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ XDELETEVEC (symtab_indices_shndx);
+ return errmsg;
}
flags = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
shdr, sh_flags, Elf_Addr);
- if (ret == 0)
- {
- /* The debugobj doesn't contain any code, thus no trampolines.
- Even when the original object needs trampolines, debugobj
- doesn't. */
- if (strcmp (name, ".note.GNU-stack") == 0)
- flags &= ~SHF_EXECINSTR;
- flags &= ~SHF_EXCLUDE;
- }
- else if (ret == -1)
- flags = SHF_EXCLUDE;
+ /* Remap the section references. */
+ {
+ unsigned int sh_info, sh_link;
+ if (flags & SHF_INFO_LINK || sh_type == SHT_REL || sh_type == SHT_RELA)
+ {
+ sh_info = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_info, Elf_Word);
+ if (sh_info < SHN_LORESERVE
+ || sh_info > SHN_HIRESERVE)
+ sh_info = sh_map[sh_info];
+ ELF_SET_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_info, Elf_Word, sh_info);
+ }
+ sh_link = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_link, Elf_Word);
+ if (sh_link < SHN_LORESERVE
+ || sh_link > SHN_HIRESERVE)
+ sh_link = sh_map[sh_link];
+ ELF_SET_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_link, Elf_Word, sh_link);
+ }
+ /* The debugobj doesn't contain any code, thus no trampolines.
+ Even when the original object needs trampolines, debugobj
+ doesn't. */
+ if (strcmp (name, ".note.GNU-stack") == 0)
+ flags &= ~SHF_EXECINSTR;
+ /* Clear SHF_EXCLUDE on to be preserved sections. */
+ flags &= ~SHF_EXCLUDE;
ELF_SET_FIELD (type_functions, ei_class, Shdr,
shdr, sh_flags, Elf_Addr, flags);
}
@@ -1456,6 +1548,8 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
XDELETEVEC (shdrs);
XDELETEVEC (pfnret);
XDELETEVEC (pfnname);
+ XDELETEVEC (symtab_indices_shndx);
+ XDELETEVEC (sh_map);
return NULL;
}