+/* Copy all sections in an ELF file. */
+
+static const char *
+simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
+ simple_object_write *dobj,
+ int (*pfn) (const char **),
+ int *err)
+{
+ struct simple_object_elf_read *eor =
+ (struct simple_object_elf_read *) sobj->data;
+ const struct elf_type_functions *type_functions = eor->type_functions;
+ struct simple_object_elf_write *eow =
+ (struct simple_object_elf_write *) dobj->data;
+ unsigned char ei_class = eor->ei_class;
+ size_t shdr_size;
+ unsigned int shnum;
+ unsigned char *shdrs;
+ const char *errmsg;
+ unsigned char *shstrhdr;
+ size_t name_size;
+ off_t shstroff;
+ unsigned char *names;
+ unsigned int i;
+ int *pfnret;
+ const char **pfnname;
+
+ shdr_size = (ei_class == ELFCLASS32
+ ? sizeof (Elf32_External_Shdr)
+ : sizeof (Elf64_External_Shdr));
+
+ /* Read the section headers. We skip section 0, which is not a
+ useful section. */
+
+ shnum = eor->shnum;
+ shdrs = XNEWVEC (unsigned char, shdr_size * (shnum - 1));
+
+ if (!simple_object_internal_read (sobj->descriptor,
+ sobj->offset + eor->shoff + shdr_size,
+ shdrs,
+ shdr_size * (shnum - 1),
+ &errmsg, err))
+ {
+ XDELETEVEC (shdrs);
+ return errmsg;
+ }
+
+ /* Read the section names. */
+
+ shstrhdr = shdrs + (eor->shstrndx - 1) * shdr_size;
+ name_size = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shstrhdr, sh_size, Elf_Addr);
+ shstroff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shstrhdr, sh_offset, Elf_Addr);
+ names = XNEWVEC (unsigned char, name_size);
+ if (!simple_object_internal_read (sobj->descriptor,
+ sobj->offset + shstroff,
+ names, name_size, &errmsg, err))
+ {
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ return errmsg;
+ }
+
+ eow->shdrs = XNEWVEC (unsigned char, shdr_size * (shnum - 1));
+ pfnret = XNEWVEC (int, shnum);
+ pfnname = XNEWVEC (const char *, shnum);
+
+ /* 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;
+ const char *name;
+ int ret;
+
+ shdr = shdrs + (i - 1) * shdr_size;
+ sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_name, Elf_Word);
+ if (sh_name >= name_size)
+ {
+ *err = 0;
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ return "ELF section name out of range";
+ }
+
+ name = (const char *) names + sh_name;
+
+ ret = (*pfn) (&name);
+ pfnret[i - 1] = ret == 1 ? 0 : -1;
+ pfnname[i - 1] = name;
+ }
+
+ /* Mark sections as preserved that are required by to be preserved
+ sections. */
+ for (i = 1; i < shnum; ++i)
+ {
+ unsigned char *shdr;
+ unsigned int sh_type, sh_info, sh_link;
+ off_t offset;
+ off_t length;
+
+ shdr = shdrs + (i - 1) * shdr_size;
+ sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_type, Elf_Word);
+ sh_info = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_info, Elf_Word);
+ sh_link = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_link, Elf_Word);
+ if (sh_type == SHT_GROUP)
+ {
+ /* Mark groups containing copied sections. */
+ unsigned entsize = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_entsize, Elf_Addr);
+ unsigned char *ent, *buf;
+ int keep = 0;
+ offset = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_offset, Elf_Addr);
+ length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_size, Elf_Addr);
+ 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);
+ return errmsg;
+ }
+ for (ent = buf + entsize; ent < buf + length; ent += entsize)
+ {
+ unsigned sec = type_functions->fetch_Elf_Word (ent);
+ if (pfnret[sec - 1] == 0)
+ keep = 1;
+ }
+ if (keep)
+ {
+ pfnret[sh_link - 1] = 0;
+ pfnret[i - 1] = 0;
+ }
+ }
+ if (sh_type == SHT_RELA
+ || sh_type == SHT_REL)
+ {
+ /* Mark relocation sections and symtab of copied sections. */
+ if (pfnret[sh_info - 1] == 0)
+ {
+ pfnret[sh_link - 1] = 0;
+ pfnret[i - 1] = 0;
+ }
+ }
+ if (sh_type == SHT_SYMTAB)
+ {
+ /* Mark strings sections of copied symtabs. */
+ if (pfnret[i - 1] == 0)
+ pfnret[sh_link - 1] = 0;
+ }
+ }
+
+ /* Then perform the actual copying. */
+ for (i = 1; i < shnum; ++i)
+ {
+ unsigned char *shdr;
+ unsigned int sh_name, sh_type;
+ const char *name;
+ off_t offset;
+ off_t length;
+ int ret;
+ const char *errmsg;
+ simple_object_write_section *dest;
+ off_t flags;
+ unsigned char *buf;
+
+ shdr = shdrs + (i - 1) * shdr_size;
+ sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_name, Elf_Word);
+ if (sh_name >= name_size)
+ {
+ *err = 0;
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ return "ELF section name out of range";
+ }
+
+ name = (const char *) names + sh_name;
+ offset = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_offset, Elf_Addr);
+ length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_size, Elf_Addr);
+ 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);
+ if (dest == NULL)
+ {
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ 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;
+
+ /* 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
+ sections. */
+ if (ret == 0)
+ {
+ 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);
+ return errmsg;
+ }
+
+ /* If we are processing .symtab purge __gnu_lto_v1 and
+ __gnu_lto_slim symbols from it. */
+ if (sh_type == SHT_SYMTAB)
+ {
+ 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);
+ 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 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;
+ }
+ /* Eliminate all COMMONs - this includes __gnu_lto_v1
+ and __gnu_lto_slim which otherwise cause endless
+ LTO plugin invocation. */
+ if (st_shndx == SHN_COMMON)
+ /* Setting st_name to "" seems to work to purge
+ COMMON symbols (in addition to setting their
+ size to zero). */
+ 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. */
+ if (ELF_ST_BIND (*st_info) == STB_LOCAL)
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_name, Elf_Word, 0);
+ 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);
+ ELF_SET_FIELD (type_functions, ei_class, Sym,
+ ent, st_shndx, Elf_Half, SHN_UNDEF);
+ *st_info = ELF_ST_INFO (ELF_ST_BIND (*st_info),
+ STT_NOTYPE);
+ *st_other = STV_DEFAULT;
+ }
+ }
+ XDELETEVEC (strings);
+ }
+
+ errmsg = simple_object_write_add_data (dobj, dest,
+ buf, length, 1, err);
+ XDELETEVEC (buf);
+ if (errmsg)
+ {
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ return errmsg;
+ }
+ }
+ else
+ {
+ /* 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);
+ }
+
+ flags = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_flags, Elf_Addr);
+ if (ret == 0)
+ flags &= ~SHF_EXCLUDE;
+ else if (ret == -1)
+ flags = SHF_EXCLUDE;
+ ELF_SET_FIELD (type_functions, ei_class, Shdr,
+ shdr, sh_flags, Elf_Addr, flags);
+ }
+
+ XDELETEVEC (names);
+ XDELETEVEC (shdrs);
+ XDELETEVEC (pfnret);
+ XDELETEVEC (pfnname);
+
+ return NULL;
+}
+
+