mirror of
git://sourceware.org/git/libabigail.git
synced 2024-12-17 23:44:35 +00:00
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>
This commit is contained in:
parent
9cd191b3c4
commit
e87f2672d9
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user