| /* ELF executable support for BFD. |
| |
| Copyright (C) 1993-2024 Free Software Foundation, Inc. |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| |
| /* |
| SECTION |
| ELF backends |
| |
| BFD support for ELF formats is being worked on. |
| Currently, the best supported back ends are for sparc and i386 |
| (running svr4 or Solaris 2). |
| |
| Documentation of the internals of the support code still needs |
| to be written. The code is changing quickly enough that we |
| haven't bothered yet. */ |
| |
| /* For sparc64-cross-sparc32. */ |
| #define _SYSCALL32 |
| #include "sysdep.h" |
| #include <limits.h> |
| #include "bfd.h" |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #define ARCH_SIZE 0 |
| #include "elf-bfd.h" |
| #include "libiberty.h" |
| #include "safe-ctype.h" |
| #include "elf-linux-core.h" |
| |
| #ifdef CORE_HEADER |
| #include CORE_HEADER |
| #endif |
| |
| static int elf_sort_sections (const void *, const void *); |
| static bool assign_file_positions_except_relocs (bfd *, struct bfd_link_info *); |
| static bool swap_out_syms (bfd *, struct elf_strtab_hash **, int, |
| struct bfd_link_info *); |
| static bool elf_parse_notes (bfd *abfd, char *buf, size_t size, |
| file_ptr offset, size_t align); |
| |
| /* Swap version information in and out. The version information is |
| currently size independent. If that ever changes, this code will |
| need to move into elfcode.h. */ |
| |
| /* Swap in a Verdef structure. */ |
| |
| void |
| _bfd_elf_swap_verdef_in (bfd *abfd, |
| const Elf_External_Verdef *src, |
| Elf_Internal_Verdef *dst) |
| { |
| dst->vd_version = H_GET_16 (abfd, src->vd_version); |
| dst->vd_flags = H_GET_16 (abfd, src->vd_flags); |
| dst->vd_ndx = H_GET_16 (abfd, src->vd_ndx); |
| dst->vd_cnt = H_GET_16 (abfd, src->vd_cnt); |
| dst->vd_hash = H_GET_32 (abfd, src->vd_hash); |
| dst->vd_aux = H_GET_32 (abfd, src->vd_aux); |
| dst->vd_next = H_GET_32 (abfd, src->vd_next); |
| } |
| |
| /* Swap out a Verdef structure. */ |
| |
| void |
| _bfd_elf_swap_verdef_out (bfd *abfd, |
| const Elf_Internal_Verdef *src, |
| Elf_External_Verdef *dst) |
| { |
| H_PUT_16 (abfd, src->vd_version, dst->vd_version); |
| H_PUT_16 (abfd, src->vd_flags, dst->vd_flags); |
| H_PUT_16 (abfd, src->vd_ndx, dst->vd_ndx); |
| H_PUT_16 (abfd, src->vd_cnt, dst->vd_cnt); |
| H_PUT_32 (abfd, src->vd_hash, dst->vd_hash); |
| H_PUT_32 (abfd, src->vd_aux, dst->vd_aux); |
| H_PUT_32 (abfd, src->vd_next, dst->vd_next); |
| } |
| |
| /* Swap in a Verdaux structure. */ |
| |
| void |
| _bfd_elf_swap_verdaux_in (bfd *abfd, |
| const Elf_External_Verdaux *src, |
| Elf_Internal_Verdaux *dst) |
| { |
| dst->vda_name = H_GET_32 (abfd, src->vda_name); |
| dst->vda_next = H_GET_32 (abfd, src->vda_next); |
| } |
| |
| /* Swap out a Verdaux structure. */ |
| |
| void |
| _bfd_elf_swap_verdaux_out (bfd *abfd, |
| const Elf_Internal_Verdaux *src, |
| Elf_External_Verdaux *dst) |
| { |
| H_PUT_32 (abfd, src->vda_name, dst->vda_name); |
| H_PUT_32 (abfd, src->vda_next, dst->vda_next); |
| } |
| |
| /* Swap in a Verneed structure. */ |
| |
| void |
| _bfd_elf_swap_verneed_in (bfd *abfd, |
| const Elf_External_Verneed *src, |
| Elf_Internal_Verneed *dst) |
| { |
| dst->vn_version = H_GET_16 (abfd, src->vn_version); |
| dst->vn_cnt = H_GET_16 (abfd, src->vn_cnt); |
| dst->vn_file = H_GET_32 (abfd, src->vn_file); |
| dst->vn_aux = H_GET_32 (abfd, src->vn_aux); |
| dst->vn_next = H_GET_32 (abfd, src->vn_next); |
| } |
| |
| /* Swap out a Verneed structure. */ |
| |
| void |
| _bfd_elf_swap_verneed_out (bfd *abfd, |
| const Elf_Internal_Verneed *src, |
| Elf_External_Verneed *dst) |
| { |
| H_PUT_16 (abfd, src->vn_version, dst->vn_version); |
| H_PUT_16 (abfd, src->vn_cnt, dst->vn_cnt); |
| H_PUT_32 (abfd, src->vn_file, dst->vn_file); |
| H_PUT_32 (abfd, src->vn_aux, dst->vn_aux); |
| H_PUT_32 (abfd, src->vn_next, dst->vn_next); |
| } |
| |
| /* Swap in a Vernaux structure. */ |
| |
| void |
| _bfd_elf_swap_vernaux_in (bfd *abfd, |
| const Elf_External_Vernaux *src, |
| Elf_Internal_Vernaux *dst) |
| { |
| dst->vna_hash = H_GET_32 (abfd, src->vna_hash); |
| dst->vna_flags = H_GET_16 (abfd, src->vna_flags); |
| dst->vna_other = H_GET_16 (abfd, src->vna_other); |
| dst->vna_name = H_GET_32 (abfd, src->vna_name); |
| dst->vna_next = H_GET_32 (abfd, src->vna_next); |
| } |
| |
| /* Swap out a Vernaux structure. */ |
| |
| void |
| _bfd_elf_swap_vernaux_out (bfd *abfd, |
| const Elf_Internal_Vernaux *src, |
| Elf_External_Vernaux *dst) |
| { |
| H_PUT_32 (abfd, src->vna_hash, dst->vna_hash); |
| H_PUT_16 (abfd, src->vna_flags, dst->vna_flags); |
| H_PUT_16 (abfd, src->vna_other, dst->vna_other); |
| H_PUT_32 (abfd, src->vna_name, dst->vna_name); |
| H_PUT_32 (abfd, src->vna_next, dst->vna_next); |
| } |
| |
| /* Swap in a Versym structure. */ |
| |
| void |
| _bfd_elf_swap_versym_in (bfd *abfd, |
| const Elf_External_Versym *src, |
| Elf_Internal_Versym *dst) |
| { |
| dst->vs_vers = H_GET_16 (abfd, src->vs_vers); |
| } |
| |
| /* Swap out a Versym structure. */ |
| |
| void |
| _bfd_elf_swap_versym_out (bfd *abfd, |
| const Elf_Internal_Versym *src, |
| Elf_External_Versym *dst) |
| { |
| H_PUT_16 (abfd, src->vs_vers, dst->vs_vers); |
| } |
| |
| /* Standard ELF hash function. Do not change this function; you will |
| cause invalid hash tables to be generated. */ |
| |
| unsigned long |
| bfd_elf_hash (const char *namearg) |
| { |
| uint32_t h = 0; |
| |
| for (const unsigned char *name = (const unsigned char *) namearg; |
| *name; name++) |
| { |
| h = (h << 4) + *name; |
| h ^= (h >> 24) & 0xf0; |
| } |
| return h & 0x0fffffff; |
| } |
| |
| /* DT_GNU_HASH hash function. Do not change this function; you will |
| cause invalid hash tables to be generated. */ |
| |
| unsigned long |
| bfd_elf_gnu_hash (const char *namearg) |
| { |
| uint32_t h = 5381; |
| |
| for (const unsigned char *name = (const unsigned char *) namearg; |
| *name; name++) |
| h = (h << 5) + h + *name; |
| return h; |
| } |
| |
| /* Create a tdata field OBJECT_SIZE bytes in length, zeroed out and with |
| the object_id field of an elf_obj_tdata field set to OBJECT_ID. */ |
| bool |
| bfd_elf_allocate_object (bfd *abfd, |
| size_t object_size, |
| enum elf_target_id object_id) |
| { |
| BFD_ASSERT (object_size >= sizeof (struct elf_obj_tdata)); |
| abfd->tdata.any = bfd_zalloc (abfd, object_size); |
| if (abfd->tdata.any == NULL) |
| return false; |
| |
| elf_object_id (abfd) = object_id; |
| if (abfd->direction != read_direction) |
| { |
| struct output_elf_obj_tdata *o = bfd_zalloc (abfd, sizeof *o); |
| if (o == NULL) |
| return false; |
| elf_tdata (abfd)->o = o; |
| elf_program_header_size (abfd) = (bfd_size_type) -1; |
| } |
| return true; |
| } |
| |
| |
| bool |
| bfd_elf_make_object (bfd *abfd) |
| { |
| const struct elf_backend_data *bed = get_elf_backend_data (abfd); |
| return bfd_elf_allocate_object (abfd, sizeof (struct elf_obj_tdata), |
| bed->target_id); |
| } |
| |
| bool |
| bfd_elf_mkcorefile (bfd *abfd) |
| { |
| /* I think this can be done just like an object file. */ |
| if (!abfd->xvec->_bfd_set_format[(int) bfd_object] (abfd)) |
| return false; |
| elf_tdata (abfd)->core = bfd_zalloc (abfd, sizeof (*elf_tdata (abfd)->core)); |
| return elf_tdata (abfd)->core != NULL; |
| } |
| |
| char * |
| bfd_elf_get_str_section (bfd *abfd, unsigned int shindex) |
| { |
| Elf_Internal_Shdr **i_shdrp; |
| bfd_byte *shstrtab = NULL; |
| file_ptr offset; |
| bfd_size_type shstrtabsize; |
| |
| i_shdrp = elf_elfsections (abfd); |
| if (i_shdrp == 0 |
| || shindex >= elf_numsections (abfd) |
| || i_shdrp[shindex] == 0) |
| return NULL; |
| |
| shstrtab = i_shdrp[shindex]->contents; |
| if (shstrtab == NULL) |
| { |
| /* No cached one, attempt to read, and cache what we read. */ |
| offset = i_shdrp[shindex]->sh_offset; |
| shstrtabsize = i_shdrp[shindex]->sh_size; |
| |
| if (shstrtabsize == 0 |
| || bfd_seek (abfd, offset, SEEK_SET) != 0 |
| || (shstrtab |
| = _bfd_mmap_readonly_persistent (abfd, shstrtabsize)) == NULL) |
| { |
| /* Once we've failed to read it, make sure we don't keep |
| trying. Otherwise, we'll keep allocating space for |
| the string table over and over. */ |
| i_shdrp[shindex]->sh_size = 0; |
| } |
| else if (shstrtab[shstrtabsize - 1] != 0) |
| { |
| /* It is an error if a string table isn't terminated. */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: string table [%u] is corrupt"), abfd, shindex); |
| shstrtab[shstrtabsize - 1] = 0; |
| } |
| i_shdrp[shindex]->contents = shstrtab; |
| } |
| return (char *) shstrtab; |
| } |
| |
| char * |
| bfd_elf_string_from_elf_section (bfd *abfd, |
| unsigned int shindex, |
| unsigned int strindex) |
| { |
| Elf_Internal_Shdr *hdr; |
| |
| if (strindex == 0) |
| return ""; |
| |
| if (elf_elfsections (abfd) == NULL || shindex >= elf_numsections (abfd)) |
| return NULL; |
| |
| hdr = elf_elfsections (abfd)[shindex]; |
| |
| if (hdr->contents == NULL) |
| { |
| if (hdr->sh_type != SHT_STRTAB && hdr->sh_type < SHT_LOOS) |
| { |
| /* PR 17512: file: f057ec89. */ |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: attempt to load strings from" |
| " a non-string section (number %d)"), |
| abfd, shindex); |
| return NULL; |
| } |
| |
| if (bfd_elf_get_str_section (abfd, shindex) == NULL) |
| return NULL; |
| } |
| else |
| { |
| /* PR 24273: The string section's contents may have already |
| been loaded elsewhere, eg because a corrupt file has the |
| string section index in the ELF header pointing at a group |
| section. So be paranoid, and test that the last byte of |
| the section is zero. */ |
| if (hdr->sh_size == 0 || hdr->contents[hdr->sh_size - 1] != 0) |
| return NULL; |
| } |
| |
| if (strindex >= hdr->sh_size) |
| { |
| unsigned int shstrndx = elf_elfheader(abfd)->e_shstrndx; |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: invalid string offset %u >= %" PRIu64 " for section `%s'"), |
| abfd, strindex, (uint64_t) hdr->sh_size, |
| (shindex == shstrndx && strindex == hdr->sh_name |
| ? ".shstrtab" |
| : bfd_elf_string_from_elf_section (abfd, shstrndx, hdr->sh_name))); |
| return NULL; |
| } |
| |
| return ((char *) hdr->contents) + strindex; |
| } |
| |
| /* Read and convert symbols to internal format. |
| SYMCOUNT specifies the number of symbols to read, starting from |
| symbol SYMOFFSET. If any of INTSYM_BUF, EXTSYM_BUF or EXTSHNDX_BUF |
| are non-NULL, they are used to store the internal symbols, external |
| symbols, and symbol section index extensions, respectively. |
| Returns a pointer to the internal symbol buffer (malloced if necessary) |
| or NULL if there were no symbols or some kind of problem. */ |
| |
| Elf_Internal_Sym * |
| bfd_elf_get_elf_syms (bfd *ibfd, |
| Elf_Internal_Shdr *symtab_hdr, |
| size_t symcount, |
| size_t symoffset, |
| Elf_Internal_Sym *intsym_buf, |
| void *extsym_buf, |
| Elf_External_Sym_Shndx *extshndx_buf) |
| { |
| Elf_Internal_Shdr *shndx_hdr; |
| void *alloc_ext; |
| const bfd_byte *esym; |
| Elf_External_Sym_Shndx *alloc_extshndx; |
| Elf_External_Sym_Shndx *shndx; |
| Elf_Internal_Sym *alloc_intsym; |
| Elf_Internal_Sym *isym; |
| Elf_Internal_Sym *isymend; |
| const struct elf_backend_data *bed; |
| size_t extsym_size; |
| size_t amt; |
| file_ptr pos; |
| |
| if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) |
| abort (); |
| |
| if (symcount == 0) |
| return intsym_buf; |
| |
| if (elf_use_dt_symtab_p (ibfd)) |
| { |
| /* Use dynamic symbol table. */ |
| if (elf_tdata (ibfd)->dt_symtab_count != symcount + symoffset) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return NULL; |
| } |
| return elf_tdata (ibfd)->dt_symtab + symoffset; |
| } |
| |
| /* Normal syms might have section extension entries. */ |
| shndx_hdr = NULL; |
| if (elf_symtab_shndx_list (ibfd) != NULL) |
| { |
| elf_section_list * entry; |
| Elf_Internal_Shdr **sections = elf_elfsections (ibfd); |
| |
| /* Find an index section that is linked to this symtab section. */ |
| for (entry = elf_symtab_shndx_list (ibfd); entry != NULL; entry = entry->next) |
| { |
| /* PR 20063. */ |
| if (entry->hdr.sh_link >= elf_numsections (ibfd)) |
| continue; |
| |
| if (sections[entry->hdr.sh_link] == symtab_hdr) |
| { |
| shndx_hdr = & entry->hdr; |
| break; |
| }; |
| } |
| |
| if (shndx_hdr == NULL) |
| { |
| if (symtab_hdr == &elf_symtab_hdr (ibfd)) |
| /* Not really accurate, but this was how the old code used |
| to work. */ |
| shndx_hdr = &elf_symtab_shndx_list (ibfd)->hdr; |
| /* Otherwise we do nothing. The assumption is that |
| the index table will not be needed. */ |
| } |
| } |
| |
| /* Read the symbols. */ |
| alloc_ext = NULL; |
| alloc_extshndx = NULL; |
| alloc_intsym = NULL; |
| bed = get_elf_backend_data (ibfd); |
| extsym_size = bed->s->sizeof_sym; |
| if (_bfd_mul_overflow (symcount, extsym_size, &amt)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| return NULL; |
| } |
| pos = symtab_hdr->sh_offset + symoffset * extsym_size; |
| size_t alloc_ext_size = amt; |
| if (bfd_seek (ibfd, pos, SEEK_SET) != 0 |
| || !_bfd_mmap_read_temporary (&extsym_buf, &alloc_ext_size, |
| &alloc_ext, ibfd, false)) |
| { |
| intsym_buf = NULL; |
| goto out2; |
| } |
| |
| size_t alloc_extshndx_size = 0; |
| if (shndx_hdr == NULL || shndx_hdr->sh_size == 0) |
| extshndx_buf = NULL; |
| else |
| { |
| if (_bfd_mul_overflow (symcount, sizeof (Elf_External_Sym_Shndx), &amt)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| intsym_buf = NULL; |
| goto out1; |
| } |
| alloc_extshndx_size = amt; |
| pos = shndx_hdr->sh_offset + symoffset * sizeof (Elf_External_Sym_Shndx); |
| if (bfd_seek (ibfd, pos, SEEK_SET) != 0 |
| || !_bfd_mmap_read_temporary ((void **) &extshndx_buf, |
| &alloc_extshndx_size, |
| (void **) &alloc_extshndx, |
| ibfd, false)) |
| { |
| intsym_buf = NULL; |
| goto out1; |
| } |
| } |
| |
| if (intsym_buf == NULL) |
| { |
| if (_bfd_mul_overflow (symcount, sizeof (Elf_Internal_Sym), &amt)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| goto out1; |
| } |
| alloc_intsym = (Elf_Internal_Sym *) bfd_malloc (amt); |
| intsym_buf = alloc_intsym; |
| if (intsym_buf == NULL) |
| goto out1; |
| } |
| |
| /* Convert the symbols to internal form. */ |
| isymend = intsym_buf + symcount; |
| for (esym = (const bfd_byte *) extsym_buf, isym = intsym_buf, |
| shndx = extshndx_buf; |
| isym < isymend; |
| esym += extsym_size, isym++, shndx = shndx != NULL ? shndx + 1 : NULL) |
| if (!(*bed->s->swap_symbol_in) (ibfd, esym, shndx, isym)) |
| { |
| symoffset += (esym - (bfd_byte *) extsym_buf) / extsym_size; |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB symbol number %lu references" |
| " nonexistent SHT_SYMTAB_SHNDX section"), |
| ibfd, (unsigned long) symoffset); |
| free (alloc_intsym); |
| intsym_buf = NULL; |
| goto out1; |
| } |
| |
| out1: |
| _bfd_munmap_readonly_temporary (alloc_extshndx, alloc_extshndx_size); |
| out2: |
| _bfd_munmap_readonly_temporary (alloc_ext, alloc_ext_size); |
| |
| return intsym_buf; |
| } |
| |
| /* Look up a symbol name. */ |
| const char * |
| bfd_elf_sym_name (bfd *abfd, |
| Elf_Internal_Shdr *symtab_hdr, |
| Elf_Internal_Sym *isym, |
| asection *sym_sec) |
| { |
| const char *name; |
| unsigned int iname = isym->st_name; |
| unsigned int shindex = symtab_hdr->sh_link; |
| |
| if (iname == 0 && ELF_ST_TYPE (isym->st_info) == STT_SECTION |
| /* Check for a bogus st_shndx to avoid crashing. */ |
| && isym->st_shndx < elf_numsections (abfd)) |
| { |
| iname = elf_elfsections (abfd)[isym->st_shndx]->sh_name; |
| shindex = elf_elfheader (abfd)->e_shstrndx; |
| } |
| |
| name = bfd_elf_string_from_elf_section (abfd, shindex, iname); |
| if (name == NULL) |
| name = "(null)"; |
| else if (sym_sec && *name == '\0') |
| name = bfd_section_name (sym_sec); |
| |
| return name; |
| } |
| |
| /* Return the name of the group signature symbol. Why isn't the |
| signature just a string? */ |
| |
| static const char * |
| group_signature (bfd *abfd, Elf_Internal_Shdr *ghdr) |
| { |
| Elf_Internal_Shdr *hdr; |
| unsigned char esym[sizeof (Elf64_External_Sym)]; |
| Elf_External_Sym_Shndx eshndx; |
| Elf_Internal_Sym isym; |
| |
| /* First we need to ensure the symbol table is available. Make sure |
| that it is a symbol table section. */ |
| if (ghdr->sh_link >= elf_numsections (abfd)) |
| return NULL; |
| hdr = elf_elfsections (abfd) [ghdr->sh_link]; |
| if (hdr->sh_type != SHT_SYMTAB |
| || ! bfd_section_from_shdr (abfd, ghdr->sh_link)) |
| return NULL; |
| |
| /* Go read the symbol. */ |
| hdr = &elf_tdata (abfd)->symtab_hdr; |
| if (bfd_elf_get_elf_syms (abfd, hdr, 1, ghdr->sh_info, |
| &isym, esym, &eshndx) == NULL) |
| return NULL; |
| |
| return bfd_elf_sym_name (abfd, hdr, &isym, NULL); |
| } |
| |
| static bool |
| is_valid_group_section_header (Elf_Internal_Shdr *shdr, size_t minsize) |
| { |
| return (shdr->sh_size >= minsize |
| && shdr->sh_entsize == GRP_ENTRY_SIZE |
| && shdr->sh_size % GRP_ENTRY_SIZE == 0 |
| && shdr->bfd_section != NULL); |
| } |
| |
| |
| /* Set next_in_group, sec_group list pointers, and group names. */ |
| |
| static bool |
| process_sht_group_entries (bfd *abfd, |
| Elf_Internal_Shdr *ghdr, unsigned int gidx) |
| { |
| unsigned char *contents; |
| |
| /* Read the raw contents. */ |
| if (!bfd_malloc_and_get_section (abfd, ghdr->bfd_section, &contents)) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: could not read contents of group [%u]"), abfd, gidx); |
| return false; |
| } |
| |
| asection *last_elt = NULL; |
| const char *gname = NULL; |
| unsigned char *p = contents + ghdr->sh_size; |
| while (1) |
| { |
| unsigned int idx; |
| Elf_Internal_Shdr *shdr; |
| asection *elt; |
| |
| p -= 4; |
| idx = H_GET_32 (abfd, p); |
| if (p == contents) |
| { |
| if ((idx & GRP_COMDAT) != 0) |
| ghdr->bfd_section->flags |
| |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD; |
| break; |
| } |
| |
| if (idx == 0 |
| || idx >= elf_numsections (abfd) |
| || (shdr = elf_elfsections (abfd)[idx])->sh_type == SHT_GROUP |
| || ((elt = shdr->bfd_section) != NULL |
| && elf_sec_group (elt) != NULL |
| && elf_sec_group (elt) != ghdr->bfd_section)) |
| { |
| _bfd_error_handler |
| (_("%pB: invalid entry (%#x) in group [%u]"), |
| abfd, idx, gidx); |
| continue; |
| } |
| |
| /* PR binutils/23199: According to the ELF gABI all sections in |
| a group must be marked with SHF_GROUP, but some tools |
| generate broken objects. Fix them up here. */ |
| shdr->sh_flags |= SHF_GROUP; |
| |
| if (elt == NULL) |
| { |
| if (shdr->sh_type != SHT_RELA && shdr->sh_type != SHT_REL) |
| { |
| const char *name = bfd_elf_string_from_elf_section |
| (abfd, elf_elfheader (abfd)->e_shstrndx, shdr->sh_name); |
| |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unexpected type (%#x) section `%s' in group [%u]"), |
| abfd, shdr->sh_type, name, gidx); |
| } |
| continue; |
| } |
| |
| /* Don't try to add a section to elf_next_in_group list twice. */ |
| if (elf_sec_group (elt) != NULL) |
| continue; |
| |
| if (last_elt == NULL) |
| { |
| /* Start a circular list with one element. |
| It will be in reverse order to match what gas does. */ |
| elf_next_in_group (elt) = elt; |
| /* Point the group section to it. */ |
| elf_next_in_group (ghdr->bfd_section) = elt; |
| gname = group_signature (abfd, ghdr); |
| if (gname == NULL) |
| { |
| free (contents); |
| return false; |
| } |
| } |
| else |
| { |
| elf_next_in_group (elt) = elf_next_in_group (last_elt); |
| elf_next_in_group (last_elt) = elt; |
| } |
| last_elt = elt; |
| elf_group_name (elt) = gname; |
| elf_sec_group (elt) = ghdr->bfd_section; |
| } |
| |
| free (contents); |
| return true; |
| } |
| |
| bool |
| _bfd_elf_setup_sections (bfd *abfd) |
| { |
| bool result = true; |
| |
| /* Process SHF_LINK_ORDER. */ |
| for (asection *s = abfd->sections; s != NULL; s = s->next) |
| { |
| Elf_Internal_Shdr *this_hdr = &elf_section_data (s)->this_hdr; |
| if ((this_hdr->sh_flags & SHF_LINK_ORDER) != 0) |
| { |
| unsigned int elfsec = this_hdr->sh_link; |
| /* An sh_link value of 0 is now allowed. It indicates that linked |
| to section has already been discarded, but that the current |
| section has been retained for some other reason. This linking |
| section is still a candidate for later garbage collection |
| however. */ |
| if (elfsec == 0) |
| { |
| elf_linked_to_section (s) = NULL; |
| } |
| else |
| { |
| asection *linksec = NULL; |
| |
| if (elfsec < elf_numsections (abfd)) |
| { |
| this_hdr = elf_elfsections (abfd)[elfsec]; |
| linksec = this_hdr->bfd_section; |
| } |
| |
| /* PR 1991, 2008: |
| Some strip/objcopy may leave an incorrect value in |
| sh_link. We don't want to proceed. */ |
| if (linksec == NULL) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: sh_link [%d] in section `%pA' is incorrect"), |
| s->owner, elfsec, s); |
| result = false; |
| } |
| |
| elf_linked_to_section (s) = linksec; |
| } |
| } |
| } |
| |
| /* Process section groups. */ |
| for (unsigned int i = 1; i < elf_numsections (abfd); i++) |
| { |
| Elf_Internal_Shdr *shdr = elf_elfsections (abfd)[i]; |
| |
| if (shdr && shdr->sh_type == SHT_GROUP) |
| { |
| if (is_valid_group_section_header (shdr, GRP_ENTRY_SIZE)) |
| { |
| if (shdr->sh_size >= 2 * GRP_ENTRY_SIZE |
| && !process_sht_group_entries (abfd, shdr, i)) |
| result = false; |
| } |
| else |
| { |
| /* PR binutils/18758: Beware of corrupt binaries with |
| invalid group data. */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: section group entry number %u is corrupt"), abfd, i); |
| result = false; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| bool |
| bfd_elf_is_group_section (bfd *abfd ATTRIBUTE_UNUSED, const asection *sec) |
| { |
| return elf_next_in_group (sec) != NULL; |
| } |
| |
| const char * |
| bfd_elf_group_name (bfd *abfd ATTRIBUTE_UNUSED, const asection *sec) |
| { |
| if (elf_sec_group (sec) != NULL) |
| return elf_group_name (sec); |
| return NULL; |
| } |
| |
| /* Make a BFD section from an ELF section. We store a pointer to the |
| BFD section in the bfd_section field of the header. */ |
| |
| bool |
| _bfd_elf_make_section_from_shdr (bfd *abfd, |
| Elf_Internal_Shdr *hdr, |
| const char *name, |
| int shindex) |
| { |
| asection *newsect; |
| flagword flags; |
| const struct elf_backend_data *bed; |
| unsigned int opb = bfd_octets_per_byte (abfd, NULL); |
| |
| if (hdr->bfd_section != NULL) |
| return true; |
| |
| newsect = bfd_make_section_anyway (abfd, name); |
| if (newsect == NULL) |
| return false; |
| |
| hdr->bfd_section = newsect; |
| elf_section_data (newsect)->this_hdr = *hdr; |
| elf_section_data (newsect)->this_idx = shindex; |
| |
| /* Always use the real type/flags. */ |
| elf_section_type (newsect) = hdr->sh_type; |
| elf_section_flags (newsect) = hdr->sh_flags; |
| |
| newsect->filepos = hdr->sh_offset; |
| |
| flags = SEC_NO_FLAGS; |
| if (hdr->sh_type != SHT_NOBITS) |
| flags |= SEC_HAS_CONTENTS; |
| if (hdr->sh_type == SHT_GROUP) |
| flags |= SEC_GROUP; |
| if ((hdr->sh_flags & SHF_ALLOC) != 0) |
| { |
| flags |= SEC_ALLOC; |
| if (hdr->sh_type != SHT_NOBITS) |
| flags |= SEC_LOAD; |
| } |
| if ((hdr->sh_flags & SHF_WRITE) == 0) |
| flags |= SEC_READONLY; |
| if ((hdr->sh_flags & SHF_EXECINSTR) != 0) |
| flags |= SEC_CODE; |
| else if ((flags & SEC_LOAD) != 0) |
| flags |= SEC_DATA; |
| if ((hdr->sh_flags & SHF_MERGE) != 0) |
| { |
| flags |= SEC_MERGE; |
| newsect->entsize = hdr->sh_entsize; |
| } |
| if ((hdr->sh_flags & SHF_STRINGS) != 0) |
| flags |= SEC_STRINGS; |
| if ((hdr->sh_flags & SHF_TLS) != 0) |
| flags |= SEC_THREAD_LOCAL; |
| if ((hdr->sh_flags & SHF_EXCLUDE) != 0) |
| flags |= SEC_EXCLUDE; |
| |
| switch (elf_elfheader (abfd)->e_ident[EI_OSABI]) |
| { |
| /* FIXME: We should not recognize SHF_GNU_MBIND for ELFOSABI_NONE, |
| but binutils as of 2019-07-23 did not set the EI_OSABI header |
| byte. */ |
| case ELFOSABI_GNU: |
| case ELFOSABI_FREEBSD: |
| if ((hdr->sh_flags & SHF_GNU_RETAIN) != 0) |
| elf_tdata (abfd)->has_gnu_osabi |= elf_gnu_osabi_retain; |
| /* Fall through */ |
| case ELFOSABI_NONE: |
| if ((hdr->sh_flags & SHF_GNU_MBIND) != 0) |
| elf_tdata (abfd)->has_gnu_osabi |= elf_gnu_osabi_mbind; |
| break; |
| } |
| |
| if ((flags & SEC_ALLOC) == 0) |
| { |
| /* The debugging sections appear to be recognized only by name, |
| not any sort of flag. Their SEC_ALLOC bits are cleared. */ |
| if (name [0] == '.') |
| { |
| if (startswith (name, ".debug") |
| || startswith (name, ".gnu.debuglto_.debug_") |
| || startswith (name, ".gnu.linkonce.wi.") |
| || startswith (name, ".zdebug")) |
| flags |= SEC_DEBUGGING | SEC_ELF_OCTETS; |
| else if (startswith (name, GNU_BUILD_ATTRS_SECTION_NAME) |
| || startswith (name, ".note.gnu")) |
| { |
| flags |= SEC_ELF_OCTETS; |
| opb = 1; |
| } |
| else if (startswith (name, ".line") |
| || startswith (name, ".stab") |
| || strcmp (name, ".gdb_index") == 0) |
| flags |= SEC_DEBUGGING; |
| } |
| } |
| |
| if (!bfd_set_section_vma (newsect, hdr->sh_addr / opb) |
| || !bfd_set_section_size (newsect, hdr->sh_size) |
| || !bfd_set_section_alignment (newsect, bfd_log2 (hdr->sh_addralign |
| & -hdr->sh_addralign))) |
| return false; |
| |
| /* As a GNU extension, if the name begins with .gnu.linkonce, we |
| only link a single copy of the section. This is used to support |
| g++. g++ will emit each template expansion in its own section. |
| The symbols will be defined as weak, so that multiple definitions |
| are permitted. The GNU linker extension is to actually discard |
| all but one of the sections. */ |
| if (startswith (name, ".gnu.linkonce") |
| && elf_next_in_group (newsect) == NULL) |
| flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD; |
| |
| if (!bfd_set_section_flags (newsect, flags)) |
| return false; |
| |
| bed = get_elf_backend_data (abfd); |
| if (bed->elf_backend_section_flags) |
| if (!bed->elf_backend_section_flags (hdr)) |
| return false; |
| |
| /* We do not parse the PT_NOTE segments as we are interested even in the |
| separate debug info files which may have the segments offsets corrupted. |
| PT_NOTEs from the core files are currently not parsed using BFD. */ |
| if (hdr->sh_type == SHT_NOTE && hdr->sh_size != 0) |
| { |
| bfd_byte *contents; |
| |
| if (!_bfd_elf_mmap_section_contents (abfd, newsect, &contents)) |
| return false; |
| |
| elf_parse_notes (abfd, (char *) contents, hdr->sh_size, |
| hdr->sh_offset, hdr->sh_addralign); |
| _bfd_elf_munmap_section_contents (newsect, contents); |
| } |
| |
| if ((newsect->flags & SEC_ALLOC) != 0) |
| { |
| Elf_Internal_Phdr *phdr; |
| unsigned int i, nload; |
| |
| /* Some ELF linkers produce binaries with all the program header |
| p_paddr fields zero. If we have such a binary with more than |
| one PT_LOAD header, then leave the section lma equal to vma |
| so that we don't create sections with overlapping lma. */ |
| phdr = elf_tdata (abfd)->phdr; |
| for (nload = 0, i = 0; i < elf_elfheader (abfd)->e_phnum; i++, phdr++) |
| if (phdr->p_paddr != 0) |
| break; |
| else if (phdr->p_type == PT_LOAD && phdr->p_memsz != 0) |
| ++nload; |
| if (i >= elf_elfheader (abfd)->e_phnum && nload > 1) |
| return true; |
| |
| phdr = elf_tdata (abfd)->phdr; |
| for (i = 0; i < elf_elfheader (abfd)->e_phnum; i++, phdr++) |
| { |
| if (((phdr->p_type == PT_LOAD |
| && (hdr->sh_flags & SHF_TLS) == 0) |
| || phdr->p_type == PT_TLS) |
| && ELF_SECTION_IN_SEGMENT (hdr, phdr)) |
| { |
| if ((newsect->flags & SEC_LOAD) == 0) |
| newsect->lma = (phdr->p_paddr |
| + hdr->sh_addr - phdr->p_vaddr) / opb; |
| else |
| /* We used to use the same adjustment for SEC_LOAD |
| sections, but that doesn't work if the segment |
| is packed with code from multiple VMAs. |
| Instead we calculate the section LMA based on |
| the segment LMA. It is assumed that the |
| segment will contain sections with contiguous |
| LMAs, even if the VMAs are not. */ |
| newsect->lma = (phdr->p_paddr |
| + hdr->sh_offset - phdr->p_offset) / opb; |
| |
| /* With contiguous segments, we can't tell from file |
| offsets whether a section with zero size should |
| be placed at the end of one segment or the |
| beginning of the next. Decide based on vaddr. */ |
| if (hdr->sh_addr >= phdr->p_vaddr |
| && (hdr->sh_addr + hdr->sh_size |
| <= phdr->p_vaddr + phdr->p_memsz)) |
| break; |
| } |
| } |
| } |
| |
| /* Compress/decompress DWARF debug sections with names: .debug_*, |
| .zdebug_*, .gnu.debuglto_.debug_, after the section flags is set. */ |
| if ((newsect->flags & SEC_DEBUGGING) != 0 |
| && (newsect->flags & SEC_HAS_CONTENTS) != 0 |
| && (newsect->flags & SEC_ELF_OCTETS) != 0) |
| { |
| enum { nothing, compress, decompress } action = nothing; |
| int compression_header_size; |
| bfd_size_type uncompressed_size; |
| unsigned int uncompressed_align_power; |
| enum compression_type ch_type = ch_none; |
| bool compressed |
| = bfd_is_section_compressed_info (abfd, newsect, |
| &compression_header_size, |
| &uncompressed_size, |
| &uncompressed_align_power, |
| &ch_type); |
| |
| /* Should we decompress? */ |
| if ((abfd->flags & BFD_DECOMPRESS) != 0 && compressed) |
| action = decompress; |
| |
| /* Should we compress? Or convert to a different compression? */ |
| else if ((abfd->flags & BFD_COMPRESS) != 0 |
| && newsect->size != 0 |
| && compression_header_size >= 0 |
| && uncompressed_size > 0) |
| { |
| if (!compressed) |
| action = compress; |
| else |
| { |
| enum compression_type new_ch_type = ch_none; |
| if ((abfd->flags & BFD_COMPRESS_GABI) != 0) |
| new_ch_type = ((abfd->flags & BFD_COMPRESS_ZSTD) != 0 |
| ? ch_compress_zstd : ch_compress_zlib); |
| if (new_ch_type != ch_type) |
| action = compress; |
| } |
| } |
| |
| if (action == compress) |
| { |
| if (!bfd_init_section_compress_status (abfd, newsect)) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unable to compress section %s"), abfd, name); |
| return false; |
| } |
| } |
| else if (action == decompress) |
| { |
| if (!bfd_init_section_decompress_status (abfd, newsect)) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unable to decompress section %s"), abfd, name); |
| return false; |
| } |
| #ifndef HAVE_ZSTD |
| if (newsect->compress_status == DECOMPRESS_SECTION_ZSTD) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_ ("%pB: section %s is compressed with zstd, but BFD " |
| "is not built with zstd support"), |
| abfd, name); |
| newsect->compress_status = COMPRESS_SECTION_NONE; |
| return false; |
| } |
| #endif |
| if (abfd->is_linker_input |
| && name[1] == 'z') |
| { |
| /* Rename section from .zdebug_* to .debug_* so that ld |
| scripts will see this section as a debug section. */ |
| char *new_name = bfd_zdebug_name_to_debug (abfd, name); |
| if (new_name == NULL) |
| return false; |
| bfd_rename_section (newsect, new_name); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| const char *const bfd_elf_section_type_names[] = |
| { |
| "SHT_NULL", "SHT_PROGBITS", "SHT_SYMTAB", "SHT_STRTAB", |
| "SHT_RELA", "SHT_HASH", "SHT_DYNAMIC", "SHT_NOTE", |
| "SHT_NOBITS", "SHT_REL", "SHT_SHLIB", "SHT_DYNSYM", |
| }; |
| |
| /* ELF relocs are against symbols. If we are producing relocatable |
| output, and the reloc is against an external symbol, and nothing |
| has given us any additional addend, the resulting reloc will also |
| be against the same symbol. In such a case, we don't want to |
| change anything about the way the reloc is handled, since it will |
| all be done at final link time. Rather than put special case code |
| into bfd_perform_relocation, all the reloc types use this howto |
| function, or should call this function for relocatable output. */ |
| |
| bfd_reloc_status_type |
| bfd_elf_generic_reloc (bfd *abfd ATTRIBUTE_UNUSED, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void *data ATTRIBUTE_UNUSED, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| if (output_bfd != NULL |
| && (symbol->flags & BSF_SECTION_SYM) == 0 |
| && (! reloc_entry->howto->partial_inplace |
| || reloc_entry->addend == 0)) |
| { |
| reloc_entry->address += input_section->output_offset; |
| return bfd_reloc_ok; |
| } |
| |
| /* In some cases the relocation should be treated as output section |
| relative, as when linking ELF DWARF into PE COFF. Many ELF |
| targets lack section relative relocations and instead use |
| ordinary absolute relocations for references between DWARF |
| sections. That is arguably a bug in those targets but it happens |
| to work for the usual case of linking to non-loaded ELF debug |
| sections with VMAs forced to zero. PE COFF on the other hand |
| doesn't allow a section VMA of zero. */ |
| if (output_bfd == NULL |
| && !reloc_entry->howto->pc_relative |
| && (symbol->section->flags & SEC_DEBUGGING) != 0 |
| && (input_section->flags & SEC_DEBUGGING) != 0) |
| reloc_entry->addend -= symbol->section->output_section->vma; |
| |
| return bfd_reloc_continue; |
| } |
| |
| /* Returns TRUE if section A matches section B. |
| Names, addresses and links may be different, but everything else |
| should be the same. */ |
| |
| static bool |
| section_match (const Elf_Internal_Shdr * a, |
| const Elf_Internal_Shdr * b) |
| { |
| if (a->sh_type != b->sh_type |
| || ((a->sh_flags ^ b->sh_flags) & ~SHF_INFO_LINK) != 0 |
| || a->sh_addralign != b->sh_addralign |
| || a->sh_entsize != b->sh_entsize) |
| return false; |
| if (a->sh_type == SHT_SYMTAB |
| || a->sh_type == SHT_STRTAB) |
| return true; |
| return a->sh_size == b->sh_size; |
| } |
| |
| /* Find a section in OBFD that has the same characteristics |
| as IHEADER. Return the index of this section or SHN_UNDEF if |
| none can be found. Check's section HINT first, as this is likely |
| to be the correct section. */ |
| |
| static unsigned int |
| find_link (const bfd *obfd, const Elf_Internal_Shdr *iheader, |
| const unsigned int hint) |
| { |
| Elf_Internal_Shdr ** oheaders = elf_elfsections (obfd); |
| unsigned int i; |
| |
| BFD_ASSERT (iheader != NULL); |
| |
| /* See PR 20922 for a reproducer of the NULL test. */ |
| if (hint < elf_numsections (obfd) |
| && oheaders[hint] != NULL |
| && section_match (oheaders[hint], iheader)) |
| return hint; |
| |
| for (i = 1; i < elf_numsections (obfd); i++) |
| { |
| Elf_Internal_Shdr * oheader = oheaders[i]; |
| |
| if (oheader == NULL) |
| continue; |
| if (section_match (oheader, iheader)) |
| /* FIXME: Do we care if there is a potential for |
| multiple matches ? */ |
| return i; |
| } |
| |
| return SHN_UNDEF; |
| } |
| |
| /* PR 19938: Attempt to set the ELF section header fields of an OS or |
| Processor specific section, based upon a matching input section. |
| Returns TRUE upon success, FALSE otherwise. */ |
| |
| static bool |
| copy_special_section_fields (const bfd *ibfd, |
| bfd *obfd, |
| const Elf_Internal_Shdr *iheader, |
| Elf_Internal_Shdr *oheader, |
| const unsigned int secnum) |
| { |
| const struct elf_backend_data *bed = get_elf_backend_data (obfd); |
| const Elf_Internal_Shdr **iheaders |
| = (const Elf_Internal_Shdr **) elf_elfsections (ibfd); |
| bool changed = false; |
| unsigned int sh_link; |
| |
| if (oheader->sh_type == SHT_NOBITS) |
| { |
| /* This is a feature for objcopy --only-keep-debug: |
| When a section's type is changed to NOBITS, we preserve |
| the sh_link and sh_info fields so that they can be |
| matched up with the original. |
| |
| Note: Strictly speaking these assignments are wrong. |
| The sh_link and sh_info fields should point to the |
| relevent sections in the output BFD, which may not be in |
| the same location as they were in the input BFD. But |
| the whole point of this action is to preserve the |
| original values of the sh_link and sh_info fields, so |
| that they can be matched up with the section headers in |
| the original file. So strictly speaking we may be |
| creating an invalid ELF file, but it is only for a file |
| that just contains debug info and only for sections |
| without any contents. */ |
| if (oheader->sh_link == 0) |
| oheader->sh_link = iheader->sh_link; |
| if (oheader->sh_info == 0) |
| oheader->sh_info = iheader->sh_info; |
| return true; |
| } |
| |
| /* Allow the target a chance to decide how these fields should be set. */ |
| if (bed->elf_backend_copy_special_section_fields (ibfd, obfd, |
| iheader, oheader)) |
| return true; |
| |
| /* We have an iheader which might match oheader, and which has non-zero |
| sh_info and/or sh_link fields. Attempt to follow those links and find |
| the section in the output bfd which corresponds to the linked section |
| in the input bfd. */ |
| if (iheader->sh_link != SHN_UNDEF) |
| { |
| /* See PR 20931 for a reproducer. */ |
| if (iheader->sh_link >= elf_numsections (ibfd)) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: invalid sh_link field (%d) in section number %d"), |
| ibfd, iheader->sh_link, secnum); |
| return false; |
| } |
| |
| sh_link = find_link (obfd, iheaders[iheader->sh_link], iheader->sh_link); |
| if (sh_link != SHN_UNDEF) |
| { |
| oheader->sh_link = sh_link; |
| changed = true; |
| } |
| else |
| /* FIXME: Should we install iheader->sh_link |
| if we could not find a match ? */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: failed to find link section for section %d"), obfd, secnum); |
| } |
| |
| if (iheader->sh_info) |
| { |
| /* The sh_info field can hold arbitrary information, but if the |
| SHF_LINK_INFO flag is set then it should be interpreted as a |
| section index. */ |
| if (iheader->sh_flags & SHF_INFO_LINK) |
| { |
| sh_link = find_link (obfd, iheaders[iheader->sh_info], |
| iheader->sh_info); |
| if (sh_link != SHN_UNDEF) |
| oheader->sh_flags |= SHF_INFO_LINK; |
| } |
| else |
| /* No idea what it means - just copy it. */ |
| sh_link = iheader->sh_info; |
| |
| if (sh_link != SHN_UNDEF) |
| { |
| oheader->sh_info = sh_link; |
| changed = true; |
| } |
| else |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: failed to find info section for section %d"), obfd, secnum); |
| } |
| |
| return changed; |
| } |
| |
| /* Copy the program header and other data from one object module to |
| another. */ |
| |
| bool |
| _bfd_elf_copy_private_bfd_data (bfd *ibfd, bfd *obfd) |
| { |
| const Elf_Internal_Shdr **iheaders |
| = (const Elf_Internal_Shdr **) elf_elfsections (ibfd); |
| Elf_Internal_Shdr **oheaders = elf_elfsections (obfd); |
| const struct elf_backend_data *bed; |
| unsigned int i; |
| |
| if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour |
| || bfd_get_flavour (obfd) != bfd_target_elf_flavour) |
| return true; |
| |
| if (!elf_flags_init (obfd)) |
| { |
| elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; |
| elf_flags_init (obfd) = true; |
| } |
| |
| elf_gp (obfd) = elf_gp (ibfd); |
| |
| /* Also copy the EI_OSABI field. */ |
| elf_elfheader (obfd)->e_ident[EI_OSABI] = |
| elf_elfheader (ibfd)->e_ident[EI_OSABI]; |
| |
| /* If set, copy the EI_ABIVERSION field. */ |
| if (elf_elfheader (ibfd)->e_ident[EI_ABIVERSION]) |
| elf_elfheader (obfd)->e_ident[EI_ABIVERSION] |
| = elf_elfheader (ibfd)->e_ident[EI_ABIVERSION]; |
| |
| /* Copy object attributes. */ |
| _bfd_elf_copy_obj_attributes (ibfd, obfd); |
| |
| if (iheaders == NULL || oheaders == NULL) |
| return true; |
| |
| bed = get_elf_backend_data (obfd); |
| |
| /* Possibly copy other fields in the section header. */ |
| for (i = 1; i < elf_numsections (obfd); i++) |
| { |
| unsigned int j; |
| Elf_Internal_Shdr * oheader = oheaders[i]; |
| |
| /* Ignore ordinary sections. SHT_NOBITS sections are considered however |
| because of a special case need for generating separate debug info |
| files. See below for more details. */ |
| if (oheader == NULL |
| || (oheader->sh_type != SHT_NOBITS |
| && oheader->sh_type < SHT_LOOS)) |
| continue; |
| |
| /* Ignore empty sections, and sections whose |
| fields have already been initialised. */ |
| if (oheader->sh_size == 0 |
| || (oheader->sh_info != 0 && oheader->sh_link != 0)) |
| continue; |
| |
| /* Scan for the matching section in the input bfd. |
| First we try for a direct mapping between the input and |
| output sections. */ |
| for (j = 1; j < elf_numsections (ibfd); j++) |
| { |
| const Elf_Internal_Shdr * iheader = iheaders[j]; |
| |
| if (iheader == NULL) |
| continue; |
| |
| if (oheader->bfd_section != NULL |
| && iheader->bfd_section != NULL |
| && iheader->bfd_section->output_section != NULL |
| && iheader->bfd_section->output_section == oheader->bfd_section) |
| { |
| /* We have found a connection from the input section to |
| the output section. Attempt to copy the header fields. |
| If this fails then do not try any further sections - |
| there should only be a one-to-one mapping between |
| input and output. */ |
| if (!copy_special_section_fields (ibfd, obfd, |
| iheader, oheader, i)) |
| j = elf_numsections (ibfd); |
| break; |
| } |
| } |
| |
| if (j < elf_numsections (ibfd)) |
| continue; |
| |
| /* That failed. So try to deduce the corresponding input section. |
| Unfortunately we cannot compare names as the output string table |
| is empty, so instead we check size, address and type. */ |
| for (j = 1; j < elf_numsections (ibfd); j++) |
| { |
| const Elf_Internal_Shdr * iheader = iheaders[j]; |
| |
| if (iheader == NULL) |
| continue; |
| |
| /* Try matching fields in the input section's header. |
| Since --only-keep-debug turns all non-debug sections into |
| SHT_NOBITS sections, the output SHT_NOBITS type matches any |
| input type. */ |
| if ((oheader->sh_type == SHT_NOBITS |
| || iheader->sh_type == oheader->sh_type) |
| && (iheader->sh_flags & ~ SHF_INFO_LINK) |
| == (oheader->sh_flags & ~ SHF_INFO_LINK) |
| && iheader->sh_addralign == oheader->sh_addralign |
| && iheader->sh_entsize == oheader->sh_entsize |
| && iheader->sh_size == oheader->sh_size |
| && iheader->sh_addr == oheader->sh_addr |
| && (iheader->sh_info != oheader->sh_info |
| || iheader->sh_link != oheader->sh_link)) |
| { |
| if (copy_special_section_fields (ibfd, obfd, iheader, oheader, i)) |
| break; |
| } |
| } |
| |
| if (j == elf_numsections (ibfd) && oheader->sh_type >= SHT_LOOS) |
| { |
| /* Final attempt. Call the backend copy function |
| with a NULL input section. */ |
| (void) bed->elf_backend_copy_special_section_fields (ibfd, obfd, |
| NULL, oheader); |
| } |
| } |
| |
| return true; |
| } |
| |
| static const char * |
| get_segment_type (unsigned int p_type) |
| { |
| const char *pt; |
| switch (p_type) |
| { |
| case PT_NULL: pt = "NULL"; break; |
| case PT_LOAD: pt = "LOAD"; break; |
| case PT_DYNAMIC: pt = "DYNAMIC"; break; |
| case PT_INTERP: pt = "INTERP"; break; |
| case PT_NOTE: pt = "NOTE"; break; |
| case PT_SHLIB: pt = "SHLIB"; break; |
| case PT_PHDR: pt = "PHDR"; break; |
| case PT_TLS: pt = "TLS"; break; |
| case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break; |
| case PT_GNU_STACK: pt = "STACK"; break; |
| case PT_GNU_RELRO: pt = "RELRO"; break; |
| case PT_GNU_SFRAME: pt = "SFRAME"; break; |
| default: pt = NULL; break; |
| } |
| return pt; |
| } |
| |
| /* Print out the program headers. */ |
| |
| bool |
| _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg) |
| { |
| FILE *f = (FILE *) farg; |
| Elf_Internal_Phdr *p; |
| asection *s; |
| bfd_byte *dynbuf = NULL; |
| |
| p = elf_tdata (abfd)->phdr; |
| if (p != NULL) |
| { |
| unsigned int i, c; |
| |
| fprintf (f, _("\nProgram Header:\n")); |
| c = elf_elfheader (abfd)->e_phnum; |
| for (i = 0; i < c; i++, p++) |
| { |
| const char *pt = get_segment_type (p->p_type); |
| char buf[20]; |
| |
| if (pt == NULL) |
| { |
| sprintf (buf, "0x%lx", p->p_type); |
| pt = buf; |
| } |
| fprintf (f, "%8s off 0x", pt); |
| bfd_fprintf_vma (abfd, f, p->p_offset); |
| fprintf (f, " vaddr 0x"); |
| bfd_fprintf_vma (abfd, f, p->p_vaddr); |
| fprintf (f, " paddr 0x"); |
| bfd_fprintf_vma (abfd, f, p->p_paddr); |
| fprintf (f, " align 2**%u\n", bfd_log2 (p->p_align)); |
| fprintf (f, " filesz 0x"); |
| bfd_fprintf_vma (abfd, f, p->p_filesz); |
| fprintf (f, " memsz 0x"); |
| bfd_fprintf_vma (abfd, f, p->p_memsz); |
| fprintf (f, " flags %c%c%c", |
| (p->p_flags & PF_R) != 0 ? 'r' : '-', |
| (p->p_flags & PF_W) != 0 ? 'w' : '-', |
| (p->p_flags & PF_X) != 0 ? 'x' : '-'); |
| if ((p->p_flags &~ (unsigned) (PF_R | PF_W | PF_X)) != 0) |
| fprintf (f, " %lx", p->p_flags &~ (unsigned) (PF_R | PF_W | PF_X)); |
| fprintf (f, "\n"); |
| } |
| } |
| |
| s = bfd_get_section_by_name (abfd, ".dynamic"); |
| if (s != NULL && (s->flags & SEC_HAS_CONTENTS) != 0) |
| { |
| unsigned int elfsec; |
| unsigned long shlink; |
| bfd_byte *extdyn, *extdynend; |
| size_t extdynsize; |
| void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *); |
| |
| fprintf (f, _("\nDynamic Section:\n")); |
| |
| if (!_bfd_elf_mmap_section_contents (abfd, s, &dynbuf)) |
| goto error_return; |
| |
| elfsec = _bfd_elf_section_from_bfd_section (abfd, s); |
| if (elfsec == SHN_BAD) |
| goto error_return; |
| shlink = elf_elfsections (abfd)[elfsec]->sh_link; |
| |
| extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn; |
| swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in; |
| |
| for (extdyn = dynbuf, extdynend = dynbuf + s->size; |
| (size_t) (extdynend - extdyn) >= extdynsize; |
| extdyn += extdynsize) |
| { |
| Elf_Internal_Dyn dyn; |
| const char *name = ""; |
| char ab[20]; |
| bool stringp; |
| const struct elf_backend_data *bed = get_elf_backend_data (abfd); |
| |
| (*swap_dyn_in) (abfd, extdyn, &dyn); |
| |
| if (dyn.d_tag == DT_NULL) |
| break; |
| |
| stringp = false; |
| switch (dyn.d_tag) |
| { |
| default: |
| if (bed->elf_backend_get_target_dtag) |
| name = (*bed->elf_backend_get_target_dtag) (dyn.d_tag); |
| |
| if (!strcmp (name, "")) |
| { |
| sprintf (ab, "%#" PRIx64, (uint64_t) dyn.d_tag); |
| name = ab; |
| } |
| break; |
| |
| case DT_NEEDED: name = "NEEDED"; stringp = true; break; |
| case DT_PLTRELSZ: name = "PLTRELSZ"; break; |
| case DT_PLTGOT: name = "PLTGOT"; break; |
| case DT_HASH: name = "HASH"; break; |
| case DT_STRTAB: name = "STRTAB"; break; |
| case DT_SYMTAB: name = "SYMTAB"; break; |
| case DT_RELA: name = "RELA"; break; |
| case DT_RELASZ: name = "RELASZ"; break; |
| case DT_RELAENT: name = "RELAENT"; break; |
| case DT_STRSZ: name = "STRSZ"; break; |
| case DT_SYMENT: name = "SYMENT"; break; |
| case DT_INIT: name = "INIT"; break; |
| case DT_FINI: name = "FINI"; break; |
| case DT_SONAME: name = "SONAME"; stringp = true; break; |
| case DT_RPATH: name = "RPATH"; stringp = true; break; |
| case DT_SYMBOLIC: name = "SYMBOLIC"; break; |
| case DT_REL: name = "REL"; break; |
| case DT_RELSZ: name = "RELSZ"; break; |
| case DT_RELENT: name = "RELENT"; break; |
| case DT_RELR: name = "RELR"; break; |
| case DT_RELRSZ: name = "RELRSZ"; break; |
| case DT_RELRENT: name = "RELRENT"; break; |
| case DT_PLTREL: name = "PLTREL"; break; |
| case DT_DEBUG: name = "DEBUG"; break; |
| case DT_TEXTREL: name = "TEXTREL"; break; |
| case DT_JMPREL: name = "JMPREL"; break; |
| case DT_BIND_NOW: name = "BIND_NOW"; break; |
| case DT_INIT_ARRAY: name = "INIT_ARRAY"; break; |
| case DT_FINI_ARRAY: name = "FINI_ARRAY"; break; |
| case DT_INIT_ARRAYSZ: name = "INIT_ARRAYSZ"; break; |
| case DT_FINI_ARRAYSZ: name = "FINI_ARRAYSZ"; break; |
| case DT_RUNPATH: name = "RUNPATH"; stringp = true; break; |
| case DT_FLAGS: name = "FLAGS"; break; |
| case DT_PREINIT_ARRAY: name = "PREINIT_ARRAY"; break; |
| case DT_PREINIT_ARRAYSZ: name = "PREINIT_ARRAYSZ"; break; |
| case DT_CHECKSUM: name = "CHECKSUM"; break; |
| case DT_PLTPADSZ: name = "PLTPADSZ"; break; |
| case DT_MOVEENT: name = "MOVEENT"; break; |
| case DT_MOVESZ: name = "MOVESZ"; break; |
| case DT_FEATURE: name = "FEATURE"; break; |
| case DT_POSFLAG_1: name = "POSFLAG_1"; break; |
| case DT_SYMINSZ: name = "SYMINSZ"; break; |
| case DT_SYMINENT: name = "SYMINENT"; break; |
| case DT_CONFIG: name = "CONFIG"; stringp = true; break; |
| case DT_DEPAUDIT: name = "DEPAUDIT"; stringp = true; break; |
| case DT_AUDIT: name = "AUDIT"; stringp = true; break; |
| case DT_PLTPAD: name = "PLTPAD"; break; |
| case DT_MOVETAB: name = "MOVETAB"; break; |
| case DT_SYMINFO: name = "SYMINFO"; break; |
| case DT_RELACOUNT: name = "RELACOUNT"; break; |
| case DT_RELCOUNT: name = "RELCOUNT"; break; |
| case DT_FLAGS_1: name = "FLAGS_1"; break; |
| case DT_VERSYM: name = "VERSYM"; break; |
| case DT_VERDEF: name = "VERDEF"; break; |
| case DT_VERDEFNUM: name = "VERDEFNUM"; break; |
| case DT_VERNEED: name = "VERNEED"; break; |
| case DT_VERNEEDNUM: name = "VERNEEDNUM"; break; |
| case DT_AUXILIARY: name = "AUXILIARY"; stringp = true; break; |
| case DT_USED: name = "USED"; break; |
| case DT_FILTER: name = "FILTER"; stringp = true; break; |
| case DT_GNU_HASH: name = "GNU_HASH"; break; |
| } |
| |
| fprintf (f, " %-20s ", name); |
| if (! stringp) |
| { |
| fprintf (f, "0x"); |
| bfd_fprintf_vma (abfd, f, dyn.d_un.d_val); |
| } |
| else |
| { |
| const char *string; |
| unsigned int tagv = dyn.d_un.d_val; |
| |
| string = bfd_elf_string_from_elf_section (abfd, shlink, tagv); |
| if (string == NULL) |
| goto error_return; |
| fprintf (f, "%s", string); |
| } |
| fprintf (f, "\n"); |
| } |
| |
| _bfd_elf_munmap_section_contents (s, dynbuf); |
| dynbuf = NULL; |
| } |
| |
| if ((elf_dynverdef (abfd) != 0 && elf_tdata (abfd)->verdef == NULL) |
| || (elf_dynverref (abfd) != 0 && elf_tdata (abfd)->verref == NULL)) |
| { |
| if (! _bfd_elf_slurp_version_tables (abfd, false)) |
| return false; |
| } |
| |
| if (elf_dynverdef (abfd) != 0) |
| { |
| Elf_Internal_Verdef *t; |
| |
| fprintf (f, _("\nVersion definitions:\n")); |
| for (t = elf_tdata (abfd)->verdef; t != NULL; t = t->vd_nextdef) |
| { |
| fprintf (f, "%d 0x%2.2x 0x%8.8lx %s\n", t->vd_ndx, |
| t->vd_flags, t->vd_hash, |
| t->vd_nodename ? t->vd_nodename : "<corrupt>"); |
| if (t->vd_auxptr != NULL && t->vd_auxptr->vda_nextptr != NULL) |
| { |
| Elf_Internal_Verdaux *a; |
| |
| fprintf (f, "\t"); |
| for (a = t->vd_auxptr->vda_nextptr; |
| a != NULL; |
| a = a->vda_nextptr) |
| fprintf (f, "%s ", |
| a->vda_nodename ? a->vda_nodename : "<corrupt>"); |
| fprintf (f, "\n"); |
| } |
| } |
| } |
| |
| if (elf_dynverref (abfd) != 0) |
| { |
| Elf_Internal_Verneed *t; |
| |
| fprintf (f, _("\nVersion References:\n")); |
| for (t = elf_tdata (abfd)->verref; t != NULL; t = t->vn_nextref) |
| { |
| Elf_Internal_Vernaux *a; |
| |
| fprintf (f, _(" required from %s:\n"), |
| t->vn_filename ? t->vn_filename : "<corrupt>"); |
| for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) |
| fprintf (f, " 0x%8.8lx 0x%2.2x %2.2d %s\n", a->vna_hash, |
| a->vna_flags, a->vna_other, |
| a->vna_nodename ? a->vna_nodename : "<corrupt>"); |
| } |
| } |
| |
| return true; |
| |
| error_return: |
| _bfd_elf_munmap_section_contents (s, dynbuf); |
| return false; |
| } |
| |
| /* Find the file offset corresponding to VMA by using the program |
| headers. */ |
| |
| static file_ptr |
| offset_from_vma (Elf_Internal_Phdr *phdrs, size_t phnum, bfd_vma vma, |
| size_t size, size_t *max_size_p) |
| { |
| Elf_Internal_Phdr *seg; |
| size_t i; |
| |
| for (seg = phdrs, i = 0; i < phnum; ++seg, ++i) |
| if (seg->p_type == PT_LOAD |
| && vma >= (seg->p_vaddr & -seg->p_align) |
| && vma + size <= seg->p_vaddr + seg->p_filesz) |
| { |
| if (max_size_p) |
| *max_size_p = seg->p_vaddr + seg->p_filesz - vma; |
| return vma - seg->p_vaddr + seg->p_offset; |
| } |
| |
| if (max_size_p) |
| *max_size_p = 0; |
| bfd_set_error (bfd_error_invalid_operation); |
| return (file_ptr) -1; |
| } |
| |
| /* Convert hash table to internal form. */ |
| |
| static bfd_vma * |
| get_hash_table_data (bfd *abfd, bfd_size_type number, |
| unsigned int ent_size, bfd_size_type filesize) |
| { |
| unsigned char *e_data = NULL; |
| bfd_vma *i_data = NULL; |
| bfd_size_type size; |
| void *e_data_addr; |
| size_t e_data_size ATTRIBUTE_UNUSED; |
| |
| if (ent_size != 4 && ent_size != 8) |
| return NULL; |
| |
| if ((size_t) number != number) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| return NULL; |
| } |
| |
| size = ent_size * number; |
| /* Be kind to memory checkers (eg valgrind, address sanitizer) by not |
| attempting to allocate memory when the read is bound to fail. */ |
| if (size > filesize |
| || number >= ~(size_t) 0 / ent_size |
| || number >= ~(size_t) 0 / sizeof (*i_data)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| return NULL; |
| } |
| |
| e_data = _bfd_mmap_readonly_temporary (abfd, size, &e_data_addr, |
| &e_data_size); |
| if (e_data == NULL) |
| return NULL; |
| |
| i_data = (bfd_vma *) bfd_malloc (number * sizeof (*i_data)); |
| if (i_data == NULL) |
| { |
| free (e_data); |
| return NULL; |
| } |
| |
| if (ent_size == 4) |
| while (number--) |
| i_data[number] = bfd_get_32 (abfd, e_data + number * ent_size); |
| else |
| while (number--) |
| i_data[number] = bfd_get_64 (abfd, e_data + number * ent_size); |
| |
| _bfd_munmap_readonly_temporary (e_data_addr, e_data_size); |
| return i_data; |
| } |
| |
| /* Address of .MIPS.xhash section. FIXME: What is the best way to |
| support DT_MIPS_XHASH? */ |
| #define DT_MIPS_XHASH 0x70000036 |
| |
| /* Reconstruct dynamic symbol table from PT_DYNAMIC segment. */ |
| |
| bool |
| _bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr, |
| Elf_Internal_Phdr *phdrs, size_t phnum, |
| bfd_size_type filesize) |
| { |
| bfd_byte *extdyn, *extdynend; |
| size_t extdynsize; |
| void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *); |
| bool (*swap_symbol_in) (bfd *, const void *, const void *, |
| Elf_Internal_Sym *); |
| Elf_Internal_Dyn dyn; |
| bfd_vma dt_hash = 0; |
| bfd_vma dt_gnu_hash = 0; |
| bfd_vma dt_mips_xhash = 0; |
| bfd_vma dt_strtab = 0; |
| bfd_vma dt_symtab = 0; |
| size_t dt_strsz = 0; |
| bfd_vma dt_versym = 0; |
| bfd_vma dt_verdef = 0; |
| bfd_vma dt_verneed = 0; |
| bfd_byte *dynbuf = NULL; |
| char *strbuf = NULL; |
| bfd_vma *gnubuckets = NULL; |
| bfd_vma *gnuchains = NULL; |
| bfd_vma *mipsxlat = NULL; |
| file_ptr saved_filepos, filepos; |
| bool res = false; |
| size_t amt; |
| bfd_byte *esymbuf = NULL, *esym; |
| bfd_size_type symcount; |
| Elf_Internal_Sym *isymbuf = NULL; |
| Elf_Internal_Sym *isym, *isymend; |
| bfd_byte *versym = NULL; |
| bfd_byte *verdef = NULL; |
| bfd_byte *verneed = NULL; |
| size_t verdef_size = 0; |
| size_t verneed_size = 0; |
| size_t extsym_size; |
| const struct elf_backend_data *bed; |
| void *dynbuf_addr = NULL; |
| void *esymbuf_addr = NULL; |
| size_t dynbuf_size = 0; |
| size_t esymbuf_size = 0; |
| |
| /* Return TRUE if symbol table is bad. */ |
| if (elf_bad_symtab (abfd)) |
| return true; |
| |
| /* Return TRUE if DT_HASH/DT_GNU_HASH have bee processed before. */ |
| if (elf_tdata (abfd)->dt_strtab != NULL) |
| return true; |
| |
| bed = get_elf_backend_data (abfd); |
| |
| /* Save file position for elf_object_p. */ |
| saved_filepos = bfd_tell (abfd); |
| |
| if (bfd_seek (abfd, phdr->p_offset, SEEK_SET) != 0) |
| goto error_return; |
| |
| dynbuf_size = phdr->p_filesz; |
| dynbuf = _bfd_mmap_readonly_temporary (abfd, dynbuf_size, |
| &dynbuf_addr, &dynbuf_size); |
| if (dynbuf == NULL) |
| goto error_return; |
| |
| extsym_size = bed->s->sizeof_sym; |
| extdynsize = bed->s->sizeof_dyn; |
| swap_dyn_in = bed->s->swap_dyn_in; |
| |
| extdyn = dynbuf; |
| if (phdr->p_filesz < extdynsize) |
| goto error_return; |
| extdynend = extdyn + phdr->p_filesz; |
| for (; extdyn <= (extdynend - extdynsize); extdyn += extdynsize) |
| { |
| swap_dyn_in (abfd, extdyn, &dyn); |
| |
| if (dyn.d_tag == DT_NULL) |
| break; |
| |
| switch (dyn.d_tag) |
| { |
| case DT_HASH: |
| dt_hash = dyn.d_un.d_val; |
| break; |
| case DT_GNU_HASH: |
| if (bed->elf_machine_code != EM_MIPS |
| && bed->elf_machine_code != EM_MIPS_RS3_LE) |
| dt_gnu_hash = dyn.d_un.d_val; |
| break; |
| case DT_STRTAB: |
| dt_strtab = dyn.d_un.d_val; |
| break; |
| case DT_SYMTAB: |
| dt_symtab = dyn.d_un.d_val; |
| break; |
| case DT_STRSZ: |
| dt_strsz = dyn.d_un.d_val; |
| break; |
| case DT_SYMENT: |
| if (dyn.d_un.d_val != extsym_size) |
| goto error_return; |
| break; |
| case DT_VERSYM: |
| dt_versym = dyn.d_un.d_val; |
| break; |
| case DT_VERDEF: |
| dt_verdef = dyn.d_un.d_val; |
| break; |
| case DT_VERNEED: |
| dt_verneed = dyn.d_un.d_val; |
| break; |
| default: |
| if (dyn.d_tag == DT_MIPS_XHASH |
| && (bed->elf_machine_code == EM_MIPS |
| || bed->elf_machine_code == EM_MIPS_RS3_LE)) |
| { |
| dt_gnu_hash = dyn.d_un.d_val; |
| dt_mips_xhash = dyn.d_un.d_val; |
| } |
| break; |
| } |
| } |
| |
| /* Check if we can reconstruct dynamic symbol table from PT_DYNAMIC |
| segment. */ |
| if ((!dt_hash && !dt_gnu_hash) |
| || !dt_strtab |
| || !dt_symtab |
| || !dt_strsz) |
| goto error_return; |
| |
| /* Get dynamic string table. */ |
| filepos = offset_from_vma (phdrs, phnum, dt_strtab, dt_strsz, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| /* Dynamic string table must be valid until ABFD is closed. */ |
| strbuf = (char *) _bfd_mmap_readonly_persistent (abfd, dt_strsz); |
| if (strbuf == NULL) |
| goto error_return; |
| if (strbuf[dt_strsz - 1] != 0) |
| { |
| /* It is an error if a string table is't terminated. */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: DT_STRTAB table is corrupt"), abfd); |
| strbuf[dt_strsz - 1] = 0; |
| } |
| |
| /* Get the real symbol count from DT_HASH or DT_GNU_HASH. Prefer |
| DT_HASH since it is simpler than DT_GNU_HASH. */ |
| if (dt_hash) |
| { |
| unsigned char nb[16]; |
| unsigned int hash_ent_size; |
| |
| switch (bed->elf_machine_code) |
| { |
| case EM_ALPHA: |
| case EM_S390: |
| case EM_S390_OLD: |
| if (bed->s->elfclass == ELFCLASS64) |
| { |
| hash_ent_size = 8; |
| break; |
| } |
| /* FALLTHROUGH */ |
| default: |
| hash_ent_size = 4; |
| break; |
| } |
| |
| filepos = offset_from_vma (phdrs, phnum, dt_hash, sizeof (nb), |
| NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0 |
| || bfd_read (nb, 2 * hash_ent_size, abfd) != 2 * hash_ent_size) |
| goto error_return; |
| |
| /* The number of dynamic symbol table entries equals the number |
| of chains. */ |
| if (hash_ent_size == 8) |
| symcount = bfd_get_64 (abfd, nb + hash_ent_size); |
| else |
| symcount = bfd_get_32 (abfd, nb + hash_ent_size); |
| } |
| else |
| { |
| /* For DT_GNU_HASH, only defined symbols with non-STB_LOCAL |
| bindings are in hash table. Since in dynamic symbol table, |
| all symbols with STB_LOCAL binding are placed before symbols |
| with other bindings and all undefined symbols are placed |
| before defined ones, the highest symbol index in DT_GNU_HASH |
| is the highest dynamic symbol table index. */ |
| unsigned char nb[16]; |
| bfd_vma ngnubuckets; |
| bfd_vma gnusymidx; |
| size_t i, ngnuchains; |
| bfd_vma maxchain = 0xffffffff, bitmaskwords; |
| bfd_vma buckets_vma; |
| |
| filepos = offset_from_vma (phdrs, phnum, dt_gnu_hash, |
| sizeof (nb), NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0 |
| || bfd_read (nb, sizeof (nb), abfd) != sizeof (nb)) |
| goto error_return; |
| |
| ngnubuckets = bfd_get_32 (abfd, nb); |
| gnusymidx = bfd_get_32 (abfd, nb + 4); |
| bitmaskwords = bfd_get_32 (abfd, nb + 8); |
| buckets_vma = dt_gnu_hash + 16; |
| if (bed->s->elfclass == ELFCLASS32) |
| buckets_vma += bitmaskwords * 4; |
| else |
| buckets_vma += bitmaskwords * 8; |
| filepos = offset_from_vma (phdrs, phnum, buckets_vma, 4, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| gnubuckets = get_hash_table_data (abfd, ngnubuckets, 4, filesize); |
| if (gnubuckets == NULL) |
| goto error_return; |
| |
| for (i = 0; i < ngnubuckets; i++) |
| if (gnubuckets[i] != 0) |
| { |
| if (gnubuckets[i] < gnusymidx) |
| goto error_return; |
| |
| if (maxchain == 0xffffffff || gnubuckets[i] > maxchain) |
| maxchain = gnubuckets[i]; |
| } |
| |
| if (maxchain == 0xffffffff) |
| { |
| symcount = 0; |
| goto empty_gnu_hash; |
| } |
| |
| maxchain -= gnusymidx; |
| filepos = offset_from_vma (phdrs, phnum, |
| (buckets_vma + |
| 4 * (ngnubuckets + maxchain)), |
| 4, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| do |
| { |
| if (bfd_read (nb, 4, abfd) != 4) |
| goto error_return; |
| ++maxchain; |
| if (maxchain == 0) |
| goto error_return; |
| } |
| while ((bfd_get_32 (abfd, nb) & 1) == 0); |
| |
| filepos = offset_from_vma (phdrs, phnum, |
| (buckets_vma + 4 * ngnubuckets), |
| 4, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| gnuchains = get_hash_table_data (abfd, maxchain, 4, filesize); |
| if (gnuchains == NULL) |
| goto error_return; |
| ngnuchains = maxchain; |
| |
| if (dt_mips_xhash) |
| { |
| filepos = offset_from_vma (phdrs, phnum, |
| (buckets_vma |
| + 4 * (ngnubuckets + maxchain)), |
| 4, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| mipsxlat = get_hash_table_data (abfd, maxchain, 4, filesize); |
| if (mipsxlat == NULL) |
| goto error_return; |
| } |
| |
| symcount = 0; |
| for (i = 0; i < ngnubuckets; ++i) |
| if (gnubuckets[i] != 0) |
| { |
| bfd_vma si = gnubuckets[i]; |
| bfd_vma off = si - gnusymidx; |
| do |
| { |
| if (mipsxlat) |
| { |
| if (mipsxlat[off] >= symcount) |
| symcount = mipsxlat[off] + 1; |
| } |
| else |
| { |
| if (si >= symcount) |
| symcount = si + 1; |
| } |
| si++; |
| } |
| while (off < ngnuchains && (gnuchains[off++] & 1) == 0); |
| } |
| } |
| |
| /* Swap in dynamic symbol table. */ |
| if (_bfd_mul_overflow (symcount, extsym_size, &amt)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| goto error_return; |
| } |
| |
| filepos = offset_from_vma (phdrs, phnum, dt_symtab, amt, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| esymbuf_size = amt; |
| esymbuf = _bfd_mmap_readonly_temporary (abfd, esymbuf_size, |
| &esymbuf_addr, |
| &esymbuf_size); |
| if (esymbuf == NULL) |
| goto error_return; |
| |
| if (_bfd_mul_overflow (symcount, sizeof (Elf_Internal_Sym), &amt)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| goto error_return; |
| } |
| |
| /* Dynamic symbol table must be valid until ABFD is closed. */ |
| isymbuf = (Elf_Internal_Sym *) bfd_alloc (abfd, amt); |
| if (isymbuf == NULL) |
| goto error_return; |
| |
| swap_symbol_in = bed->s->swap_symbol_in; |
| |
| /* Convert the symbols to internal form. */ |
| isymend = isymbuf + symcount; |
| for (esym = esymbuf, isym = isymbuf; |
| isym < isymend; |
| esym += extsym_size, isym++) |
| if (!swap_symbol_in (abfd, esym, NULL, isym) |
| || isym->st_name >= dt_strsz) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| goto error_return; |
| } |
| |
| if (dt_versym) |
| { |
| /* Swap in DT_VERSYM. */ |
| if (_bfd_mul_overflow (symcount, 2, &amt)) |
| { |
| bfd_set_error (bfd_error_file_too_big); |
| goto error_return; |
| } |
| |
| filepos = offset_from_vma (phdrs, phnum, dt_versym, amt, NULL); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| /* DT_VERSYM info must be valid until ABFD is closed. */ |
| versym = _bfd_mmap_readonly_persistent (abfd, amt); |
| |
| if (dt_verdef) |
| { |
| /* Read in DT_VERDEF. */ |
| filepos = offset_from_vma (phdrs, phnum, dt_verdef, |
| 0, &verdef_size); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| /* DT_VERDEF info must be valid until ABFD is closed. */ |
| verdef = _bfd_mmap_readonly_persistent (abfd, verdef_size); |
| } |
| |
| if (dt_verneed) |
| { |
| /* Read in DT_VERNEED. */ |
| filepos = offset_from_vma (phdrs, phnum, dt_verneed, |
| 0, &verneed_size); |
| if (filepos == (file_ptr) -1 |
| || bfd_seek (abfd, filepos, SEEK_SET) != 0) |
| goto error_return; |
| |
| /* DT_VERNEED info must be valid until ABFD is closed. */ |
| verneed = _bfd_mmap_readonly_persistent (abfd, verneed_size); |
| } |
| } |
| |
| empty_gnu_hash: |
| elf_tdata (abfd)->dt_strtab = strbuf; |
| elf_tdata (abfd)->dt_strsz = dt_strsz; |
| elf_tdata (abfd)->dt_symtab = isymbuf; |
| elf_tdata (abfd)->dt_symtab_count = symcount; |
| elf_tdata (abfd)->dt_versym = versym; |
| elf_tdata (abfd)->dt_verdef = verdef; |
| elf_tdata (abfd)->dt_verneed = verneed; |
| elf_tdata (abfd)->dt_verdef_count |
| = verdef_size / sizeof (Elf_External_Verdef); |
| elf_tdata (abfd)->dt_verneed_count |
| = verneed_size / sizeof (Elf_External_Verneed); |
| |
| res = true; |
| |
| error_return: |
| /* Restore file position for elf_object_p. */ |
| if (bfd_seek (abfd, saved_filepos, SEEK_SET) != 0) |
| res = false; |
| _bfd_munmap_readonly_temporary (dynbuf_addr, dynbuf_size); |
| _bfd_munmap_readonly_temporary (esymbuf_addr, esymbuf_size); |
| free (gnubuckets); |
| free (gnuchains); |
| free (mipsxlat); |
| return res; |
| } |
| |
| /* Reconstruct section from dynamic symbol. */ |
| |
| asection * |
| _bfd_elf_get_section_from_dynamic_symbol (bfd *abfd, |
| Elf_Internal_Sym *isym) |
| { |
| asection *sec; |
| flagword flags; |
| |
| if (!elf_use_dt_symtab_p (abfd)) |
| return NULL; |
| |
| flags = SEC_ALLOC | SEC_LOAD; |
| switch (ELF_ST_TYPE (isym->st_info)) |
| { |
| case STT_FUNC: |
| case STT_GNU_IFUNC: |
| sec = bfd_get_section_by_name (abfd, ".text"); |
| if (sec == NULL) |
| sec = bfd_make_section_with_flags (abfd, |
| ".text", |
| flags | SEC_CODE); |
| break; |
| case STT_COMMON: |
| sec = bfd_com_section_ptr; |
| break; |
| case STT_OBJECT: |
| sec = bfd_get_section_by_name (abfd, ".data"); |
| if (sec == NULL) |
| sec = bfd_make_section_with_flags (abfd, |
| ".data", |
| flags | SEC_DATA); |
| break; |
| case STT_TLS: |
| sec = bfd_get_section_by_name (abfd, ".tdata"); |
| if (sec == NULL) |
| sec = bfd_make_section_with_flags (abfd, |
| ".tdata", |
| (flags |
| | SEC_DATA |
| | SEC_THREAD_LOCAL)); |
| break; |
| default: |
| sec = bfd_abs_section_ptr; |
| break; |
| } |
| |
| return sec; |
| } |
| |
| /* Get version name. If BASE_P is TRUE, return "Base" for VER_FLG_BASE |
| and return symbol version for symbol version itself. */ |
| |
| const char * |
| _bfd_elf_get_symbol_version_string (bfd *abfd, asymbol *symbol, |
| bool base_p, |
| bool *hidden) |
| { |
| const char *version_string = NULL; |
| if ((elf_dynversym (abfd) != 0 |
| && (elf_dynverdef (abfd) != 0 || elf_dynverref (abfd) != 0)) |
| || (elf_tdata (abfd)->dt_versym != NULL |
| && (elf_tdata (abfd)->dt_verdef != NULL |
| || elf_tdata (abfd)->dt_verneed != NULL))) |
| { |
| unsigned int vernum = ((elf_symbol_type *) symbol)->version; |
| |
| *hidden = (vernum & VERSYM_HIDDEN) != 0; |
| vernum &= VERSYM_VERSION; |
| |
| if (vernum == 0) |
| version_string = ""; |
| else if (vernum == 1 |
| && (vernum > elf_tdata (abfd)->cverdefs |
| || (elf_tdata (abfd)->verdef[0].vd_flags |
| == VER_FLG_BASE))) |
| version_string = base_p ? "Base" : ""; |
| else if (vernum <= elf_tdata (abfd)->cverdefs) |
| { |
| const char *nodename |
| = elf_tdata (abfd)->verdef[vernum - 1].vd_nodename; |
| version_string = ""; |
| if (base_p |
| || nodename == NULL |
| || symbol->name == NULL |
| || strcmp (symbol->name, nodename) != 0) |
| version_string = nodename; |
| } |
| else |
| { |
| Elf_Internal_Verneed *t; |
| |
| version_string = _("<corrupt>"); |
| for (t = elf_tdata (abfd)->verref; |
| t != NULL; |
| t = t->vn_nextref) |
| { |
| Elf_Internal_Vernaux *a; |
| |
| for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) |
| { |
| if (a->vna_other == vernum) |
| { |
| *hidden = true; |
| version_string = a->vna_nodename; |
| break; |
| } |
| } |
| } |
| } |
| } |
| return version_string; |
| } |
| |
| /* Display ELF-specific fields of a symbol. */ |
| |
| void |
| bfd_elf_print_symbol (bfd *abfd, |
| void *filep, |
| asymbol *symbol, |
| bfd_print_symbol_type how) |
| { |
| FILE *file = (FILE *) filep; |
| switch (how) |
| { |
| case bfd_print_symbol_name: |
| fprintf (file, "%s", symbol->name); |
| break; |
| case bfd_print_symbol_more: |
| fprintf (file, "elf "); |
| bfd_fprintf_vma (abfd, file, symbol->value); |
| fprintf (file, " %x", symbol->flags); |
| break; |
| case bfd_print_symbol_all: |
| { |
| const char *section_name; |
| const char *name = NULL; |
| const struct elf_backend_data *bed; |
| unsigned char st_other; |
| bfd_vma val; |
| const char *version_string; |
| bool hidden; |
| |
| section_name = symbol->section ? symbol->section->name : "(*none*)"; |
| |
| bed = get_elf_backend_data (abfd); |
| if (bed->elf_backend_print_symbol_all) |
| name = (*bed->elf_backend_print_symbol_all) (abfd, filep, symbol); |
| |
| if (name == NULL) |
| { |
| name = symbol->name; |
| bfd_print_symbol_vandf (abfd, file, symbol); |
| } |
| |
| fprintf (file, " %s\t", section_name); |
| /* Print the "other" value for a symbol. For common symbols, |
| we've already printed the size; now print the alignment. |
| For other symbols, we have no specified alignment, and |
| we've printed the address; now print the size. */ |
| if (symbol->section && bfd_is_com_section (symbol->section)) |
| val = ((elf_symbol_type *) symbol)->internal_elf_sym.st_value; |
| else |
| val = ((elf_symbol_type *) symbol)->internal_elf_sym.st_size; |
| bfd_fprintf_vma (abfd, file, val); |
| |
| /* If we have version information, print it. */ |
| version_string = _bfd_elf_get_symbol_version_string (abfd, |
| symbol, |
| true, |
| &hidden); |
| if (version_string) |
| { |
| if (!hidden) |
| fprintf (file, " %-11s", version_string); |
| else |
| { |
| int i; |
| |
| fprintf (file, " (%s)", version_string); |
| for (i = 10 - strlen (version_string); i > 0; --i) |
| putc (' ', file); |
| } |
| } |
| |
| /* If the st_other field is not zero, print it. */ |
| st_other = ((elf_symbol_type *) symbol)->internal_elf_sym.st_other; |
| |
| switch (st_other) |
| { |
| case 0: break; |
| case STV_INTERNAL: fprintf (file, " .internal"); break; |
| case STV_HIDDEN: fprintf (file, " .hidden"); break; |
| case STV_PROTECTED: fprintf (file, " .protected"); break; |
| default: |
| /* Some other non-defined flags are also present, so print |
| everything hex. */ |
| fprintf (file, " 0x%02x", (unsigned int) st_other); |
| } |
| |
| fprintf (file, " %s", name); |
| } |
| break; |
| } |
| } |
| |
| /* ELF .o/exec file reading */ |
| |
| /* Create a new bfd section from an ELF section header. */ |
| |
| bool |
| bfd_section_from_shdr (bfd *abfd, unsigned int shindex) |
| { |
| Elf_Internal_Shdr *hdr; |
| Elf_Internal_Ehdr *ehdr; |
| const struct elf_backend_data *bed; |
| const char *name; |
| bool ret = true; |
| |
| if (shindex >= elf_numsections (abfd)) |
| return false; |
| |
| /* PR17512: A corrupt ELF binary might contain a loop of sections via |
| sh_link or sh_info. Detect this here, by refusing to load a |
| section that we are already in the process of loading. */ |
| if (elf_tdata (abfd)->being_created[shindex]) |
| { |
| _bfd_error_handler |
| (_("%pB: warning: loop in section dependencies detected"), abfd); |
| return false; |
| } |
| elf_tdata (abfd)->being_created[shindex] = true; |
| |
| hdr = elf_elfsections (abfd)[shindex]; |
| ehdr = elf_elfheader (abfd); |
| name = bfd_elf_string_from_elf_section (abfd, ehdr->e_shstrndx, |
| hdr->sh_name); |
| if (name == NULL) |
| goto fail; |
| |
| bed = get_elf_backend_data (abfd); |
| switch (hdr->sh_type) |
| { |
| case SHT_NULL: |
| /* Inactive section. Throw it away. */ |
| goto success; |
| |
| case SHT_PROGBITS: /* Normal section with contents. */ |
| case SHT_NOBITS: /* .bss section. */ |
| case SHT_HASH: /* .hash section. */ |
| case SHT_NOTE: /* .note section. */ |
| case SHT_INIT_ARRAY: /* .init_array section. */ |
| case SHT_FINI_ARRAY: /* .fini_array section. */ |
| case SHT_PREINIT_ARRAY: /* .preinit_array section. */ |
| case SHT_GNU_LIBLIST: /* .gnu.liblist section. */ |
| case SHT_GNU_HASH: /* .gnu.hash section. */ |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| |
| case SHT_DYNAMIC: /* Dynamic linking information. */ |
| if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) |
| goto fail; |
| |
| if (hdr->sh_link > elf_numsections (abfd)) |
| { |
| /* PR 10478: Accept Solaris binaries with a sh_link field |
| set to SHN_BEFORE (LORESERVE) or SHN_AFTER (LORESERVE+1). */ |
| switch (bfd_get_arch (abfd)) |
| { |
| case bfd_arch_i386: |
| case bfd_arch_sparc: |
| if (hdr->sh_link == (SHN_LORESERVE & 0xffff) |
| || hdr->sh_link == ((SHN_LORESERVE + 1) & 0xffff)) |
| break; |
| /* Otherwise fall through. */ |
| default: |
| goto fail; |
| } |
| } |
| else if (elf_elfsections (abfd)[hdr->sh_link] == NULL) |
| goto fail; |
| else if (elf_elfsections (abfd)[hdr->sh_link]->sh_type != SHT_STRTAB) |
| { |
| Elf_Internal_Shdr *dynsymhdr; |
| |
| /* The shared libraries distributed with hpux11 have a bogus |
| sh_link field for the ".dynamic" section. Find the |
| string table for the ".dynsym" section instead. */ |
| if (elf_dynsymtab (abfd) != 0) |
| { |
| dynsymhdr = elf_elfsections (abfd)[elf_dynsymtab (abfd)]; |
| hdr->sh_link = dynsymhdr->sh_link; |
| } |
| else |
| { |
| unsigned int i, num_sec; |
| |
| num_sec = elf_numsections (abfd); |
| for (i = 1; i < num_sec; i++) |
| { |
| dynsymhdr = elf_elfsections (abfd)[i]; |
| if (dynsymhdr->sh_type == SHT_DYNSYM) |
| { |
| hdr->sh_link = dynsymhdr->sh_link; |
| break; |
| } |
| } |
| } |
| } |
| goto success; |
| |
| case SHT_SYMTAB: /* A symbol table. */ |
| if (elf_onesymtab (abfd) == shindex) |
| goto success; |
| |
| if (hdr->sh_entsize != bed->s->sizeof_sym) |
| goto fail; |
| |
| if (hdr->sh_info * hdr->sh_entsize > hdr->sh_size) |
| { |
| if (hdr->sh_size != 0) |
| goto fail; |
| /* Some assemblers erroneously set sh_info to one with a |
| zero sh_size. ld sees this as a global symbol count |
| of (unsigned) -1. Fix it here. */ |
| hdr->sh_info = 0; |
| goto success; |
| } |
| |
| /* PR 18854: A binary might contain more than one symbol table. |
| Unusual, but possible. Warn, but continue. */ |
| if (elf_onesymtab (abfd) != 0) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: warning: multiple symbol tables detected" |
| " - ignoring the table in section %u"), |
| abfd, shindex); |
| goto success; |
| } |
| elf_onesymtab (abfd) = shindex; |
| elf_symtab_hdr (abfd) = *hdr; |
| elf_elfsections (abfd)[shindex] = hdr = & elf_symtab_hdr (abfd); |
| abfd->flags |= HAS_SYMS; |
| |
| /* Sometimes a shared object will map in the symbol table. If |
| SHF_ALLOC is set, and this is a shared object, then we also |
| treat this section as a BFD section. We can not base the |
| decision purely on SHF_ALLOC, because that flag is sometimes |
| set in a relocatable object file, which would confuse the |
| linker. */ |
| if ((hdr->sh_flags & SHF_ALLOC) != 0 |
| && (abfd->flags & DYNAMIC) != 0 |
| && ! _bfd_elf_make_section_from_shdr (abfd, hdr, name, |
| shindex)) |
| goto fail; |
| |
| /* Go looking for SHT_SYMTAB_SHNDX too, since if there is one we |
| can't read symbols without that section loaded as well. It |
| is most likely specified by the next section header. */ |
| { |
| elf_section_list * entry; |
| unsigned int i, num_sec; |
| |
| for (entry = elf_symtab_shndx_list (abfd); entry; entry = entry->next) |
| if (entry->hdr.sh_link == shindex) |
| goto success; |
| |
| num_sec = elf_numsections (abfd); |
| for (i = shindex + 1; i < num_sec; i++) |
| { |
| Elf_Internal_Shdr *hdr2 = elf_elfsections (abfd)[i]; |
| |
| if (hdr2->sh_type == SHT_SYMTAB_SHNDX |
| && hdr2->sh_link == shindex) |
| break; |
| } |
| |
| if (i == num_sec) |
| for (i = 1; i < shindex; i++) |
| { |
| Elf_Internal_Shdr *hdr2 = elf_elfsections (abfd)[i]; |
| |
| if (hdr2->sh_type == SHT_SYMTAB_SHNDX |
| && hdr2->sh_link == shindex) |
| break; |
| } |
| |
| if (i != shindex) |
| ret = bfd_section_from_shdr (abfd, i); |
| /* else FIXME: we have failed to find the symbol table. |
| Should we issue an error? */ |
| goto success; |
| } |
| |
| case SHT_DYNSYM: /* A dynamic symbol table. */ |
| if (elf_dynsymtab (abfd) == shindex) |
| goto success; |
| |
| if (hdr->sh_entsize != bed->s->sizeof_sym) |
| goto fail; |
| |
| if (hdr->sh_info * hdr->sh_entsize > hdr->sh_size) |
| { |
| if (hdr->sh_size != 0) |
| goto fail; |
| |
| /* Some linkers erroneously set sh_info to one with a |
| zero sh_size. ld sees this as a global symbol count |
| of (unsigned) -1. Fix it here. */ |
| hdr->sh_info = 0; |
| goto success; |
| } |
| |
| /* PR 18854: A binary might contain more than one dynamic symbol table. |
| Unusual, but possible. Warn, but continue. */ |
| if (elf_dynsymtab (abfd) != 0) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: warning: multiple dynamic symbol tables detected" |
| " - ignoring the table in section %u"), |
| abfd, shindex); |
| goto success; |
| } |
| elf_dynsymtab (abfd) = shindex; |
| elf_tdata (abfd)->dynsymtab_hdr = *hdr; |
| elf_elfsections (abfd)[shindex] = hdr = &elf_tdata (abfd)->dynsymtab_hdr; |
| abfd->flags |= HAS_SYMS; |
| |
| /* Besides being a symbol table, we also treat this as a regular |
| section, so that objcopy can handle it. */ |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| |
| case SHT_SYMTAB_SHNDX: /* Symbol section indices when >64k sections. */ |
| { |
| elf_section_list * entry; |
| |
| for (entry = elf_symtab_shndx_list (abfd); entry; entry = entry->next) |
| if (entry->ndx == shindex) |
| goto success; |
| |
| entry = bfd_alloc (abfd, sizeof (*entry)); |
| if (entry == NULL) |
| goto fail; |
| entry->ndx = shindex; |
| entry->hdr = * hdr; |
| entry->next = elf_symtab_shndx_list (abfd); |
| elf_symtab_shndx_list (abfd) = entry; |
| elf_elfsections (abfd)[shindex] = & entry->hdr; |
| goto success; |
| } |
| |
| case SHT_STRTAB: /* A string table. */ |
| if (hdr->bfd_section != NULL) |
| goto success; |
| |
| if (ehdr->e_shstrndx == shindex) |
| { |
| elf_tdata (abfd)->shstrtab_hdr = *hdr; |
| elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->shstrtab_hdr; |
| goto success; |
| } |
| |
| if (elf_elfsections (abfd)[elf_onesymtab (abfd)]->sh_link == shindex) |
| { |
| symtab_strtab: |
| elf_tdata (abfd)->strtab_hdr = *hdr; |
| elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->strtab_hdr; |
| goto success; |
| } |
| |
| if (elf_elfsections (abfd)[elf_dynsymtab (abfd)]->sh_link == shindex) |
| { |
| dynsymtab_strtab: |
| elf_tdata (abfd)->dynstrtab_hdr = *hdr; |
| hdr = &elf_tdata (abfd)->dynstrtab_hdr; |
| elf_elfsections (abfd)[shindex] = hdr; |
| /* We also treat this as a regular section, so that objcopy |
| can handle it. */ |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, |
| shindex); |
| goto success; |
| } |
| |
| /* If the string table isn't one of the above, then treat it as a |
| regular section. We need to scan all the headers to be sure, |
| just in case this strtab section appeared before the above. */ |
| if (elf_onesymtab (abfd) == 0 || elf_dynsymtab (abfd) == 0) |
| { |
| unsigned int i, num_sec; |
| |
| num_sec = elf_numsections (abfd); |
| for (i = 1; i < num_sec; i++) |
| { |
| Elf_Internal_Shdr *hdr2 = elf_elfsections (abfd)[i]; |
| if (hdr2->sh_link == shindex) |
| { |
| /* Prevent endless recursion on broken objects. */ |
| if (i == shindex) |
| goto fail; |
| if (! bfd_section_from_shdr (abfd, i)) |
| goto fail; |
| if (elf_onesymtab (abfd) == i) |
| goto symtab_strtab; |
| if (elf_dynsymtab (abfd) == i) |
| goto dynsymtab_strtab; |
| } |
| } |
| } |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| |
| case SHT_REL: |
| case SHT_RELA: |
| case SHT_RELR: |
| /* *These* do a lot of work -- but build no sections! */ |
| { |
| asection *target_sect; |
| Elf_Internal_Shdr *hdr2, **p_hdr; |
| unsigned int num_sec = elf_numsections (abfd); |
| struct bfd_elf_section_data *esdt; |
| bfd_size_type size; |
| |
| if (hdr->sh_type == SHT_REL) |
| size = bed->s->sizeof_rel; |
| else if (hdr->sh_type == SHT_RELA) |
| size = bed->s->sizeof_rela; |
| else |
| size = bed->s->arch_size / 8; |
| if (hdr->sh_entsize != size) |
| goto fail; |
| |
| /* Check for a bogus link to avoid crashing. */ |
| if (hdr->sh_link >= num_sec) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: invalid link %u for reloc section %s (index %u)"), |
| abfd, hdr->sh_link, name, shindex); |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| } |
| |
| /* Get the symbol table. */ |
| if ((elf_elfsections (abfd)[hdr->sh_link]->sh_type == SHT_SYMTAB |
| || elf_elfsections (abfd)[hdr->sh_link]->sh_type == SHT_DYNSYM) |
| && ! bfd_section_from_shdr (abfd, hdr->sh_link)) |
| goto fail; |
| |
| /* If this is an alloc section in an executable or shared |
| library, or the reloc section does not use the main symbol |
| table we don't treat it as a reloc section. BFD can't |
| adequately represent such a section, so at least for now, |
| we don't try. We just present it as a normal section. We |
| also can't use it as a reloc section if it points to the |
| null section, an invalid section, another reloc section, or |
| its sh_link points to the null section. */ |
| if (((abfd->flags & (DYNAMIC | EXEC_P)) != 0 |
| && (hdr->sh_flags & SHF_ALLOC) != 0) |
| || (hdr->sh_flags & SHF_COMPRESSED) != 0 |
| || hdr->sh_type == SHT_RELR |
| || hdr->sh_link == SHN_UNDEF |
| || hdr->sh_link != elf_onesymtab (abfd) |
| || hdr->sh_info == SHN_UNDEF |
| || hdr->sh_info >= num_sec |
| || elf_elfsections (abfd)[hdr->sh_info]->sh_type == SHT_REL |
| || elf_elfsections (abfd)[hdr->sh_info]->sh_type == SHT_RELA) |
| { |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| } |
| |
| if (! bfd_section_from_shdr (abfd, hdr->sh_info)) |
| goto fail; |
| |
| target_sect = bfd_section_from_elf_index (abfd, hdr->sh_info); |
| if (target_sect == NULL) |
| goto fail; |
| |
| esdt = elf_section_data (target_sect); |
| if (hdr->sh_type == SHT_RELA) |
| p_hdr = &esdt->rela.hdr; |
| else |
| p_hdr = &esdt->rel.hdr; |
| |
| /* PR 17512: file: 0b4f81b7. |
| Also see PR 24456, for a file which deliberately has two reloc |
| sections. */ |
| if (*p_hdr != NULL) |
| { |
| if (!bed->init_secondary_reloc_section (abfd, hdr, name, shindex)) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: warning: secondary relocation section '%s' " |
| "for section %pA found - ignoring"), |
| abfd, name, target_sect); |
| } |
| else |
| esdt->has_secondary_relocs = true; |
| goto success; |
| } |
| |
| hdr2 = (Elf_Internal_Shdr *) bfd_alloc (abfd, sizeof (*hdr2)); |
| if (hdr2 == NULL) |
| goto fail; |
| *hdr2 = *hdr; |
| *p_hdr = hdr2; |
| elf_elfsections (abfd)[shindex] = hdr2; |
| target_sect->reloc_count += (NUM_SHDR_ENTRIES (hdr) |
| * bed->s->int_rels_per_ext_rel); |
| target_sect->flags |= SEC_RELOC; |
| target_sect->relocation = NULL; |
| target_sect->rel_filepos = hdr->sh_offset; |
| /* In the section to which the relocations apply, mark whether |
| its relocations are of the REL or RELA variety. */ |
| if (hdr->sh_size != 0) |
| { |
| if (hdr->sh_type == SHT_RELA) |
| target_sect->use_rela_p = 1; |
| } |
| abfd->flags |= HAS_RELOC; |
| goto success; |
| } |
| |
| case SHT_GNU_verdef: |
| if (hdr->sh_info != 0) |
| elf_dynverdef (abfd) = shindex; |
| elf_tdata (abfd)->dynverdef_hdr = *hdr; |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| |
| case SHT_GNU_versym: |
| if (hdr->sh_entsize != sizeof (Elf_External_Versym)) |
| goto fail; |
| |
| elf_dynversym (abfd) = shindex; |
| elf_tdata (abfd)->dynversym_hdr = *hdr; |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| |
| case SHT_GNU_verneed: |
| if (hdr->sh_info != 0) |
| elf_dynverref (abfd) = shindex; |
| elf_tdata (abfd)->dynverref_hdr = *hdr; |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| |
| case SHT_SHLIB: |
| goto success; |
| |
| case SHT_GROUP: |
| if (!_bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) |
| goto fail; |
| |
| goto success; |
| |
| default: |
| /* Possibly an attributes section. */ |
| if (hdr->sh_type == SHT_GNU_ATTRIBUTES |
| || hdr->sh_type == bed->obj_attrs_section_type) |
| { |
| if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) |
| goto fail; |
| _bfd_elf_parse_attributes (abfd, hdr); |
| goto success; |
| } |
| |
| /* Check for any processor-specific section types. */ |
| if (bed->elf_backend_section_from_shdr (abfd, hdr, name, shindex)) |
| goto success; |
| |
| if (hdr->sh_type >= SHT_LOUSER && hdr->sh_type <= SHT_HIUSER) |
| { |
| if ((hdr->sh_flags & SHF_ALLOC) != 0) |
| /* FIXME: How to properly handle allocated section reserved |
| for applications? */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unknown type [%#x] section `%s'"), |
| abfd, hdr->sh_type, name); |
| else |
| { |
| /* Allow sections reserved for applications. */ |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| } |
| } |
| else if (hdr->sh_type >= SHT_LOPROC |
| && hdr->sh_type <= SHT_HIPROC) |
| /* FIXME: We should handle this section. */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unknown type [%#x] section `%s'"), |
| abfd, hdr->sh_type, name); |
| else if (hdr->sh_type >= SHT_LOOS && hdr->sh_type <= SHT_HIOS) |
| { |
| /* Unrecognised OS-specific sections. */ |
| if ((hdr->sh_flags & SHF_OS_NONCONFORMING) != 0) |
| /* SHF_OS_NONCONFORMING indicates that special knowledge is |
| required to correctly process the section and the file should |
| be rejected with an error message. */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unknown type [%#x] section `%s'"), |
| abfd, hdr->sh_type, name); |
| else |
| { |
| /* Otherwise it should be processed. */ |
| ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); |
| goto success; |
| } |
| } |
| else |
| /* FIXME: We should handle this section. */ |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: unknown type [%#x] section `%s'"), |
| abfd, hdr->sh_type, name); |
| |
| goto fail; |
| } |
| |
| fail: |
| ret = false; |
| success: |
| elf_tdata (abfd)->being_created[shindex] = false; |
| return ret; |
| } |
| |
| /* Return the local symbol specified by ABFD, R_SYMNDX. */ |
| |
| Elf_Internal_Sym * |
| bfd_sym_from_r_symndx (struct sym_cache *cache, |
| bfd *abfd, |
| unsigned long r_symndx) |
| { |
| unsigned int ent = r_symndx % LOCAL_SYM_CACHE_SIZE; |
| |
| if (cache->abfd != abfd || cache->indx[ent] != r_symndx) |
| { |
| Elf_Internal_Shdr *symtab_hdr; |
| unsigned char esym[sizeof (Elf64_External_Sym)]; |
| Elf_External_Sym_Shndx eshndx; |
| |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| if (bfd_elf_get_elf_syms (abfd, symtab_hdr, 1, r_symndx, |
| &cache->sym[ent], esym, &eshndx) == NULL) |
| return NULL; |
| |
| if (cache->abfd != abfd) |
| { |
| memset (cache->indx, -1, sizeof (cache->indx)); |
| cache->abfd = abfd; |
| } |
| cache->indx[ent] = r_symndx; |
| } |
| |
| return &cache->sym[ent]; |
|