From patchwork Mon Apr 20 11:08:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Matthias_M=C3=A4nnich?= X-Patchwork-Id: 39097 From: maennich@google.com (Matthias Maennich) Date: Mon, 20 Apr 2020 13:08:41 +0200 Subject: [PATCH 3/8] abg-elf-helpers: move some versioning helpers from abg-dwarf-reader In-Reply-To: <20200420110846.218792-1-maennich@google.com> References: <20200420110846.218792-1-maennich@google.com> Message-ID: <20200420110846.218792-4-maennich@google.com> Move some definitions from abg-dwarf-reader to abg-elf-helpers that are strictly only related to ELF. * abg-dwarf-reader.cc(get_symbol_versionning_sections): Move function out. (get_version_definition_for_versym): Likewise. (get_version_needed_for_versym): Likewise. (get_version_for_symbol): Likewise. * abg-elf-helpers.cc(get_symbol_versionning_sections): Move function in. (get_version_definition_for_versym): Likewise. (get_version_needed_for_versym): Likewise. (get_version_for_symbol): Likewise. * abg-elf-helpers.cc(get_symbol_versionning_sections): Add declaration. (get_version_definition_for_versym): Likewise. (get_version_needed_for_versym): Likewise. (get_version_for_symbol): Likewise. Signed-off-by: Matthias Maennich Reviewed-by: Giuliano Procida --- src/abg-dwarf-reader.cc | 256 ---------------------------------------- src/abg-elf-helpers.cc | 251 +++++++++++++++++++++++++++++++++++++++ src/abg-elf-helpers.h | 28 +++++ 3 files changed, 279 insertions(+), 256 deletions(-) diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc index 6ec7e4b6e968..303c1f8df4c2 100644 --- a/src/abg-dwarf-reader.cc +++ b/src/abg-dwarf-reader.cc @@ -294,12 +294,6 @@ static void add_symbol_to_map(const elf_symbol_sptr& sym, string_elf_symbols_map_type& map); -static bool -get_symbol_versionning_sections(Elf* elf_handle, - Elf_Scn*& versym_section, - Elf_Scn*& verdef_section, - Elf_Scn*& verneed_section); - static bool get_parent_die(const read_context& ctxt, const Dwarf_Die* die, @@ -867,256 +861,6 @@ compare_symbol_name(const string& symbol_name, return symbol_name == name; } -/// 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. -static 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; -} - -/// 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. -static 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. -static 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. -static 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; -} - /// Lookup a symbol using the SysV ELF hash table. /// /// Note that this function hasn't been tested. So it hasn't been diff --git a/src/abg-elf-helpers.cc b/src/abg-elf-helpers.cc index b0113a4efd2c..b7b868a332ec 100644 --- a/src/abg-elf-helpers.cc +++ b/src/abg-elf-helpers.cc @@ -500,6 +500,257 @@ Elf_Scn* find_data1_section(Elf* elf_handle) {return find_section(elf_handle, ".data1", 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; +} + +/// 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; +} + + } // end namespace elf_helpers } // end namespace abigail diff --git a/src/abg-elf-helpers.h b/src/abg-elf-helpers.h index 58720da0fa9e..7e1c231ccc4e 100644 --- a/src/abg-elf-helpers.h +++ b/src/abg-elf-helpers.h @@ -96,6 +96,34 @@ find_data_section(Elf* elf_handle); Elf_Scn* find_data1_section(Elf* elf_handle); +bool +get_symbol_versionning_sections(Elf* elf_handle, + Elf_Scn*& versym_section, + Elf_Scn*& verdef_section, + Elf_Scn*& verneed_section); + +// +// Helpers for symbol versioning +// + +bool +get_version_definition_for_versym(Elf* elf_handle, + GElf_Versym* versym, + Elf_Scn* verdef_section, + elf_symbol::version& version); + +bool +get_version_needed_for_versym(Elf* elf_handle, + GElf_Versym* versym, + Elf_Scn* verneed_section, + elf_symbol::version& version); + +bool +get_version_for_symbol(Elf* elf_handle, + size_t symbol_index, + bool get_def_version, + elf_symbol::version& version); + } // end namespace elf_helpers } // end namespace abigail