| /* Copyright (C) 2021-2023 Free Software Foundation, Inc. |
| Contributed by Oracle. |
| |
| This file is part of GNU Binutils. |
| |
| 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, 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, 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "config.h" |
| #include <unistd.h> |
| |
| #include "util.h" |
| #include "bfd.h" |
| #include "elf-bfd.h" |
| #include "Elf.h" |
| #include "Map.h" |
| #include "StringBuilder.h" |
| #include "DbeFile.h" |
| |
| typedef uint32_t Elf32_Word; |
| typedef uint32_t Elf64_Word; |
| typedef uint32_t Elf32_Addr; |
| typedef uint64_t Elf64_Addr; |
| typedef uint64_t Elf64_Xword; |
| typedef int32_t Elf32_Sword; |
| typedef int64_t Elf64_Sxword; |
| typedef uint16_t Elf32_Half; |
| typedef uint16_t Elf64_Half; |
| |
| // Ancillary entry |
| typedef struct |
| { |
| Elf32_Word a_tag; /* how to interpret value */ |
| union |
| { |
| Elf32_Word a_val; |
| Elf32_Addr a_ptr; |
| } a_un; |
| } Elf32_Ancillary; |
| |
| struct S_Elf64_Ancillary |
| { |
| Elf64_Xword a_tag; /* how to interpret value */ |
| union |
| { |
| Elf64_Xword a_val; |
| Elf64_Addr a_ptr; |
| } a_un; |
| }; |
| |
| /* Dynamic section entry. */ |
| typedef struct |
| { |
| Elf32_Sword d_tag; /* Dynamic entry type */ |
| |
| union |
| { |
| Elf32_Word d_val; /* Integer value */ |
| Elf32_Addr d_ptr; /* Address value */ |
| } d_un; |
| } Elf32_Dyn; |
| |
| struct S_Elf64_Dyn |
| { |
| Elf64_Sxword d_tag; /* Dynamic entry type */ |
| |
| union |
| { |
| Elf64_Xword d_val; /* Integer value */ |
| Elf64_Addr d_ptr; /* Address value */ |
| } d_un; |
| }; |
| |
| |
| // Symbol table |
| typedef struct |
| { |
| Elf32_Word st_name; |
| Elf32_Addr st_value; |
| Elf32_Word st_size; |
| unsigned char st_info; /* bind, type: ELF_32_ST_... */ |
| unsigned char st_other; |
| Elf32_Half st_shndx; /* SHN_... */ |
| } Elf32_Sym; |
| |
| typedef struct |
| { |
| Elf64_Word st_name; |
| unsigned char st_info; /* bind, type: ELF_64_ST_... */ |
| unsigned char st_other; |
| Elf64_Half st_shndx; /* SHN_... */ |
| Elf64_Addr st_value; |
| Elf64_Xword st_size; |
| } Elf64_Sym; |
| |
| |
| // Relocation |
| typedef struct |
| { |
| Elf32_Addr r_offset; |
| Elf32_Word r_info; /* sym, type: ELF32_R_... */ |
| } Elf32_Rel; |
| |
| typedef struct |
| { |
| Elf32_Addr r_offset; |
| Elf32_Word r_info; /* sym, type: ELF32_R_... */ |
| Elf32_Sword r_addend; |
| } Elf32_Rela; |
| |
| typedef struct |
| { |
| Elf64_Addr r_offset; |
| Elf64_Xword r_info; /* sym, type: ELF64_R_... */ |
| } Elf64_Rel; |
| |
| typedef struct |
| { |
| Elf64_Addr r_offset; |
| Elf64_Xword r_info; /* sym, type: ELF64_R_... */ |
| Elf64_Sxword r_addend; |
| } Elf64_Rela; |
| |
| int Elf::bfd_status = -1; |
| |
| void |
| Elf::elf_init () |
| { |
| if (bfd_status == -1) |
| bfd_status = bfd_init (); |
| } |
| |
| Elf::Elf (char *filename) : DbeMessages (), Data_window (filename) |
| { |
| ehdrp = NULL; |
| data = NULL; |
| ancillary_files = NULL; |
| elfSymbols = NULL; |
| gnu_debug_file = NULL; |
| dbeFile = NULL; |
| abfd = NULL; |
| bfd_symcnt = -1; |
| bfd_dynsymcnt = -1; |
| bfd_synthcnt = -1; |
| bfd_sym = NULL; |
| bfd_dynsym = NULL; |
| bfd_synthsym = NULL; |
| synthsym = NULL; |
| |
| if (bfd_status != BFD_INIT_MAGIC) |
| { |
| status = ELF_ERR_CANT_OPEN_FILE; |
| return; |
| } |
| abfd = bfd_openr (filename, NULL); |
| if (abfd == NULL) |
| { |
| status = ELF_ERR_CANT_OPEN_FILE; |
| return; |
| } |
| abfd->flags |= BFD_DECOMPRESS; |
| if (!bfd_check_format (abfd, bfd_object)) |
| { |
| bfd_close (abfd); |
| abfd = NULL; |
| status = ELF_ERR_CANT_OPEN_FILE; |
| return; |
| } |
| ehdrp = elf_getehdr (); |
| if (ehdrp == NULL) |
| { |
| bfd_close (abfd); |
| abfd = NULL; |
| status = ELF_ERR_BAD_ELF_FORMAT; |
| return; |
| } |
| elf_class = ehdrp->e_ident[EI_CLASS]; |
| elf_datatype = ehdrp->e_ident[EI_DATA]; |
| |
| if (not_opened ()) |
| { |
| status = ELF_ERR_CANT_OPEN_FILE; |
| return; |
| } |
| status = ELF_ERR_NONE; |
| |
| #if ARCH(SPARC) |
| need_swap_endian = is_Intel (); |
| #else |
| need_swap_endian = !is_Intel (); |
| #endif |
| |
| analyzerInfo = 0; |
| SUNW_ldynsym = 0; |
| gnuLink = 0; |
| stab = 0; |
| stabStr = 0; |
| stabIndex = 0; |
| stabIndexStr = 0; |
| stabExcl = 0; |
| stabExclStr = 0; |
| symtab = 0; |
| dynsym = 0; |
| info = 0; |
| plt = 0; |
| dwarf = false; |
| |
| for (unsigned int sec = 1; sec < elf_getehdr ()->e_shnum; sec++) |
| { |
| char *name = get_sec_name (sec); |
| if (name == NULL) |
| continue; |
| if (streq (name, NTXT (".stab"))) |
| stab = sec; |
| else if (streq (name, NTXT (".stabstr"))) |
| stabStr = sec; |
| else if (streq (name, NTXT (".stab.index"))) |
| stabIndex = sec; |
| else if (streq (name, NTXT (".stab.indexstr"))) |
| stabIndexStr = sec; |
| else if (streq (name, NTXT (".stab.excl"))) |
| stabExcl = sec; |
| else if (streq (name, NTXT (".stab.exclstr"))) |
| stabExclStr = sec; |
| else if (streq (name, NTXT (".gnu_debuglink"))) |
| gnuLink = sec; |
| else if (streq (name, NTXT (".__analyzer_info"))) |
| analyzerInfo = sec; |
| else if (streq (name, NTXT (".info"))) |
| info = true; |
| else if (streq (name, NTXT (".plt"))) |
| plt = sec; |
| else if (streq (name, NTXT (".SUNW_ldynsym"))) |
| SUNW_ldynsym = sec; |
| else if (streq (name, NTXT (".dynsym"))) |
| dynsym = sec; |
| else if (streq (name, NTXT (".symtab"))) |
| symtab = sec; |
| else if (strncmp (name, NTXT (".debug"), 6) == 0) |
| dwarf = true; |
| } |
| if (fd != -1) |
| { |
| close (fd); |
| fd = -1; |
| } |
| } |
| |
| Elf::~Elf () |
| { |
| if (data) |
| { |
| for (int i = 0; i < (int) ehdrp->e_shnum; i++) |
| { |
| Elf_Data *p = data[i]; |
| if (p && !mmap_on_file && (p->d_flags & SHF_SUNW_ABSENT) == 0) |
| free (p->d_buf); |
| delete p; |
| } |
| free (data); |
| } |
| if (ancillary_files) |
| { |
| ancillary_files->destroy (); |
| delete ancillary_files; |
| } |
| delete elfSymbols; |
| delete gnu_debug_file; |
| delete dbeFile; |
| delete synthsym; |
| free (bfd_sym); |
| free (bfd_dynsym); |
| free (bfd_synthsym); |
| if (abfd) |
| bfd_close (abfd); |
| } |
| |
| Elf_Internal_Ehdr * |
| Elf::elf_getehdr () |
| { |
| if (ehdrp == NULL && abfd) |
| ehdrp = elf_elfheader (abfd); |
| return ehdrp; |
| } |
| |
| Elf_Internal_Phdr * |
| Elf::get_phdr (unsigned int ndx) |
| { |
| if (ehdrp == NULL || ndx >= ehdrp->e_phnum) |
| return NULL; |
| return &(elf_tdata (abfd)->phdr[ndx]); |
| } |
| |
| Elf_Internal_Shdr * |
| Elf::get_shdr (unsigned int ndx) |
| { |
| if (ehdrp == NULL || ndx >= ehdrp->e_shnum) |
| return NULL; |
| return elf_elfsections (abfd)[ndx]; |
| } |
| |
| Elf64_Dyn * |
| Elf::elf_getdyn (Elf_Internal_Phdr *phdr, unsigned int ndx, Elf64_Dyn *pdyn) |
| { |
| if (elf_getclass () == ELFCLASS32) |
| { |
| if (ndx * sizeof (Elf32_Dyn) >= phdr->p_filesz) |
| return NULL; |
| Elf32_Dyn *hdr = (Elf32_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf32_Dyn), |
| sizeof (Elf32_Dyn)); |
| if (hdr == NULL) |
| return NULL; |
| pdyn->d_tag = decode (hdr->d_tag); |
| pdyn->d_un.d_val = decode (hdr->d_un.d_val); |
| } |
| else |
| { |
| if (ndx * sizeof (Elf64_Dyn) >= phdr->p_filesz) |
| return NULL; |
| Elf64_Dyn *hdr = (Elf64_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf64_Dyn), |
| sizeof (Elf64_Dyn)); |
| if (hdr == NULL) |
| return NULL; |
| pdyn->d_tag = decode (hdr->d_tag); |
| pdyn->d_un.d_val = decode (hdr->d_un.d_val); |
| } |
| return pdyn; |
| } |
| |
| unsigned |
| Elf::elf_version (unsigned ver) |
| { |
| // We compile locally, no need to check the version |
| return ver; |
| } |
| |
| Elf * |
| Elf::elf_begin (char *fname, Elf_status *stp) |
| { |
| if (fname == NULL) |
| { |
| if (stp) |
| *stp = ELF_ERR_CANT_OPEN_FILE; |
| return NULL; |
| } |
| Elf *elf = new Elf (fname); |
| if (stp) |
| *stp = elf->status; |
| if (elf->status != ELF_ERR_NONE) |
| { |
| delete elf; |
| return NULL; |
| } |
| #if DEBUG |
| if (DUMP_ELF_SEC) |
| { |
| char *str = elf->dump (); |
| fprintf (stderr, NTXT ("%s\n\n"), str); |
| free (str); |
| } |
| #endif /* DEBUG */ |
| return elf; |
| } |
| |
| unsigned int |
| Elf::elf_get_sec_num (const char *name) |
| { |
| if (name == NULL || ehdrp == NULL) |
| return 0; |
| for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++) |
| { |
| Elf_Internal_Shdr *shdr = get_shdr (sec); |
| if (shdr == NULL) |
| continue; |
| char *sname = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); |
| if (sname != NULL && strcmp (name, sname) == 0) |
| return sec; |
| } |
| return 0; |
| } |
| |
| char * |
| Elf::get_sec_name (unsigned int sec) |
| { |
| Elf_Internal_Shdr *shdr = get_shdr (sec); |
| if (ehdrp == NULL || shdr == NULL) |
| return NULL; |
| return elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); |
| } |
| |
| Elf_Data * |
| Elf::elf_getdata (unsigned int sec) |
| { |
| if (data == NULL) |
| { |
| data = (Elf_Data **) malloc (ehdrp->e_shnum * sizeof (Elf_Data *)); |
| for (int i = 0; i < (int) ehdrp->e_shnum; i++) |
| data[i] = NULL; |
| } |
| Elf_Data *edta = data[sec]; |
| if (edta == NULL) |
| { |
| Elf_Internal_Shdr *shdr = get_shdr (sec); |
| if (shdr == NULL) |
| return NULL; |
| edta = new Elf_Data; |
| data[sec] = edta; |
| if ((shdr->sh_flags & SHF_SUNW_ABSENT) != 0) |
| { |
| char *sname = get_sec_name (sec); |
| for (int i = 0, sz = VecSize(ancillary_files); i < sz; i++) |
| { |
| Elf *ancElf = ancillary_files->fetch (i); |
| int secNum = sec; |
| if (dbe_strcmp (sname, ancElf->get_sec_name (sec)) != 0) |
| { |
| append_msg (CMSG_WARN, |
| "Warning: the section #%d (%s) is mismatch in ancillary file '%s')\n", |
| sec, STR (sname), STR (ancElf->fname)); |
| secNum = ancElf->elf_get_sec_num (sname); |
| } |
| if (secNum > 0) |
| { |
| Elf_Data *ed = ancElf->elf_getdata (secNum); |
| if (ed && ed->d_buf) |
| { |
| *edta = *ed; |
| edta->d_flags |= SHF_SUNW_ABSENT; |
| return edta; |
| } |
| } |
| } |
| } |
| edta->d_buf = get_data (shdr->sh_offset, (size_t) shdr->sh_size, NULL); |
| edta->d_flags = shdr->sh_flags; |
| edta->d_size = ((edta->d_buf == NULL) || (shdr->sh_type == SHT_NOBITS)) ? 0 : shdr->sh_size; |
| edta->d_off = shdr->sh_offset; |
| edta->d_align = shdr->sh_addralign; |
| } |
| return edta; |
| } |
| |
| int64_t |
| Elf::elf_checksum () |
| { |
| if (ehdrp == NULL) |
| return 0; |
| int64_t chk = 0; |
| for (unsigned int ndx = 0; ndx < ehdrp->e_phnum; ndx++) |
| { |
| Elf_Internal_Phdr *phdr = get_phdr (ndx); |
| if (phdr == NULL) |
| continue; |
| if (phdr->p_type == PT_DYNAMIC) |
| { |
| Elf64_Dyn edyn; |
| for (unsigned int i = 0; elf_getdyn (phdr, i, &edyn) != NULL; i++) |
| { |
| if (!edyn.d_tag) |
| break; |
| if (edyn.d_tag == DT_CHECKSUM) |
| { |
| chk = edyn.d_un.d_val; |
| break; |
| } |
| } |
| } |
| } |
| return normalize_checksum (chk); |
| } |
| |
| uint64_t |
| Elf::get_baseAddr () |
| { |
| uint64_t addr = 0; |
| for (unsigned int pnum = 0; pnum < elf_getehdr ()->e_phnum; pnum++) |
| { |
| Elf_Internal_Phdr *phdr = get_phdr (pnum); |
| if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X)) |
| { |
| if (addr == 0) |
| addr = phdr->p_vaddr; |
| else |
| { |
| addr = 0; |
| break; |
| } |
| } |
| } |
| return addr; |
| } |
| |
| char * |
| Elf::elf_strptr (unsigned int sec, uint64_t off) |
| { |
| Elf_Data *edta = elf_getdata (sec); |
| if (edta && edta->d_buf && edta->d_size > off) |
| return ((char *) edta->d_buf) + off; |
| return NULL; |
| } |
| |
| Elf_Internal_Sym * |
| Elf::elf_getsym (Elf_Data *edta, unsigned int ndx, Elf_Internal_Sym *dst) |
| { |
| if (dst == NULL || edta == NULL) |
| return NULL; |
| if (elf_getclass () == ELFCLASS32) |
| { |
| if (edta->d_size <= ndx * sizeof (Elf32_Sym)) |
| return NULL; |
| Elf32_Sym *hdr = (Elf32_Sym*) bind (edta->d_off + ndx * sizeof (Elf32_Sym), sizeof (Elf32_Sym)); |
| if (hdr == NULL) |
| return NULL; |
| dst->st_name = decode (hdr->st_name); |
| dst->st_value = decode (hdr->st_value); |
| dst->st_size = decode (hdr->st_size); |
| dst->st_info = ELF64_ST_INFO (ELF32_ST_BIND (decode (hdr->st_info)), |
| ELF32_ST_TYPE (decode (hdr->st_info))); |
| dst->st_other = decode (hdr->st_other); |
| dst->st_shndx = decode (hdr->st_shndx); |
| } |
| else |
| { |
| if (edta->d_size <= ndx * sizeof (Elf64_Sym)) |
| return NULL; |
| Elf64_Sym *hdr = (Elf64_Sym*) bind (edta->d_off + ndx * sizeof (Elf64_Sym), |
| sizeof (Elf64_Sym)); |
| if (hdr == NULL) |
| return NULL; |
| dst->st_name = decode (hdr->st_name); |
| dst->st_value = decode (hdr->st_value); |
| dst->st_size = decode (hdr->st_size); |
| dst->st_info = decode (hdr->st_info); |
| dst->st_other = decode (hdr->st_other); |
| dst->st_shndx = decode (hdr->st_shndx); |
| } |
| return dst; |
| } |
| |
| Elf_Internal_Rela * |
| Elf::elf_getrel (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst) |
| { |
| if (dst == NULL || edta == NULL || edta->d_buf == NULL) |
| return NULL; |
| if (elf_getclass () == ELFCLASS32) |
| { |
| Elf32_Rel *rel = ((Elf32_Rel *) edta->d_buf) + ndx; |
| dst->r_offset = decode (rel->r_offset); |
| dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rel->r_info)), |
| ELF32_R_TYPE (decode (rel->r_info))); |
| } |
| else |
| { |
| Elf64_Rel *rel = ((Elf64_Rel *) edta->d_buf) + ndx; |
| dst->r_offset = decode (rel->r_offset); |
| dst->r_info = decode (rel->r_info); |
| } |
| return dst; |
| } |
| |
| Elf_Internal_Rela * |
| Elf::elf_getrela (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst) |
| { |
| if (dst == NULL || edta == NULL || edta->d_buf == NULL) |
| return NULL; |
| if (elf_getclass () == ELFCLASS32) |
| { |
| Elf32_Rela *rela = ((Elf32_Rela *) edta->d_buf) + ndx; |
| dst->r_offset = decode (rela->r_offset); |
| dst->r_addend = decode (rela->r_addend); |
| dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rela->r_info)), |
| ELF32_R_TYPE (decode (rela->r_info))); |
| } |
| else |
| { |
| Elf64_Rela *rela = ((Elf64_Rela *) edta->d_buf) + ndx; |
| dst->r_offset = decode (rela->r_offset); |
| dst->r_addend = decode (rela->r_addend); |
| dst->r_info = decode (rela->r_info); |
| } |
| return dst; |
| } |
| |
| Elf64_Ancillary * |
| Elf::elf_getancillary (Elf_Data *edta, unsigned int ndx, Elf64_Ancillary *dst) |
| { |
| if (dst == NULL || edta == NULL || edta->d_buf == NULL) |
| return NULL; |
| if (elf_getclass () == ELFCLASS32) |
| { |
| Elf32_Ancillary *p = ((Elf32_Ancillary *) edta->d_buf) + ndx; |
| dst->a_tag = decode (p->a_tag); |
| dst->a_un.a_val = decode (p->a_un.a_val); |
| } |
| else |
| { |
| Elf64_Ancillary *p = ((Elf64_Ancillary *) edta->d_buf) + ndx; |
| dst->a_tag = decode (p->a_tag); |
| dst->a_un.a_val = decode (p->a_un.a_val); |
| } |
| return dst; |
| } |
| |
| Elf * |
| Elf::get_related_file (const char *lo_name, const char *nm) |
| { |
| DbeFile *df; |
| if (*nm == '/') |
| { |
| df = new DbeFile (nm); |
| df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE); |
| } |
| else |
| { |
| char *bname = get_basename (lo_name); |
| char *fnm = dbe_sprintf ("%.*s/%s", (int) (bname - lo_name), lo_name, nm); |
| df = new DbeFile (fnm); |
| df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE); |
| free (fnm); |
| } |
| Dprintf (DEBUG_STABS, "get_related_file: %s -> '%s'\n", nm, df->get_name ()); |
| Elf_status st = ELF_ERR_CANT_OPEN_FILE; |
| Elf *elf = elf_begin (df->get_location (), &st); |
| if (elf) |
| { |
| elf->dbeFile = df; |
| return elf; |
| } |
| switch (st) |
| { |
| case ELF_ERR_CANT_OPEN_FILE: |
| append_msg (CMSG_ERROR, GTXT ("Cannot open file `%s'"), df->get_name ()); |
| break; |
| case ELF_ERR_BAD_ELF_FORMAT: |
| default: |
| append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), |
| df->get_name ()); |
| break; |
| } |
| delete df; |
| return NULL; |
| } |
| |
| Elf * |
| Elf::find_ancillary_files (char *lo_name) |
| { |
| // read the .gnu_debuglink and .SUNW_ancillary seections |
| if (gnu_debug_file) |
| return gnu_debug_file; |
| unsigned int sec = elf_get_sec_num (NTXT (".gnu_debuglink")); |
| if (sec > 0) |
| { |
| Elf_Data *dp = elf_getdata (sec); |
| if (dp) |
| { |
| gnu_debug_file = get_related_file (lo_name, (char *) (dp->d_buf)); |
| if (gnu_debug_file) |
| return gnu_debug_file; |
| } |
| } |
| |
| sec = elf_get_sec_num (NTXT (".SUNW_ancillary")); |
| if (sec > 0) |
| { |
| Elf_Internal_Shdr *shdr = get_shdr (sec); |
| uint64_t check_sum = 0; |
| char *ancName = NULL; |
| if (shdr) |
| { |
| Elf_Data *dp = elf_getdata (sec); |
| for (int i = 0, sz = (int) (shdr->sh_size / shdr->sh_entsize); |
| i < sz; i++) |
| { |
| Elf64_Ancillary anc; |
| if (elf_getancillary (dp, i, &anc) == NULL |
| || anc.a_tag == ANC_SUNW_NULL) |
| break; |
| if (anc.a_tag == ANC_SUNW_MEMBER) |
| ancName = elf_strptr (shdr->sh_link, anc.a_un.a_ptr); |
| else if (anc.a_tag == ANC_SUNW_CHECKSUM) |
| { |
| if (i == 0) |
| { |
| check_sum = anc.a_un.a_val; |
| continue; |
| } |
| if (check_sum == anc.a_un.a_val) |
| ancName = NULL; |
| if (ancName) |
| { |
| Elf *ancElf = get_related_file (lo_name, ancName); |
| if (ancElf == NULL) |
| continue; |
| int ancSec = ancElf->elf_get_sec_num (".SUNW_ancillary"); |
| if (ancSec > 0) |
| { |
| Elf_Internal_Shdr *ancHdr = ancElf->get_shdr (ancSec); |
| if (ancHdr) |
| { |
| Elf_Data *anc_dp = ancElf->elf_getdata (ancSec); |
| Elf64_Ancillary anc1; |
| if (ancElf->elf_getancillary (anc_dp, 0, &anc1) |
| && (anc1.a_tag == ANC_SUNW_CHECKSUM) && |
| anc1.a_un.a_val == anc.a_un.a_val) |
| { |
| if (ancillary_files == NULL) |
| ancillary_files = new Vector<Elf*>(2); |
| ancillary_files->append (ancElf); |
| } |
| else |
| append_msg (CMSG_WARN, GTXT ("Load Object: '%s' (checksum Ox%lld). The .anc file '%s' has checksum Ox%llx"), |
| STR (fname), (long long) check_sum, |
| STR (ancElf->dbeFile->get_location ()), |
| (long long) anc1.a_un.a_val); |
| } |
| } |
| ancName = NULL; |
| } |
| } |
| } |
| } |
| } |
| return NULL; |
| } |
| |
| void |
| Elf::get_bfd_symbols() |
| { |
| if (bfd_symcnt < 0) |
| { |
| if ((bfd_get_file_flags (abfd) & HAS_SYMS) != 0) |
| bfd_symcnt = bfd_get_symtab_upper_bound (abfd); |
| if (bfd_symcnt > 0) |
| { |
| bfd_sym = (asymbol **) malloc (bfd_symcnt); |
| bfd_symcnt = bfd_canonicalize_symtab (abfd, bfd_sym); |
| if (bfd_symcnt < 0) |
| { |
| free (bfd_sym); |
| bfd_sym = NULL; |
| } |
| } |
| else |
| bfd_symcnt = 0; |
| } |
| |
| if (bfd_dynsymcnt < 0) |
| { |
| bfd_dynsymcnt = bfd_get_dynamic_symtab_upper_bound (abfd); |
| if (bfd_dynsymcnt > 0) |
| { |
| bfd_dynsym = (asymbol **) malloc (bfd_dynsymcnt); |
| bfd_dynsymcnt = bfd_canonicalize_dynamic_symtab (abfd, bfd_dynsym); |
| if (bfd_dynsymcnt < 0) |
| { |
| free (bfd_dynsym); |
| bfd_dynsym = NULL; |
| } |
| } |
| else |
| bfd_dynsymcnt = 0; |
| } |
| if (bfd_synthcnt < 0) |
| { |
| bfd_synthcnt = bfd_get_synthetic_symtab (abfd, bfd_symcnt, bfd_sym, |
| bfd_dynsymcnt, bfd_dynsym, &bfd_synthsym); |
| if (bfd_synthcnt < 0) |
| bfd_synthcnt = 0; |
| } |
| } |
| |
| static int |
| cmp_sym_addr (const void *a, const void *b) |
| { |
| asymbol *sym1 = *((asymbol **) a); |
| asymbol *sym2 = *((asymbol **) b); |
| uint64_t a1 = sym1->value; |
| uint64_t a2 = sym2->value; |
| if (sym1->section) |
| a1 += sym1->section->vma; |
| if (sym2->section) |
| a2 += sym2->section->vma; |
| return a1 < a2 ? -1 : (a1 == a2 ? 0 : 1); |
| } |
| |
| const char * |
| Elf::get_funcname_in_plt (uint64_t pc) |
| { |
| if (synthsym == NULL) |
| { |
| get_bfd_symbols(); |
| synthsym = new Vector<asymbol *> (bfd_synthcnt + 1); |
| for (long i = 0; i < bfd_synthcnt; i++) |
| synthsym->append (bfd_synthsym + i); |
| synthsym->sort (cmp_sym_addr); |
| } |
| |
| asymbol sym, *symp = &sym; |
| sym.section = NULL; |
| sym.value = pc; |
| long ind = synthsym->bisearch (0, -1, &symp, cmp_sym_addr); |
| if (ind >= 0) |
| return synthsym->get (ind)->name; |
| return NULL; |
| } |
| |
| char* |
| Elf::get_location () |
| { |
| return dbeFile ? dbeFile->get_location () : fname; |
| } |
| |
| #define RET_S(x) if (t == x) return (char *) #x |
| |
| static char * |
| get_elf_class_name (int t) |
| { |
| RET_S (ELFCLASSNONE); |
| RET_S (ELFCLASS32); |
| RET_S (ELFCLASS64); |
| return NTXT ("ELFCLASS_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_data_name (int t) |
| { |
| RET_S (ELFDATANONE); |
| RET_S (ELFDATA2LSB); |
| RET_S (ELFDATA2MSB); |
| return NTXT ("ELFDATA_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_osabi_name (int t) |
| { |
| RET_S (ELFOSABI_NONE); |
| RET_S (ELFOSABI_HPUX); |
| RET_S (ELFOSABI_NETBSD); |
| RET_S (ELFOSABI_LINUX); |
| RET_S (ELFOSABI_SOLARIS); |
| RET_S (ELFOSABI_AIX); |
| RET_S (ELFOSABI_IRIX); |
| RET_S (ELFOSABI_FREEBSD); |
| RET_S (ELFOSABI_TRU64); |
| RET_S (ELFOSABI_MODESTO); |
| RET_S (ELFOSABI_OPENBSD); |
| return NTXT ("ELFOSABI_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_etype_name (int t) |
| { |
| RET_S (ET_NONE); |
| RET_S (ET_REL); |
| RET_S (ET_EXEC); |
| RET_S (ET_DYN); |
| RET_S (ET_CORE); |
| RET_S (ET_LOPROC); |
| RET_S (ET_HIPROC); |
| return NTXT ("ETYPE_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_ptype_name (int t) |
| { |
| RET_S (PT_NULL); |
| RET_S (PT_LOAD); |
| RET_S (PT_DYNAMIC); |
| RET_S (PT_INTERP); |
| RET_S (PT_NOTE); |
| RET_S (PT_SHLIB); |
| RET_S (PT_PHDR); |
| RET_S (PT_TLS); |
| RET_S (PT_LOOS); |
| RET_S (PT_GNU_EH_FRAME); |
| RET_S (PT_GNU_EH_FRAME); |
| RET_S (PT_HIOS); |
| RET_S (PT_LOPROC); |
| RET_S (PT_HIPROC); |
| return NTXT ("PTYPE_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_shtype_name (unsigned int t) |
| { |
| RET_S (SHT_NULL); |
| RET_S (SHT_PROGBITS); |
| RET_S (SHT_SYMTAB); |
| RET_S (SHT_STRTAB); |
| RET_S (SHT_RELA); |
| RET_S (SHT_HASH); |
| RET_S (SHT_DYNAMIC); |
| RET_S (SHT_NOTE); |
| RET_S (SHT_NOBITS); |
| RET_S (SHT_REL); |
| RET_S (SHT_SHLIB); |
| RET_S (SHT_DYNSYM); |
| RET_S (SHT_INIT_ARRAY); |
| RET_S (SHT_FINI_ARRAY); |
| RET_S (SHT_PREINIT_ARRAY); |
| RET_S (SHT_GROUP); |
| RET_S (SHT_SYMTAB_SHNDX); |
| RET_S (SHT_LOOS); |
| RET_S (SHT_SUNW_verdef); |
| RET_S (SHT_SUNW_verneed); |
| RET_S (SHT_HIOS); |
| RET_S (SHT_LOPROC); |
| RET_S (SHT_HIPROC); |
| RET_S (SHT_LOUSER); |
| RET_S (SHT_HIUSER); |
| return NTXT ("SHTYPE_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_machine_name (int t) |
| { |
| RET_S (EM_NONE); |
| RET_S (EM_M32); |
| RET_S (EM_SPARC); |
| RET_S (EM_386); |
| RET_S (EM_68K); |
| RET_S (EM_88K); |
| RET_S (EM_860); |
| RET_S (EM_MIPS); |
| RET_S (EM_S370); |
| RET_S (EM_MIPS_RS3_LE); |
| RET_S (EM_SPARC32PLUS); |
| RET_S (EM_960); |
| RET_S (EM_PPC); |
| RET_S (EM_PPC64); |
| RET_S (EM_V800); |
| RET_S (EM_FR20); |
| RET_S (EM_RH32); |
| RET_S (EM_RCE); |
| RET_S (EM_ARM); |
| RET_S (EM_ALPHA); |
| RET_S (EM_SH); |
| RET_S (EM_SPARCV9); |
| RET_S (EM_TRICORE); |
| RET_S (EM_ARC); |
| RET_S (EM_H8_300); |
| RET_S (EM_H8_300H); |
| RET_S (EM_H8S); |
| RET_S (EM_H8_500); |
| RET_S (EM_IA_64); |
| RET_S (EM_MIPS_X); |
| RET_S (EM_COLDFIRE); |
| RET_S (EM_68HC12); |
| RET_S (EM_MMA); |
| RET_S (EM_PCP); |
| RET_S (EM_NCPU); |
| RET_S (EM_NDR1); |
| RET_S (EM_STARCORE); |
| RET_S (EM_ME16); |
| RET_S (EM_ST100); |
| RET_S (EM_TINYJ); |
| RET_S (EM_X86_64); |
| RET_S (EM_PDSP); |
| RET_S (EM_FX66); |
| RET_S (EM_ST9PLUS); |
| RET_S (EM_ST7); |
| RET_S (EM_68HC16); |
| RET_S (EM_68HC11); |
| RET_S (EM_68HC08); |
| RET_S (EM_68HC05); |
| RET_S (EM_SVX); |
| RET_S (EM_ST19); |
| RET_S (EM_VAX); |
| RET_S (EM_CRIS); |
| RET_S (EM_JAVELIN); |
| RET_S (EM_FIREPATH); |
| RET_S (EM_ZSP); |
| RET_S (EM_MMIX); |
| RET_S (EM_HUANY); |
| RET_S (EM_PRISM); |
| RET_S (EM_AVR); |
| RET_S (EM_FR30); |
| RET_S (EM_D10V); |
| RET_S (EM_D30V); |
| RET_S (EM_V850); |
| RET_S (EM_M32R); |
| RET_S (EM_MN10300); |
| RET_S (EM_MN10200); |
| RET_S (EM_PJ); |
| RET_S (EM_OPENRISC); |
| RET_S (EM_XTENSA); |
| return NTXT ("ELFMACHINE_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_version_name (int t) |
| { |
| RET_S (EV_NONE); |
| RET_S (EV_CURRENT); |
| return NTXT ("VERSION_UNKNOWN"); |
| } |
| |
| static char * |
| get_elf_ancillary_tag (int t) |
| { |
| RET_S (ANC_SUNW_NULL); |
| RET_S (ANC_SUNW_CHECKSUM); |
| RET_S (ANC_SUNW_MEMBER); |
| RET_S (ANC_SUNW_NUM); |
| return NTXT ("ANCILLARY_TAG_UNKNOWN"); |
| } |
| |
| #define ADD_S(x) if ((f & (x)) == (x)) { sb->append(' '); sb->append(#x); f &= ~(x); } |
| |
| static void |
| dump_sh_flags (StringBuilder *sb, long long flags) |
| { |
| long long f = flags; |
| if (f != 0) |
| { |
| sb->append (NTXT (" [")); |
| ADD_S (SHF_WRITE) |
| ADD_S (SHF_ALLOC) |
| ADD_S (SHF_EXECINSTR) |
| ADD_S (SHF_MERGE) |
| ADD_S (SHF_STRINGS) |
| ADD_S (SHF_INFO_LINK) |
| ADD_S (SHF_LINK_ORDER) |
| ADD_S (SHF_OS_NONCONFORMING) |
| ADD_S (SHF_GROUP) |
| ADD_S (SHF_TLS) |
| ADD_S (SHF_SUNW_ABSENT) |
| ADD_S (SHF_EXCLUDE) |
| if (f != 0 && f != flags) |
| sb->appendf (NTXT (" 0x%llx"), (long long) f); |
| sb->append (NTXT (" ]")); |
| } |
| sb->append (NTXT ("\n")); |
| } |
| |
| static void |
| dump_p_flags (StringBuilder *sb, long long flags) |
| { |
| long long f = flags; |
| if (f != 0) |
| { |
| sb->append (NTXT (" [")); |
| ADD_S (PF_X) |
| ADD_S (PF_W) |
| ADD_S (PF_R) |
| ADD_S (PF_MASKPROC) |
| if (f != 0 && f != flags) |
| sb->appendf (NTXT (" 0x%llx"), (long long) f); |
| sb->append (NTXT (" ]")); |
| } |
| sb->append (NTXT ("\n")); |
| } |
| |
| char * |
| Elf::dump () |
| { |
| StringBuilder sb; |
| sb.sprintf (NTXT ("ELF Header: %s\n"), fname ? fname : GTXT ("(unknown)")); |
| if (ehdrp == NULL) |
| { |
| sb.appendf (GTXT ("\n\n Cannot read Elf header\n")); |
| return sb.toString (); |
| } |
| sb.appendf (NTXT (" %-15s "), NTXT ("e_ident")); |
| for (int i = 0; i < EI_NIDENT; i++) |
| sb.appendf (NTXT ("%x"), ehdrp->e_ident[i]); |
| sb.append (NTXT ("\n")); |
| char *fmt0 = NTXT (" %-15s %10lld ( %s )\n"); |
| char *fmt1 = NTXT (" %-15s 0x%08llx ( %lld )\n"); |
| char *fmt2 = NTXT (" %-15s 0x%08llx"); |
| sb.appendf (fmt0, NTXT ("EI_CLASS"), (long long) ehdrp->e_ident[EI_CLASS], |
| get_elf_class_name (ehdrp->e_ident[EI_CLASS])); |
| sb.appendf (fmt0, NTXT ("EI_DATA"), (long long) ehdrp->e_ident[EI_DATA], |
| get_elf_data_name (ehdrp->e_ident[EI_DATA])); |
| sb.appendf (fmt0, NTXT ("EI_OSABI"), (long long) ehdrp->e_ident[EI_OSABI], |
| get_elf_osabi_name (ehdrp->e_ident[EI_OSABI])); |
| sb.appendf (fmt0, NTXT ("e_type"), (long long) ehdrp->e_type, |
| get_elf_etype_name (ehdrp->e_type)); |
| sb.appendf (fmt0, NTXT ("e_machine"), (long long) ehdrp->e_machine, |
| get_elf_machine_name (ehdrp->e_machine)); |
| sb.appendf (fmt0, NTXT ("e_version"), (long long) ehdrp->e_version, |
| get_elf_version_name (ehdrp->e_version)); |
| sb.appendf (fmt1, NTXT ("e_entry"), (long long) ehdrp->e_entry, |
| (long long) ehdrp->e_entry); |
| sb.appendf (fmt1, NTXT ("e_phoff"), (long long) ehdrp->e_phoff, |
| (long long) ehdrp->e_phoff); |
| sb.appendf (fmt1, NTXT ("e_shoff"), (long long) ehdrp->e_shoff, |
| (long long) ehdrp->e_shoff); |
| sb.appendf (fmt1, NTXT ("e_flags"), (long long) ehdrp->e_flags, |
| (long long) ehdrp->e_flags); |
| sb.appendf (fmt1, NTXT ("e_ehsize"), (long long) ehdrp->e_ehsize, |
| (long long) ehdrp->e_ehsize); |
| sb.appendf (fmt1, NTXT ("e_phentsize"), (long long) ehdrp->e_phentsize, |
| (long long) ehdrp->e_phentsize); |
| sb.appendf (fmt1, NTXT ("e_phnum"), (long long) ehdrp->e_phnum, |
| (long long) ehdrp->e_phnum); |
| sb.appendf (fmt1, NTXT ("e_shentsize"), (long long) ehdrp->e_shentsize, |
| (long long) ehdrp->e_shentsize); |
| sb.appendf (fmt1, NTXT ("e_shnum"), (long long) ehdrp->e_shnum, |
| (long long) ehdrp->e_shnum); |
| sb.appendf (fmt1, NTXT ("e_shstrndx"), (long long) ehdrp->e_shstrndx, |
| (long long) ehdrp->e_shstrndx); |
| |
| for (unsigned int i = 0; i < ehdrp->e_phnum; i++) |
| { |
| sb.appendf (NTXT ("\nProgram Header[%d]:\n"), i); |
| Elf_Internal_Phdr *phdr = get_phdr (i); |
| if (phdr == NULL) |
| { |
| sb.appendf (NTXT (" ERROR: get_phdr(%d) failed\n"), i); |
| continue; |
| } |
| sb.appendf (fmt0, "p_type", (long long) phdr->p_type, |
| get_elf_ptype_name (phdr->p_type)); |
| sb.appendf (fmt2, "p_flags", (long long) phdr->p_flags); |
| dump_p_flags (&sb, phdr->p_flags); |
| sb.appendf (fmt1, "p_offset", (long long) phdr->p_offset, |
| (long long) phdr->p_offset); |
| sb.appendf (fmt1, "p_vaddr", (long long) phdr->p_vaddr, |
| (long long) phdr->p_vaddr); |
| sb.appendf (fmt1, "p_paddr", (long long) phdr->p_paddr, |
| (long long) phdr->p_paddr); |
| sb.appendf (fmt1, "p_filesz", (long long) phdr->p_filesz, |
| (long long) phdr->p_filesz); |
| sb.appendf (fmt1, "p_memsz", (long long) phdr->p_memsz, |
| (long long) phdr->p_memsz); |
| sb.appendf (fmt1, "p_align", (long long) phdr->p_align, |
| (long long) phdr->p_align); |
| } |
| |
| for (unsigned int i = 1; i < ehdrp->e_shnum; i++) |
| { |
| sb.appendf (NTXT ("\nSection Header[%d]:\n"), i); |
| Elf_Internal_Shdr *shdr = get_shdr (i); |
| if (shdr == NULL) |
| { |
| sb.appendf (NTXT (" ERROR: get_shdr(%d) failed\n"), i); |
| continue; |
| } |
| char *s = get_sec_name (i); |
| sb.appendf (fmt0, "sh_name", (long long) shdr->sh_name, |
| s ? s : NTXT ("NULL")); |
| sb.appendf (fmt0, "sh_type", (long long) shdr->sh_type, |
| get_elf_shtype_name (shdr->sh_type)); |
| sb.appendf (fmt2, "sh_flags", (long long) shdr->sh_flags); |
| dump_sh_flags (&sb, shdr->sh_flags); |
| sb.appendf (fmt1, "sh_addr", (long long) shdr->sh_addr, |
| (long long) shdr->sh_addr); |
| sb.appendf (fmt1, "sh_offset", (long long) shdr->sh_offset, |
| (long long) shdr->sh_offset); |
| sb.appendf (fmt1, "sh_size", (long long) shdr->sh_size, |
| (long long) shdr->sh_size); |
| sb.appendf (fmt1, "sh_link", (long long) shdr->sh_link, |
| (long long) shdr->sh_link); |
| sb.appendf (fmt1, "sh_info", (long long) shdr->sh_info, |
| (long long) shdr->sh_info); |
| sb.appendf (fmt1, "sh_addralign", (long long) shdr->sh_addralign, |
| (long long) shdr->sh_addralign); |
| sb.appendf (fmt1, "sh_entsize", (long long) shdr->sh_entsize, |
| (long long) shdr->sh_entsize); |
| } |
| |
| for (unsigned int i = 1; i < ehdrp->e_shnum; i++) |
| { |
| Elf_Internal_Shdr *shdr = get_shdr (i); |
| if (shdr == NULL) |
| continue; |
| char *secName = get_sec_name (i); |
| if (secName == NULL) |
| continue; |
| if (strcmp (NTXT (".SUNW_ancillary"), secName) == 0) |
| { |
| sb.appendf (NTXT ("\nSection[%d]: %s\n"), i, secName); |
| Elf_Data *dp = elf_getdata (i); |
| for (int j = 0, cnt = (int) (shdr->sh_size / shdr->sh_entsize); |
| j < cnt; j++) |
| { |
| Elf64_Ancillary anc; |
| if (elf_getancillary (dp, j, &anc) == NULL) |
| break; |
| sb.appendf (NTXT ("%10d %-20s 0x%08llx %6lld"), j, |
| get_elf_ancillary_tag ((int) anc.a_tag), |
| (long long) anc.a_un.a_ptr, (long long) anc.a_un.a_ptr); |
| if (anc.a_tag == ANC_SUNW_MEMBER) |
| sb.appendf (NTXT (" %s\n"), STR (elf_strptr (shdr->sh_link, anc.a_un.a_ptr))); |
| else |
| sb.append (NTXT ("\n")); |
| } |
| } |
| } |
| return sb.toString (); |
| } |
| |
| void |
| Elf::dump_elf_sec () |
| { |
| if (!DUMP_ELF_SEC) |
| return; |
| if (ehdrp == NULL) |
| return; |
| Dprintf (DUMP_ELF_SEC, "======= DwarfLib::dump_elf_sec\n" |
| " N |type|flags| sh_addr | sh_offset | sh_size | sh_link |" |
| " sh_info | sh_addralign | sh_entsize | sh_name | name\n"); |
| for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++) |
| { |
| Elf_Internal_Shdr *shdr = get_shdr (sec); |
| if (shdr == NULL) |
| continue; |
| char *name = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); |
| Dprintf (DUMP_ELF_SEC, "%3d:%3d |%4d |%9lld | %9lld |%8lld |%8lld |" |
| "%8lld |%14d |%11lld | %6lld %s\n", |
| sec, (int) shdr->sh_type, (int) shdr->sh_flags, |
| (long long) shdr->sh_addr, (long long) shdr->sh_offset, |
| (long long) shdr->sh_size, (long long) shdr->sh_link, |
| (long long) shdr->sh_info, |
| (int) shdr->sh_addralign, (long long) shdr->sh_entsize, |
| (long long) shdr->sh_name, name ? name : NTXT ("NULL")); |
| } |
| Dprintf (DUMP_ELF_SEC, NTXT ("\n")); |
| } |