| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // -*- Mode: C++ -*- |
| // |
| // Copyright (C) 2020 Google, Inc. |
| |
| /// @file |
| /// |
| /// This contains the definitions of the ELF utilities for the dwarf reader. |
| |
| #include "abg-elf-helpers.h" |
| |
| #include <elf.h> |
| |
| #include "abg-tools-utils.h" |
| |
| namespace abigail |
| { |
| |
| namespace elf_helpers |
| { |
| |
| /// Convert an elf symbol type (given by the ELF{32,64}_ST_TYPE |
| /// macros) into an elf_symbol::type value. |
| /// |
| /// Note that this function aborts when given an unexpected value. |
| /// |
| /// @param the symbol type value to convert. |
| /// |
| /// @return the converted value. |
| elf_symbol::type |
| stt_to_elf_symbol_type(unsigned char stt) |
| { |
| switch (stt) |
| { |
| case STT_NOTYPE: |
| return elf_symbol::NOTYPE_TYPE; |
| case STT_OBJECT: |
| return elf_symbol::OBJECT_TYPE; |
| case STT_FUNC: |
| return elf_symbol::FUNC_TYPE; |
| case STT_SECTION: |
| return elf_symbol::SECTION_TYPE; |
| case STT_FILE: |
| return elf_symbol::FILE_TYPE; |
| case STT_COMMON: |
| return elf_symbol::COMMON_TYPE; |
| case STT_TLS: |
| return elf_symbol::TLS_TYPE; |
| case STT_GNU_IFUNC: |
| return elf_symbol::GNU_IFUNC_TYPE; |
| default: |
| // An unknown value that probably ought to be supported? Let's |
| // abort right here rather than yielding garbage. |
| ABG_ASSERT_NOT_REACHED; |
| } |
| } |
| |
| /// Convert an elf symbol binding (given by the ELF{32,64}_ST_BIND |
| /// macros) into an elf_symbol::binding value. |
| /// |
| /// Note that this function aborts when given an unexpected value. |
| /// |
| /// @param the symbol binding value to convert. |
| /// |
| /// @return the converted value. |
| elf_symbol::binding |
| stb_to_elf_symbol_binding(unsigned char stb) |
| { |
| switch (stb) |
| { |
| case STB_LOCAL: |
| return elf_symbol::LOCAL_BINDING; |
| case STB_GLOBAL: |
| return elf_symbol::GLOBAL_BINDING; |
| case STB_WEAK: |
| return elf_symbol::WEAK_BINDING; |
| case STB_GNU_UNIQUE: |
| return elf_symbol::GNU_UNIQUE_BINDING; |
| default: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| } |
| |
| /// Convert an ELF symbol visiblity given by the symbols ->st_other |
| /// data member as returned by the GELF_ST_VISIBILITY macro into a |
| /// elf_symbol::visiblity value. |
| /// |
| /// @param stv the value of the ->st_other data member of the ELF |
| /// symbol. |
| /// |
| /// @return the converted elf_symbol::visiblity value. |
| elf_symbol::visibility |
| stv_to_elf_symbol_visibility(unsigned char stv) |
| { |
| switch (stv) |
| { |
| case STV_DEFAULT: |
| return elf_symbol::DEFAULT_VISIBILITY; |
| case STV_INTERNAL: |
| return elf_symbol::INTERNAL_VISIBILITY; |
| case STV_HIDDEN: |
| return elf_symbol::HIDDEN_VISIBILITY; |
| case STV_PROTECTED: |
| return elf_symbol::PROTECTED_VISIBILITY; |
| default: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| } |
| |
| /// Convert the value of the e_machine field of GElf_Ehdr into a |
| /// string. This is to get a string representing the architecture of |
| /// the elf file at hand. |
| /// |
| /// @param e_machine the value of GElf_Ehdr::e_machine. |
| /// |
| /// @return the string representation of GElf_Ehdr::e_machine. |
| std::string |
| e_machine_to_string(GElf_Half e_machine) |
| { |
| switch (e_machine) |
| { |
| case EM_NONE: |
| return "elf-no-arch"; |
| case EM_M32: |
| return "elf-att-we-32100"; |
| case EM_SPARC: |
| return "elf-sun-sparc"; |
| case EM_386: |
| return "elf-intel-80386"; |
| case EM_68K: |
| return "elf-motorola-68k"; |
| case EM_88K: |
| return "elf-motorola-88k"; |
| case EM_860: |
| return "elf-intel-80860"; |
| case EM_MIPS: |
| return "elf-mips-r3000-be"; |
| case EM_S370: |
| return "elf-ibm-s370"; |
| case EM_MIPS_RS3_LE: |
| return "elf-mips-r3000-le"; |
| case EM_PARISC: |
| return "elf-hp-parisc"; |
| case EM_VPP500: |
| return "elf-fujitsu-vpp500"; |
| case EM_SPARC32PLUS: |
| return "elf-sun-sparc-v8plus"; |
| case EM_960: |
| return "elf-intel-80960"; |
| case EM_PPC: |
| return "elf-powerpc"; |
| case EM_PPC64: |
| return "elf-powerpc-64"; |
| case EM_S390: |
| return "elf-ibm-s390"; |
| case EM_V800: |
| return "elf-nec-v800"; |
| case EM_FR20: |
| return "elf-fujitsu-fr20"; |
| case EM_RH32: |
| return "elf-trw-rh32"; |
| case EM_RCE: |
| return "elf-motorola-rce"; |
| case EM_ARM: |
| return "elf-arm"; |
| case EM_FAKE_ALPHA: |
| return "elf-digital-alpha"; |
| case EM_SH: |
| return "elf-hitachi-sh"; |
| case EM_SPARCV9: |
| return "elf-sun-sparc-v9-64"; |
| case EM_TRICORE: |
| return "elf-siemens-tricore"; |
| case EM_ARC: |
| return "elf-argonaut-risc-core"; |
| case EM_H8_300: |
| return "elf-hitachi-h8-300"; |
| case EM_H8_300H: |
| return "elf-hitachi-h8-300h"; |
| case EM_H8S: |
| return "elf-hitachi-h8s"; |
| case EM_H8_500: |
| return "elf-hitachi-h8-500"; |
| case EM_IA_64: |
| return "elf-intel-ia-64"; |
| case EM_MIPS_X: |
| return "elf-stanford-mips-x"; |
| case EM_COLDFIRE: |
| return "elf-motorola-coldfire"; |
| case EM_68HC12: |
| return "elf-motorola-68hc12"; |
| case EM_MMA: |
| return "elf-fujitsu-mma"; |
| case EM_PCP: |
| return "elf-siemens-pcp"; |
| case EM_NCPU: |
| return "elf-sony-ncpu"; |
| case EM_NDR1: |
| return "elf-denso-ndr1"; |
| case EM_STARCORE: |
| return "elf-motorola-starcore"; |
| case EM_ME16: |
| return "elf-toyota-me16"; |
| case EM_ST100: |
| return "elf-stm-st100"; |
| case EM_TINYJ: |
| return "elf-alc-tinyj"; |
| case EM_X86_64: |
| return "elf-amd-x86_64"; |
| case EM_PDSP: |
| return "elf-sony-pdsp"; |
| case EM_FX66: |
| return "elf-siemens-fx66"; |
| case EM_ST9PLUS: |
| return "elf-stm-st9+"; |
| case EM_ST7: |
| return "elf-stm-st7"; |
| case EM_68HC16: |
| return "elf-motorola-68hc16"; |
| case EM_68HC11: |
| return "elf-motorola-68hc11"; |
| case EM_68HC08: |
| return "elf-motorola-68hc08"; |
| case EM_68HC05: |
| return "elf-motorola-68hc05"; |
| case EM_SVX: |
| return "elf-sg-svx"; |
| case EM_ST19: |
| return "elf-stm-st19"; |
| case EM_VAX: |
| return "elf-digital-vax"; |
| case EM_CRIS: |
| return "elf-axis-cris"; |
| case EM_JAVELIN: |
| return "elf-infineon-javelin"; |
| case EM_FIREPATH: |
| return "elf-firepath"; |
| case EM_ZSP: |
| return "elf-lsi-zsp"; |
| case EM_MMIX: |
| return "elf-don-knuth-mmix"; |
| case EM_HUANY: |
| return "elf-harvard-huany"; |
| case EM_PRISM: |
| return "elf-sitera-prism"; |
| case EM_AVR: |
| return "elf-atmel-avr"; |
| case EM_FR30: |
| return "elf-fujistu-fr30"; |
| case EM_D10V: |
| return "elf-mitsubishi-d10v"; |
| case EM_D30V: |
| return "elf-mitsubishi-d30v"; |
| case EM_V850: |
| return "elf-nec-v850"; |
| case EM_M32R: |
| return "elf-mitsubishi-m32r"; |
| case EM_MN10300: |
| return "elf-matsushita-mn10300"; |
| case EM_MN10200: |
| return "elf-matsushita-mn10200"; |
| case EM_PJ: |
| return "elf-picojava"; |
| case EM_OPENRISC: |
| return "elf-openrisc-32"; |
| case EM_ARC_A5: |
| return "elf-arc-a5"; |
| case EM_XTENSA: |
| return "elf-tensilica-xtensa"; |
| |
| #ifdef HAVE_EM_AARCH64_MACRO |
| case EM_AARCH64: |
| return "elf-arm-aarch64"; |
| #endif |
| |
| #ifdef HAVE_EM_TILEPRO_MACRO |
| case EM_TILEPRO: |
| return "elf-tilera-tilepro"; |
| #endif |
| |
| #ifdef HAVE_EM_TILEGX_MACRO |
| case EM_TILEGX: |
| return "elf-tilera-tilegx"; |
| #endif |
| |
| case EM_NUM: |
| return "elf-last-arch-number"; |
| case EM_ALPHA: |
| return "elf-non-official-alpha"; |
| default: |
| { |
| std::ostringstream o; |
| o << "elf-unknown-arch-value-" << e_machine; |
| return o.str(); |
| } |
| } |
| } |
| |
| /// Find and return a section by its name and its type. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param name the name of the section. |
| /// |
| /// @param section_type the type of the section. This is the |
| /// Elf32_Shdr::sh_type (or Elf64_Shdr::sh_type) data member. |
| /// Examples of values of this parameter are SHT_PROGBITS or SHT_NOBITS. |
| /// |
| /// @return the section found, nor nil if none was found. |
| Elf_Scn* |
| find_section(Elf* elf_handle, const std::string& name, Elf64_Word section_type) |
| { |
| size_t section_header_string_index = 0; |
| if (elf_getshdrstrndx (elf_handle, §ion_header_string_index) < 0) |
| return 0; |
| |
| Elf_Scn* section = 0; |
| GElf_Shdr header_mem, *header; |
| while ((section = elf_nextscn(elf_handle, section)) != 0) |
| { |
| header = gelf_getshdr(section, &header_mem); |
| if (header == NULL || header->sh_type != section_type) |
| continue; |
| |
| const char* section_name = |
| elf_strptr(elf_handle, section_header_string_index, header->sh_name); |
| if (section_name && name == section_name) |
| return section; |
| } |
| |
| return 0; |
| } |
| |
| /// Find the symbol table. |
| /// |
| /// If we are looking at a relocatable or executable file, this |
| /// function will return the .symtab symbol table (of type |
| /// SHT_SYMTAB). But if we are looking at a DSO it returns the |
| /// .dynsym symbol table (of type SHT_DYNSYM). |
| /// |
| /// @param elf_handle the elf handle to consider. |
| /// |
| /// @param symtab the symbol table found. |
| /// |
| /// @return the symbol table section |
| Elf_Scn* |
| find_symbol_table_section(Elf* elf_handle) |
| { |
| Elf_Scn* section = 0, *dynsym = 0, *sym_tab = 0; |
| while ((section = elf_nextscn(elf_handle, section)) != 0) |
| { |
| GElf_Shdr header_mem, *header; |
| header = gelf_getshdr(section, &header_mem); |
| if (header->sh_type == SHT_DYNSYM) |
| dynsym = section; |
| else if (header->sh_type == SHT_SYMTAB) |
| sym_tab = section; |
| } |
| |
| if (dynsym || sym_tab) |
| { |
| GElf_Ehdr eh_mem; |
| GElf_Ehdr* elf_header = gelf_getehdr(elf_handle, &eh_mem); |
| if (elf_header->e_type == ET_REL |
| || elf_header->e_type == ET_EXEC) |
| return sym_tab ? sym_tab : dynsym; |
| else |
| return dynsym ? dynsym : sym_tab; |
| } |
| return NULL; |
| } |
| |
| /// Find the index (in the section headers table) of the symbol table |
| /// section. |
| /// |
| /// If we are looking at a relocatable or executable file, this |
| /// function will return the index for the .symtab symbol table (of |
| /// type SHT_SYMTAB). But if we are looking at a DSO it returns the |
| /// index for the .dynsym symbol table (of type SHT_DYNSYM). |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param symtab_index the index of the symbol_table, that was found. |
| /// |
| /// @return true iff the symbol table section index was found. |
| bool |
| find_symbol_table_section_index(Elf* elf_handle, size_t& symtab_index) |
| { |
| Elf_Scn* section = find_symbol_table_section(elf_handle); |
| |
| if (!section) |
| return false; |
| |
| symtab_index = elf_ndxscn(section); |
| return true; |
| } |
| |
| /// Get the offset offset of the hash table section. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param ht_section_offset this is set to the resulting offset |
| /// of the hash table section. This is set iff the function returns true. |
| /// |
| /// @param symtab_section_offset the offset of the section of the |
| /// symbol table the hash table refers to. |
| hash_table_kind |
| find_hash_table_section_index(Elf* elf_handle, |
| size_t& ht_section_index, |
| size_t& symtab_section_index) |
| { |
| if (!elf_handle) |
| return NO_HASH_TABLE_KIND; |
| |
| GElf_Shdr header_mem, *section_header; |
| bool found_sysv_ht = false, found_gnu_ht = false; |
| for (Elf_Scn* section = elf_nextscn(elf_handle, 0); |
| section != 0; |
| section = elf_nextscn(elf_handle, section)) |
| { |
| section_header= gelf_getshdr(section, &header_mem); |
| if (section_header->sh_type != SHT_HASH |
| && section_header->sh_type != SHT_GNU_HASH) |
| continue; |
| |
| ht_section_index = elf_ndxscn(section); |
| symtab_section_index = section_header->sh_link; |
| |
| if (section_header->sh_type == SHT_HASH) |
| found_sysv_ht = true; |
| else if (section_header->sh_type == SHT_GNU_HASH) |
| found_gnu_ht = true; |
| } |
| |
| if (found_gnu_ht) |
| return GNU_HASH_TABLE_KIND; |
| else if (found_sysv_ht) |
| return SYSV_HASH_TABLE_KIND; |
| else |
| return NO_HASH_TABLE_KIND; |
| } |
| |
| /// Find and return the .text section. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @return the .text section found. |
| Elf_Scn* |
| find_text_section(Elf* elf_handle) |
| {return find_section(elf_handle, ".text", SHT_PROGBITS);} |
| |
| /// Find and return the .bss section. |
| /// |
| /// @param elf_handle. |
| /// |
| /// @return the .bss section found. |
| Elf_Scn* |
| find_bss_section(Elf* elf_handle) |
| {return find_section(elf_handle, ".bss", SHT_NOBITS);} |
| |
| /// Find and return the .rodata section. |
| /// |
| /// @param elf_handle. |
| /// |
| /// @return the .rodata section found. |
| Elf_Scn* |
| find_rodata_section(Elf* elf_handle) |
| {return find_section(elf_handle, ".rodata", SHT_PROGBITS);} |
| |
| /// Find and return the .data section. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @return the .data section found. |
| Elf_Scn* |
| find_data_section(Elf* elf_handle) |
| {return find_section(elf_handle, ".data", SHT_PROGBITS);} |
| |
| /// Find and return the .data1 section. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @return the .data1 section found. |
| Elf_Scn* |
| find_data1_section(Elf* elf_handle) |
| {return find_section(elf_handle, ".data1", SHT_PROGBITS);} |
| |
| /// Return the "Official Procedure descriptors section." This |
| /// section is named .opd, and is usually present only on PPC64 |
| /// ELFv1 binaries. |
| /// |
| /// @param elf_handle the elf handle to consider. |
| /// |
| /// @return the .opd section, if found. Return nil otherwise. |
| Elf_Scn* |
| find_opd_section(Elf* elf_handle) |
| {return find_section(elf_handle, ".opd", SHT_PROGBITS);} |
| |
| /// Return the SHT_GNU_versym, SHT_GNU_verdef and SHT_GNU_verneed |
| /// sections that are involved in symbol versionning. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param versym_section the SHT_GNU_versym section found. If the |
| /// section wasn't found, this is set to nil. |
| /// |
| /// @param verdef_section the SHT_GNU_verdef section found. If the |
| /// section wasn't found, this is set to nil. |
| /// |
| /// @param verneed_section the SHT_GNU_verneed section found. If the |
| /// section wasn't found, this is set to nil. |
| /// |
| /// @return true iff at least one of the sections where found. |
| bool |
| get_symbol_versionning_sections(Elf* elf_handle, |
| Elf_Scn*& versym_section, |
| Elf_Scn*& verdef_section, |
| Elf_Scn*& verneed_section) |
| { |
| Elf_Scn* section = NULL; |
| GElf_Shdr mem; |
| Elf_Scn* versym = NULL, *verdef = NULL, *verneed = NULL; |
| |
| while ((section = elf_nextscn(elf_handle, section)) != NULL) |
| { |
| GElf_Shdr* h = gelf_getshdr(section, &mem); |
| if (h->sh_type == SHT_GNU_versym) |
| versym = section; |
| else if (h->sh_type == SHT_GNU_verdef) |
| verdef = section; |
| else if (h->sh_type == SHT_GNU_verneed) |
| verneed = section; |
| } |
| |
| if (versym || verdef || verneed) |
| { |
| // At least one the versionning sections was found. Return it. |
| versym_section = versym; |
| verdef_section = verdef; |
| verneed_section = verneed; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Return the __ksymtab section of a linux kernel ELF file (either |
| /// a vmlinux binary or a kernel module). |
| /// |
| /// @param elf_handle the elf handle to consider. |
| /// |
| /// @return the __ksymtab section if found, nil otherwise. |
| Elf_Scn* |
| find_ksymtab_section(Elf* elf_handle) |
| {return find_section(elf_handle, "__ksymtab", SHT_PROGBITS);} |
| |
| /// Return the __ksymtab_gpl section of a linux kernel ELF file (either |
| /// a vmlinux binary or a kernel module). |
| /// |
| /// @param elf_handle the elf handle to consider. |
| /// |
| /// @return the __ksymtab section if found, nil otherwise. |
| Elf_Scn* |
| find_ksymtab_gpl_section(Elf* elf_handle) |
| {return find_section(elf_handle, "__ksymtab_gpl", SHT_PROGBITS);} |
| |
| /// Find the __ksymtab_strings section of a Linux kernel binary. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @return the find_ksymtab_strings_section of the linux kernel |
| /// binary denoted by @p elf_handle, or nil if such a section could |
| /// not be found. |
| Elf_Scn* |
| find_ksymtab_strings_section(Elf *elf_handle) |
| { |
| if (is_linux_kernel(elf_handle)) |
| return find_section(elf_handle, "__ksymtab_strings", SHT_PROGBITS); |
| return 0; |
| } |
| |
| /// Return the .rel{a,} section corresponding to a given section. |
| /// |
| /// @param elf_handle the elf handle to consider. |
| /// |
| /// @param target_section the section to search the relocation section for |
| /// |
| /// @return the .rel{a,} section if found, null otherwise. |
| Elf_Scn* |
| find_relocation_section(Elf* elf_handle, Elf_Scn* target_section) |
| { |
| if (target_section) |
| { |
| // the relo section we are searching for has this index as sh_info |
| size_t target_index = elf_ndxscn(target_section); |
| |
| // now iterate over all the sections, look for relocation sections and |
| // find the one that points to the section we are searching for |
| Elf_Scn* section = 0; |
| GElf_Shdr header_mem, *header; |
| while ((section = elf_nextscn(elf_handle, section)) != 0) |
| { |
| header = gelf_getshdr(section, &header_mem); |
| if (header == NULL |
| || (header->sh_type != SHT_RELA && header->sh_type != SHT_REL)) |
| continue; |
| |
| if (header->sh_info == target_index) |
| return section; |
| } |
| } |
| return NULL; |
| } |
| |
| /// Get the version definition (from the SHT_GNU_verdef section) of a |
| /// given symbol represented by a pointer to GElf_Versym. |
| /// |
| /// @param elf_hande the elf handle to use. |
| /// |
| /// @param versym the symbol to get the version definition for. |
| /// |
| /// @param verdef_section the SHT_GNU_verdef section. |
| /// |
| /// @param version the resulting version definition. This is set iff |
| /// the function returns true. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| bool |
| get_version_definition_for_versym(Elf* elf_handle, |
| GElf_Versym* versym, |
| Elf_Scn* verdef_section, |
| elf_symbol::version& version) |
| { |
| Elf_Data* verdef_data = elf_getdata(verdef_section, NULL); |
| GElf_Verdef verdef_mem; |
| GElf_Verdef* verdef = gelf_getverdef(verdef_data, 0, &verdef_mem); |
| size_t vd_offset = 0; |
| |
| for (;; vd_offset += verdef->vd_next) |
| { |
| for (;verdef != 0;) |
| { |
| if (verdef->vd_ndx == (*versym & 0x7fff)) |
| // Found the version of the symbol. |
| break; |
| vd_offset += verdef->vd_next; |
| verdef = (verdef->vd_next == 0 |
| ? 0 |
| : gelf_getverdef(verdef_data, vd_offset, &verdef_mem)); |
| } |
| |
| if (verdef != 0) |
| { |
| GElf_Verdaux verdaux_mem; |
| GElf_Verdaux *verdaux = gelf_getverdaux(verdef_data, |
| vd_offset + verdef->vd_aux, |
| &verdaux_mem); |
| GElf_Shdr header_mem; |
| GElf_Shdr* verdef_section_header = gelf_getshdr(verdef_section, |
| &header_mem); |
| size_t verdef_stridx = verdef_section_header->sh_link; |
| version.str(elf_strptr(elf_handle, verdef_stridx, verdaux->vda_name)); |
| if (*versym & 0x8000) |
| version.is_default(false); |
| else |
| version.is_default(true); |
| return true; |
| } |
| if (!verdef || verdef->vd_next == 0) |
| break; |
| } |
| return false; |
| } |
| |
| /// Get the version needed (from the SHT_GNU_verneed section) to |
| /// resolve an undefined symbol represented by a pointer to |
| /// GElf_Versym. |
| /// |
| /// @param elf_hande the elf handle to use. |
| /// |
| /// @param versym the symbol to get the version definition for. |
| /// |
| /// @param verneed_section the SHT_GNU_verneed section. |
| /// |
| /// @param version the resulting version definition. This is set iff |
| /// the function returns true. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| bool |
| get_version_needed_for_versym(Elf* elf_handle, |
| GElf_Versym* versym, |
| Elf_Scn* verneed_section, |
| elf_symbol::version& version) |
| { |
| if (versym == 0 || elf_handle == 0 || verneed_section == 0) |
| return false; |
| |
| size_t vn_offset = 0; |
| Elf_Data* verneed_data = elf_getdata(verneed_section, NULL); |
| GElf_Verneed verneed_mem; |
| GElf_Verneed* verneed = gelf_getverneed(verneed_data, 0, &verneed_mem); |
| |
| for (;verneed; vn_offset += verneed->vn_next) |
| { |
| size_t vna_offset = vn_offset; |
| GElf_Vernaux vernaux_mem; |
| GElf_Vernaux *vernaux = gelf_getvernaux(verneed_data, |
| vn_offset + verneed->vn_aux, |
| &vernaux_mem); |
| for (;vernaux != 0 && verneed;) |
| { |
| if (vernaux->vna_other == *versym) |
| // Found the version of the symbol. |
| break; |
| vna_offset += verneed->vn_next; |
| verneed = (verneed->vn_next == 0 |
| ? 0 |
| : gelf_getverneed(verneed_data, vna_offset, &verneed_mem)); |
| } |
| |
| if (verneed != 0 && vernaux != 0 && vernaux->vna_other == *versym) |
| { |
| GElf_Shdr header_mem; |
| GElf_Shdr* verneed_section_header = gelf_getshdr(verneed_section, |
| &header_mem); |
| size_t verneed_stridx = verneed_section_header->sh_link; |
| version.str(elf_strptr(elf_handle, |
| verneed_stridx, |
| vernaux->vna_name)); |
| if (*versym & 0x8000) |
| version.is_default(false); |
| else |
| version.is_default(true); |
| return true; |
| } |
| |
| if (!verneed || verneed->vn_next == 0) |
| break; |
| } |
| return false; |
| } |
| |
| /// Return the version for a symbol that is at a given index in its |
| /// SHT_SYMTAB section. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param symbol_index the index of the symbol to consider. |
| /// |
| /// @param get_def_version if this is true, it means that that we want |
| /// the version for a defined symbol; in that case, the version is |
| /// looked for in a section of type SHT_GNU_verdef. Otherwise, if |
| /// this parameter is false, this means that we want the version for |
| /// an undefined symbol; in that case, the version is the needed one |
| /// for the symbol to be resolved; so the version is looked fo in a |
| /// section of type SHT_GNU_verneed. |
| /// |
| /// @param version the version found for symbol at @p symbol_index. |
| /// |
| /// @return true iff a version was found for symbol at index @p |
| /// symbol_index. |
| bool |
| get_version_for_symbol(Elf* elf_handle, |
| size_t symbol_index, |
| bool get_def_version, |
| elf_symbol::version& version) |
| { |
| Elf_Scn *versym_section = NULL, |
| *verdef_section = NULL, |
| *verneed_section = NULL; |
| |
| if (!get_symbol_versionning_sections(elf_handle, |
| versym_section, |
| verdef_section, |
| verneed_section)) |
| return false; |
| |
| GElf_Versym versym_mem; |
| Elf_Data* versym_data = (versym_section) |
| ? elf_getdata(versym_section, NULL) |
| : NULL; |
| GElf_Versym* versym = (versym_data) |
| ? gelf_getversym(versym_data, symbol_index, &versym_mem) |
| : NULL; |
| |
| if (versym == 0 || *versym <= 1) |
| // I got these value from the code of readelf.c in elfutils. |
| // Apparently, if the symbol version entry has these values, the |
| // symbol must be discarded. This is not documented in the |
| // official specification. |
| return false; |
| |
| if (get_def_version) |
| { |
| if (*versym == 0x8001) |
| // I got this value from the code of readelf.c in elfutils |
| // too. It's not really documented in the official |
| // specification. |
| return false; |
| |
| if (verdef_section |
| && get_version_definition_for_versym(elf_handle, versym, |
| verdef_section, version)) |
| return true; |
| } |
| else |
| { |
| if (verneed_section |
| && get_version_needed_for_versym(elf_handle, versym, |
| verneed_section, version)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Test if the architecture of the current binary is ppc64. |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return true iff the architecture of the current binary is ppc64. |
| bool |
| architecture_is_ppc64(Elf* elf_handle) |
| { |
| GElf_Ehdr eh_mem; |
| GElf_Ehdr* elf_header = gelf_getehdr(elf_handle, &eh_mem); |
| return (elf_header && elf_header->e_machine == EM_PPC64); |
| } |
| |
| /// Test if the endianness of the current binary is Big Endian. |
| /// |
| /// https://en.wikipedia.org/wiki/Endianness. |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return true iff the current binary is Big Endian. |
| bool |
| architecture_is_big_endian(Elf* elf_handle) |
| { |
| GElf_Ehdr elf_header; |
| gelf_getehdr(elf_handle, &elf_header); |
| |
| bool is_big_endian = (elf_header.e_ident[EI_DATA] == ELFDATA2MSB); |
| |
| if (!is_big_endian) |
| ABG_ASSERT(elf_header.e_ident[EI_DATA] == ELFDATA2LSB); |
| |
| return is_big_endian; |
| } |
| |
| /// Test if the ELF binary denoted by a given ELF handle is a Linux |
| /// Kernel Module. |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return true iff the binary denoted by @p elf_handle is a Linux |
| /// kernel module. |
| bool |
| is_linux_kernel_module(Elf *elf_handle) |
| { |
| return (find_section(elf_handle, ".modinfo", SHT_PROGBITS) |
| && find_section(elf_handle, |
| ".gnu.linkonce.this_module", |
| SHT_PROGBITS)); |
| } |
| |
| /// Test if the ELF binary denoted by a given ELF handle is a Linux |
| /// Kernel binary (either vmlinux or a kernel module). |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return true iff the binary denoted by @p elf_handle is a Linux |
| /// kernel binary |
| bool |
| is_linux_kernel(Elf *elf_handle) |
| { |
| return (find_section(elf_handle, |
| "__ksymtab_strings", |
| SHT_PROGBITS) |
| || is_linux_kernel_module(elf_handle)); |
| } |
| |
| /// Get the address at which a given binary is loaded in memory. |
| /// |
| /// @param elf_handle the elf handle for the binary to consider. |
| /// |
| /// @param load_address the address where the binary is loaded. This |
| /// is set by the function iff it returns true. |
| /// |
| /// @return true if the function could get the binary load address |
| /// and assign @p load_address to it. |
| bool |
| get_binary_load_address(Elf* elf_handle, GElf_Addr& load_address) |
| { |
| GElf_Ehdr elf_header; |
| gelf_getehdr(elf_handle, &elf_header); |
| size_t num_segments = elf_header.e_phnum; |
| GElf_Phdr *program_header = NULL; |
| GElf_Addr result; |
| bool found_loaded_segment = false; |
| GElf_Phdr ph_mem; |
| |
| for (unsigned i = 0; i < num_segments; ++i) |
| { |
| program_header = gelf_getphdr(elf_handle, i, &ph_mem); |
| if (program_header && program_header->p_type == PT_LOAD) |
| { |
| if (!found_loaded_segment) |
| { |
| result = program_header->p_vaddr; |
| found_loaded_segment = true; |
| } |
| |
| if (program_header->p_vaddr < result) |
| // The resulting load address we want is the lowest |
| // load address of all the loaded segments. |
| result = program_header->p_vaddr; |
| } |
| } |
| |
| if (found_loaded_segment) |
| { |
| load_address = result; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Return the size of a word for the current architecture. |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return the size of a word. |
| unsigned char |
| get_architecture_word_size(Elf* elf_handle) |
| { |
| unsigned char word_size = 0; |
| GElf_Ehdr elf_header; |
| gelf_getehdr(elf_handle, &elf_header); |
| if (elf_header.e_ident[EI_CLASS] == ELFCLASS32) |
| word_size = 4; |
| else if (elf_header.e_ident[EI_CLASS] == ELFCLASS64) |
| word_size = 8; |
| else |
| ABG_ASSERT_NOT_REACHED; |
| return word_size; |
| } |
| |
| /// Test if the elf file being read is an executable. |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return true iff the elf file being read is an / executable. |
| bool |
| is_executable(Elf* elf_handle) |
| { |
| GElf_Ehdr elf_header; |
| gelf_getehdr(elf_handle, &elf_header); |
| return elf_header.e_type == ET_EXEC; |
| } |
| |
| /// Test if the elf file being read is a dynamic shared / object. |
| /// |
| /// @param elf_handle the ELF handle to consider. |
| /// |
| /// @return true iff the elf file being read is a / dynamic shared object. |
| bool |
| is_dso(Elf* elf_handle) |
| { |
| GElf_Ehdr elf_header; |
| gelf_getehdr(elf_handle, &elf_header); |
| return elf_header.e_type == ET_DYN; |
| } |
| |
| /// Translate a section-relative symbol address (i.e, symbol value) |
| /// into an absolute symbol address by adding the address of the |
| /// section the symbol belongs to, to the address value. |
| /// |
| /// This is useful when looking at symbol values coming from |
| /// relocatable files (of ET_REL kind). If the binary is not |
| /// ET_REL, then the function does nothing and returns the input |
| /// address unchanged. |
| /// |
| /// @param elf_handle the elf handle for the binary to consider. |
| /// |
| /// @param sym the symbol whose address to possibly needs to be |
| /// translated. |
| /// |
| /// @return the section-relative address, translated into an |
| /// absolute address, if @p sym is from an ET_REL binary. |
| /// Otherwise, return the address of @p sym, unchanged. |
| GElf_Addr |
| maybe_adjust_et_rel_sym_addr_to_abs_addr(Elf* elf_handle, GElf_Sym* sym) |
| { |
| Elf_Scn* symbol_section = elf_getscn(elf_handle, sym->st_shndx); |
| GElf_Addr addr = sym->st_value; |
| |
| if (!symbol_section) |
| return addr; |
| |
| GElf_Ehdr elf_header; |
| if (!gelf_getehdr(elf_handle, &elf_header)) |
| return addr; |
| |
| if (elf_header.e_type != ET_REL) |
| return addr; |
| |
| GElf_Shdr section_header; |
| if (!gelf_getshdr(symbol_section, §ion_header)) |
| return addr; |
| |
| return addr + section_header.sh_addr; |
| } |
| |
| } // end namespace elf_helpers |
| } // end namespace abigail |