diff options
author | Matthias Maennich <maennich@google.com> | 2020-05-12 00:29:19 +0200 |
---|---|---|
committer | Dodji Seketeli <dodji@redhat.com> | 2021-04-02 15:29:38 +0200 |
commit | e87f2672d94403f3b9e5459a6a5c0dfd949e0c13 (patch) | |
tree | 3a924d56569b4154d13eb63f6d082f4ebe1642bf | |
parent | 9cd191b3c41dcc2f9f3e93d8b74f05fd0e85a124 (diff) | |
download | libabigail-e87f2672d94403f3b9e5459a6a5c0dfd949e0c13.tar.gz |
abg-elf-helpers: migrate ppc64 specific helpers
This migrates more helpers to abg-elf-helpers:
lookup_ppc64_elf_fn_entry_point_address with dependencies
read_uint64_from_array_of_bytes
read_int_from_array_of_bytes
address_is_in_opd_section with dependency
address_is_in_section
read_context::find_opd_section and read_context::opd_section_ are obsolete.
* src/abg-dwarf-reader.cc (read_context::opd_section_): Delete.
(read_context::find_opd_section): Delete.
(read_context::read_uint64_from_array_of_bytes): Delete.
(read_context::read_int_from_array_of_bytes): Delete.
(read_context::lookup_ppc64_elf_fn_entry_point_address): Delete.
(read_context::address_is_in_opd_section): Delete.
(read_context::address_is_in_section): Delete.
(read_context::load_symbol_maps_from_symtab_section): Adjust.
* src/abg-elf-helpers.cc (read_int_from_array_of_bytes): New.
(read_uint64_from_array_of_bytes): New.
(lookup_ppc64_elf_fn_entry_point_address): New.
(address_is_in_section): New.
(address_is_in_opd_section): New.
* src/abg-elf-helpers.h
(lookup_ppc64_elf_fn_entry_point_address): New declaration.
(address_is_in_opd_section): New declaration.
Reviewed-by: Giuliano Procida <gprocida@google.com>
Reviewed-by: Dodji Seketeli <dodji@seketeli.org>
Signed-off-by: Matthias Maennich <maennich@google.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
-rw-r--r-- | src/abg-dwarf-reader.cc | 208 | ||||
-rw-r--r-- | src/abg-elf-helpers.cc | 188 | ||||
-rw-r--r-- | src/abg-elf-helpers.h | 8 |
3 files changed, 200 insertions, 204 deletions
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc index cf37151e..02e0ddac 100644 --- a/src/abg-dwarf-reader.cc +++ b/src/abg-dwarf-reader.cc @@ -2146,10 +2146,6 @@ public: mutable Elf* elf_handle_; string elf_path_; mutable Elf_Scn* symtab_section_; - // The "Official procedure descriptor section, aka .opd", used in - // ppc64 elf v1 binaries. This section contains the procedure - // descriptors on that platform. - mutable Elf_Scn* opd_section_; Dwarf_Die* cur_tu_die_; mutable dwarf_expr_eval_context dwarf_expr_eval_context_; // A set of maps (one per kind of die source) that associates a decl @@ -2330,7 +2326,6 @@ public: elf_handle_ = 0; elf_path_ = elf_path; symtab_section_ = 0; - opd_section_ = 0; cur_tu_die_ = 0; exported_decls_builder_ = 0; @@ -5323,19 +5318,6 @@ public: return symtab_section_; } - /// Return the "Official Procedure descriptors section." This - /// section is named .opd, and is usually present only on PPC64 - /// ELFv1 binaries. - /// - /// @return the .opd section, if found. Return nil otherwise. - Elf_Scn* - find_opd_section() const - { - if (!opd_section_) - opd_section_ = elf_helpers::find_opd_section(elf_handle()); - return opd_section_; - } - /// Lookup an elf symbol, referred to by its index, from the .symtab /// section. /// @@ -5447,152 +5429,6 @@ public: return sym; } - /// Read 8 bytes and convert their value into an uint64_t. - /// - /// @param bytes the array of bytes to read the next 8 bytes from. - /// Note that this array must be at least 8 bytes long. - /// - /// @param result where to store the resuting uint64_t that was read. - /// - /// @param is_big_endian if true, read the 8 bytes in Big Endian - /// mode, otherwise, read them in Little Endian. - /// - /// @param true if the 8 bytes could be read, false otherwise. - bool - read_uint64_from_array_of_bytes(const uint8_t *bytes, - bool is_big_endian, - uint64_t &result) const - { - return read_int_from_array_of_bytes(bytes, 8, is_big_endian, result); - } - - /// Read N bytes and convert their value into an integer type T. - /// - /// Note that N cannot be bigger than 8 for now. The type passed needs to be - /// at least of the size of number_of_bytes. - /// - /// @param bytes the array of bytes to read the next 8 bytes from. - /// Note that this array must be at least 8 bytes long. - /// - /// @param number_of_bytes the number of bytes to read. This number - /// cannot be bigger than 8. - /// - /// @param is_big_endian if true, read the 8 bytes in Big Endian - /// mode, otherwise, read them in Little Endian. - /// - /// @param result where to store the resuting integer that was read. - /// - /// - /// @param true if the 8 bytes could be read, false otherwise. - template<typename T> - bool - read_int_from_array_of_bytes(const uint8_t *bytes, - unsigned char number_of_bytes, - bool is_big_endian, - T &result) const - { - if (!bytes) - return false; - - ABG_ASSERT(number_of_bytes <= 8); - ABG_ASSERT(number_of_bytes <= sizeof(T)); - - T res = 0; - - const uint8_t *cur = bytes; - if (is_big_endian) - { - // In Big Endian, the most significant byte is at the lowest - // address. - const uint8_t* msb = cur; - res = *msb; - - // Now read the remaining least significant bytes. - for (uint i = 1; i < number_of_bytes; ++i) - res = (res << 8) | ((T)msb[i]); - } - else - { - // In Little Endian, the least significant byte is at the - // lowest address. - const uint8_t* lsb = cur; - res = *lsb; - // Now read the remaining most significant bytes. - for (uint i = 1; i < number_of_bytes; ++i) - res = res | (((T)lsb[i]) << i * 8); - } - - result = res; - return true; - } - - /// Lookup the address of the function entry point that corresponds - /// to the address of a given function descriptor. - /// - /// On PPC64, a function pointer is the address of a function - /// descriptor. Function descriptors are located in the .opd - /// section. Each function descriptor is a triplet of three - /// addresses, each one on 64 bits. Among those three address only - /// the first one is of any interest to us: the address of the entry - /// point of the function. - /// - /// This function returns the address of the entry point of the - /// function whose descriptor's address is given. - /// - /// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-DES - /// - /// https://www.ibm.com/developerworks/community/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/deeply_understand_64_bit_powerpc_elf_abi_function_descriptors?lang=en - /// - /// @param fn_desc_address the address of the function descriptor to - /// consider. - /// - /// @return the address of the entry point of the function whose - /// descriptor has the address @p fn_desc_address. If there is no - /// .opd section (e.g because we are not on ppc64) or more generally - /// if the function descriptor could not be found then this function - /// just returns the address of the fuction descriptor. - GElf_Addr - lookup_ppc64_elf_fn_entry_point_address(GElf_Addr fn_desc_address) const - { - if (!elf_handle()) - return fn_desc_address; - - if (!architecture_is_ppc64(elf_handle())) - return fn_desc_address; - - bool is_big_endian = architecture_is_big_endian(elf_handle()); - - Elf_Scn *opd_section = find_opd_section(); - if (!opd_section) - return fn_desc_address; - - GElf_Shdr header_mem; - // The section header of the .opd section. - GElf_Shdr *opd_sheader = gelf_getshdr(opd_section, &header_mem); - - // The offset of the function descriptor entry, in the .opd - // section. - size_t fn_desc_offset = fn_desc_address - opd_sheader->sh_addr; - Elf_Data *elf_data = elf_rawdata(opd_section, 0); - - // Ensure that the opd_section has at least 8 bytes, starting from - // the offset we want read the data from. - if (elf_data->d_size <= fn_desc_offset + 8) - return fn_desc_address; - - // A pointer to the data of the .opd section, that we can actually - // do something with. - uint8_t * bytes = (uint8_t*) elf_data->d_buf; - - // The resulting address we are looking for is going to be formed - // in this variable. - GElf_Addr result = 0; - ABG_ASSERT(read_uint64_from_array_of_bytes(bytes + fn_desc_offset, - is_big_endian, result)); - - return result; - } - /// Test if a given function symbol has been exported. /// /// @param symbol_address the address of the symbol we are looking @@ -5911,13 +5747,15 @@ public: // symbol that are in the .opd section. GElf_Addr fn_desc_addr = sym->st_value; GElf_Addr fn_entry_point_addr = - lookup_ppc64_elf_fn_entry_point_address(fn_desc_addr); + lookup_ppc64_elf_fn_entry_point_address(elf_handle(), + fn_desc_addr); addr_elf_symbol_sptr_map_type::const_iterator it2 = fun_entry_addr_sym_map().find(fn_entry_point_addr); if (it2 == fun_entry_addr_sym_map().end()) fun_entry_addr_sym_map()[fn_entry_point_addr] = symbol; - else if (address_is_in_opd_section(fn_desc_addr)) + else if (address_is_in_opd_section(elf_handle(), + fn_desc_addr)) { // Either // @@ -6080,24 +5918,6 @@ public: return true; } - /// Return true if an address is in the ".opd" section that is - /// present on the ppc64 platform. - /// - /// @param addr the address to consider. - /// - /// @return true iff @p addr is designates a word that is in the - /// ".opd" section. - bool - address_is_in_opd_section(Dwarf_Addr addr) - { - Elf_Scn * opd_section = find_opd_section(); - if (!opd_section) - return false; - if (address_is_in_section(addr, opd_section)) - return true; - return false; - } - /// Load the symbol maps if necessary. /// /// @return true iff the symbol maps has been loaded by this @@ -6248,26 +6068,6 @@ public: return addr; } - /// Test if a given address is in a given section. - /// - /// @param addr the address to consider. - /// - /// @param section the section to consider. - bool - address_is_in_section(Dwarf_Addr addr, Elf_Scn* section) const - { - if (!section) - return false; - - GElf_Shdr sheader_mem; - GElf_Shdr* sheader = gelf_getshdr(section, &sheader_mem); - - if (sheader->sh_addr <= addr && addr <= sheader->sh_addr + sheader->sh_size) - return true; - - return false; - } - /// For a relocatable (*.o) elf file, this function expects an /// absolute address, representing a global variable symbol. It /// then extracts the address of the {.data,.data1,.rodata,.bss} diff --git a/src/abg-elf-helpers.cc b/src/abg-elf-helpers.cc index 213c5d60..480381de 100644 --- a/src/abg-elf-helpers.cc +++ b/src/abg-elf-helpers.cc @@ -861,6 +861,153 @@ architecture_is_big_endian(Elf* elf_handle) return is_big_endian; } +/// Read N bytes and convert their value into an integer type T. +/// +/// Note that N cannot be bigger than 8 for now. The type passed needs to be at +/// least of the size of number_of_bytes. +/// +/// @param bytes the array of bytes to read the next 8 bytes from. +/// Note that this array must be at least 8 bytes long. +/// +/// @param number_of_bytes the number of bytes to read. This number +/// cannot be bigger than 8. +/// +/// @param is_big_endian if true, read the 8 bytes in Big Endian +/// mode, otherwise, read them in Little Endian. +/// +/// @param result where to store the resuting integer that was read. +/// +/// +/// @param true if the 8 bytes could be read, false otherwise. +template <typename T> +bool +read_int_from_array_of_bytes(const uint8_t* bytes, + unsigned char number_of_bytes, + bool is_big_endian, + T& result) +{ + if (!bytes) + return false; + + ABG_ASSERT(number_of_bytes <= 8); + ABG_ASSERT(number_of_bytes <= sizeof(T)); + + T res = 0; + + const uint8_t* cur = bytes; + if (is_big_endian) + { + // In Big Endian, the most significant byte is at the lowest + // address. + const uint8_t* msb = cur; + res = *msb; + + // Now read the remaining least significant bytes. + for (uint i = 1; i < number_of_bytes; ++i) + res = (res << 8) | ((T)msb[i]); + } + else + { + // In Little Endian, the least significant byte is at the + // lowest address. + const uint8_t* lsb = cur; + res = *lsb; + // Now read the remaining most significant bytes. + for (uint i = 1; i < number_of_bytes; ++i) + res = res | (((T)lsb[i]) << i * 8); + } + + result = res; + return true; +} + +/// Read 8 bytes and convert their value into an uint64_t. +/// +/// @param bytes the array of bytes to read the next 8 bytes from. +/// Note that this array must be at least 8 bytes long. +/// +/// @param result where to store the resuting uint64_t that was read. +/// +/// @param is_big_endian if true, read the 8 bytes in Big Endian +/// mode, otherwise, read them in Little Endian. +/// +/// @param true if the 8 bytes could be read, false otherwise. +bool +read_uint64_from_array_of_bytes(const uint8_t* bytes, + bool is_big_endian, + uint64_t& result) +{ + return read_int_from_array_of_bytes(bytes, 8, is_big_endian, result); +} + + +/// Lookup the address of the function entry point that corresponds +/// to the address of a given function descriptor. +/// +/// On PPC64, a function pointer is the address of a function +/// descriptor. Function descriptors are located in the .opd +/// section. Each function descriptor is a triplet of three +/// addresses, each one on 64 bits. Among those three address only +/// the first one is of any interest to us: the address of the entry +/// point of the function. +/// +/// This function returns the address of the entry point of the +/// function whose descriptor's address is given. +/// +/// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-DES +/// +/// https://www.ibm.com/developerworks/community/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/deeply_understand_64_bit_powerpc_elf_abi_function_descriptors?lang=en +/// +/// @param fn_desc_address the address of the function descriptor to +/// consider. +/// +/// @return the address of the entry point of the function whose +/// descriptor has the address @p fn_desc_address. If there is no +/// .opd section (e.g because we are not on ppc64) or more generally +/// if the function descriptor could not be found then this function +/// just returns the address of the fuction descriptor. +GElf_Addr +lookup_ppc64_elf_fn_entry_point_address(Elf* elf_handle, GElf_Addr fn_desc_address) +{ + if (!elf_handle) + return fn_desc_address; + + if (!architecture_is_ppc64(elf_handle)) + return fn_desc_address; + + bool is_big_endian = architecture_is_big_endian(elf_handle); + + Elf_Scn* opd_section = find_opd_section(elf_handle); + if (!opd_section) + return fn_desc_address; + + GElf_Shdr header_mem; + // The section header of the .opd section. + GElf_Shdr* opd_sheader = gelf_getshdr(opd_section, &header_mem); + + // The offset of the function descriptor entry, in the .opd + // section. + size_t fn_desc_offset = fn_desc_address - opd_sheader->sh_addr; + Elf_Data* elf_data = elf_rawdata(opd_section, 0); + + // Ensure that the opd_section has at least 8 bytes, starting from + // the offset we want read the data from. + if (elf_data->d_size <= fn_desc_offset + 8) + return fn_desc_address; + + // A pointer to the data of the .opd section, that we can actually + // do something with. + uint8_t* bytes = (uint8_t*)elf_data->d_buf; + + // The resulting address we are looking for is going to be formed + // in this variable. + GElf_Addr result = 0; + ABG_ASSERT(read_uint64_from_array_of_bytes(bytes + fn_desc_offset, + is_big_endian, result)); + + return result; +} + /// Test if the ELF binary denoted by a given ELF handle is a Linux /// Kernel Module. /// @@ -1025,5 +1172,46 @@ maybe_adjust_et_rel_sym_addr_to_abs_addr(Elf* elf_handle, GElf_Sym* sym) return addr + section_header.sh_addr; } +/// Test if a given address is in a given section. +/// +/// @param addr the address to consider. +/// +/// @param section the section to consider. +/// +/// @return true iff @p addr is in section @p section. +bool +address_is_in_section(Dwarf_Addr addr, Elf_Scn* section) +{ + if (!section) + return false; + + GElf_Shdr sheader_mem; + GElf_Shdr* sheader = gelf_getshdr(section, &sheader_mem); + + if (sheader->sh_addr <= addr && addr <= sheader->sh_addr + sheader->sh_size) + return true; + + return false; +} + +/// Return true if an address is in the ".opd" section that is +/// present on the ppc64 platform. +/// +/// @param addr the address to consider. +/// +/// @return true iff @p addr designates a word that is in the ".opd" +/// section. +bool +address_is_in_opd_section(Elf* elf_handle, Dwarf_Addr addr) +{ + Elf_Scn * opd_section = find_opd_section(elf_handle); + if (!opd_section) + return false; + if (address_is_in_section(addr, opd_section)) + 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 7ce4df95..96e03d26 100644 --- a/src/abg-elf-helpers.h +++ b/src/abg-elf-helpers.h @@ -12,6 +12,7 @@ #include "config.h" +#include <elfutils/libdwfl.h> #include <gelf.h> #include <string> @@ -136,6 +137,10 @@ architecture_is_arm32(Elf* elf_handle); bool architecture_is_big_endian(Elf* elf_handle); +GElf_Addr +lookup_ppc64_elf_fn_entry_point_address(Elf* elf_handle, + GElf_Addr fn_desc_address); + // // Helpers for Linux Kernel Binaries // @@ -165,6 +170,9 @@ is_dso(Elf* elf_handle); GElf_Addr maybe_adjust_et_rel_sym_addr_to_abs_addr(Elf* elf_handle, GElf_Sym* sym); +bool +address_is_in_opd_section(Elf* elf_handle, Dwarf_Addr addr); + } // end namespace elf_helpers } // end namespace abigail |