/* Copyright (C) 2007-2010 The Android Open Source Project | |
** | |
** This software is licensed under the terms of the GNU General Public | |
** License version 2, as published by the Free Software Foundation, and | |
** may be copied, distributed, and modified under those terms. | |
** | |
** 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. | |
*/ | |
/* | |
* Contains declaration of ElfFile classes that encapsulate an ELF file. | |
*/ | |
#ifndef ELFF_ELF_FILE_H_ | |
#define ELFF_ELF_FILE_H_ | |
#include "dwarf_die.h" | |
#include "elf_mapped_section.h" | |
#include "elff_api.h" | |
#include "android/utils/mapfile.h" | |
/* Encapsulates architecture-independent functionality of an ELF file. | |
* | |
* This class is a base class for templated ElfFileImpl. This class implements | |
* functionality around an ELF file that is independent from particulars of the | |
* ELF's CPU architectire, while ElfFileImpl handles all particulars of CPU | |
* architecture (namely, 32 or 64-bit), for which ELF file has been built. | |
* | |
* NOTE: This class operates on ELF sections that have been mapped to memory. | |
* | |
*/ | |
class ElfFile { | |
public: | |
/* Constructs ElfFile instance. */ | |
ElfFile(); | |
/* Destructs ElfFile instance. */ | |
virtual ~ElfFile(); | |
/* Creates ElfFileImpl instance, depending on ELF file CPU architecture. | |
* This method will collect initial information about requested ELF file, | |
* and will instantiate appropriate ElfFileImpl class object for it. | |
* Param: | |
* path - Full path to the ELF file. | |
* Return: | |
* Initialized ElfFileImpl instance, typecasted back to ElfFile object on | |
* success, or NULL on failure, with errno providing extended error | |
* information. | |
*/ | |
static ElfFile* Create(const char* path); | |
/* Checks if ELF file is a 64, or 32-bit ELF file. */ | |
bool is_ELF_64() const { | |
return is_ELF_64_; | |
} | |
bool is_ELF_32() const { | |
return !is_ELF_64_; | |
} | |
/* Checks if ELF file data format is big, or little-endian. */ | |
bool is_elf_big_endian() const { | |
return is_elf_big_endian_; | |
} | |
bool is_elf_little_endian() const { | |
return !is_elf_big_endian_; | |
} | |
/* Checks whether or not endianness of CPU this library is built for matches | |
* endianness of the ELF file that is represented with this instance. */ | |
bool same_endianness() const { | |
return same_endianness_; | |
} | |
/* Checks if format of DWARF data in this file is 64, or 32-bit. */ | |
bool is_DWARF_64() const { | |
return is_DWARF_64_; | |
} | |
bool is_DWARF_32() const { | |
return !is_DWARF_64_; | |
} | |
/* Gets DWARF objects allocator for this instance. */ | |
class ElfAllocator* allocator() const { | |
return allocator_; | |
} | |
/* Gets head of compilation unit list, collected during parsing of this file. | |
* NOTE: list of collected compilation units returned from this method is | |
* in reverse order relatively to the order CUs have been added to the list | |
* during ELF file parsing. | |
*/ | |
class DwarfCU* last_cu() const { | |
return last_cu_; | |
} | |
/* Gets number of compilation units, collected during parsing of | |
* this ELF file with parse_compilation_units() method. | |
*/ | |
int cu_count() const { | |
return cu_count_; | |
} | |
/* Gets executable file flag */ | |
bool is_exec() const { | |
return is_exec_; | |
} | |
protected: | |
/* Initializes ElfFile instance. This method is called from Create method of | |
* this class after appropriate ElfFileImpl instance has been created. Note, | |
* that Create() method will validate that requested file is an ELF file, | |
* prior to instantiating of an ElfFileImpl object, and calling this method. | |
* Param: | |
* elf_hdr - Address of the common ELF file header. | |
* path - See Create(). | |
* Return: | |
* true on success, or false on failure, with errno containing extended | |
* error information. | |
*/ | |
virtual bool initialize(const Elf_CommonHdr* elf_hdr, const char* path); | |
/*============================================================================= | |
* Endianness helper methods. | |
* Since endianness of ELF file may differ from the endianness of the CPU this | |
* library runs on, every time a value is required from a section of the ELF | |
* file, it must be first pulled out of that section to a local variable, and | |
* then used from that local variable. While value is pulled from ELF file | |
* section, it must be converted accordingly to the endianness of the CPU and | |
* ELF file. Routines bellow provide such functionality. | |
=============================================================================*/ | |
public: | |
/* Pulls one byte value from ELF file. Note that for one byte we don't need | |
* to do any endianness conversion, and these two methods are provided purely | |
* for completness of the API. | |
* Param: | |
* val - References value inside ELF file buffer to pull data from. | |
* Return | |
* Pulled value with endianness appropriate for the CPU this library is | |
* running on. | |
*/ | |
uint8_t pull_val(const uint8_t* val) const { | |
return *val; | |
} | |
uint8_t pull_val(const uint8_t& val) const { | |
return val; | |
} | |
int8_t pull_val(const int8_t* val) const { | |
return *val; | |
} | |
int8_t pull_val(const int8_t& val) const { | |
return val; | |
} | |
/* Pulls two byte value from ELF file. | |
* Param: | |
* val - References value inside ELF file buffer to pull data from. | |
* Return | |
* Pulled value with endianness appropriate for the CPU this library is | |
* running on. | |
*/ | |
uint16_t pull_val(const uint16_t* val) const { | |
if (same_endianness()) { | |
return *val; | |
} | |
if (is_elf_big_endian()) { | |
return (uint16_t)get_byte(val, 0) << 8 | get_byte(val, 1); | |
} else { | |
return (uint16_t)get_byte(val, 1) << 8 | get_byte(val, 0); | |
} | |
} | |
uint16_t pull_val(const uint16_t& val) const { | |
return same_endianness() ? val : pull_val(&val); | |
} | |
int16_t pull_val(const int16_t* val) const { | |
return static_cast<int16_t> | |
(pull_val(reinterpret_cast<const uint16_t*>(val))); | |
} | |
int16_t pull_val(const int16_t& val) const { | |
return static_cast<int16_t> | |
(pull_val(reinterpret_cast<const uint16_t&>(val))); | |
} | |
/* Pulls four byte value from ELF file. | |
* Param: | |
* val - References value inside ELF file buffer to pull data from. | |
* Return | |
* Pulled value with endianness appropriate for the CPU this library is | |
* running on. | |
*/ | |
uint32_t pull_val(const uint32_t* val) const { | |
if (same_endianness()) { | |
return *val; | |
} | |
if (is_elf_big_endian()) { | |
return (uint32_t)get_byte(val, 0) << 24 | | |
(uint32_t)get_byte(val, 1) << 16 | | |
(uint32_t)get_byte(val, 2) << 8 | | |
(uint32_t)get_byte(val, 3); | |
} else { | |
return (uint32_t)get_byte(val, 3) << 24 | | |
(uint32_t)get_byte(val, 2) << 16 | | |
(uint32_t)get_byte(val, 1) << 8 | | |
(uint32_t)get_byte(val, 0); | |
} | |
} | |
uint32_t pull_val(const uint32_t& val) const { | |
return same_endianness() ? val : pull_val(&val); | |
} | |
int32_t pull_val(const int32_t* val) const { | |
return static_cast<int32_t> | |
(pull_val(reinterpret_cast<const uint32_t*>(val))); | |
} | |
int32_t pull_val(const int32_t& val) const { | |
return static_cast<int32_t> | |
(pull_val(reinterpret_cast<const uint32_t&>(val))); | |
} | |
/* Pulls eight byte value from ELF file. | |
* Param: | |
* val - References value inside ELF file buffer to pull data from. | |
* Return | |
* Pulled value with endianness appropriate for the CPU this library is | |
* running on. | |
*/ | |
uint64_t pull_val(const uint64_t* val) const { | |
if (same_endianness()) { | |
return *val; | |
} | |
if (is_elf_big_endian()) { | |
return (uint64_t)get_byte(val, 0) << 56 | | |
(uint64_t)get_byte(val, 1) << 48 | | |
(uint64_t)get_byte(val, 2) << 40 | | |
(uint64_t)get_byte(val, 3) << 32 | | |
(uint64_t)get_byte(val, 4) << 24 | | |
(uint64_t)get_byte(val, 5) << 16 | | |
(uint64_t)get_byte(val, 6) << 8 | | |
(uint64_t)get_byte(val, 7); | |
} else { | |
return (uint64_t)get_byte(val, 7) << 56 | | |
(uint64_t)get_byte(val, 6) << 48 | | |
(uint64_t)get_byte(val, 5) << 40 | | |
(uint64_t)get_byte(val, 4) << 32 | | |
(uint64_t)get_byte(val, 3) << 24 | | |
(uint64_t)get_byte(val, 2) << 16 | | |
(uint64_t)get_byte(val, 1) << 8 | | |
(uint64_t)get_byte(val, 0); | |
} | |
} | |
uint64_t pull_val(const uint64_t& val) const { | |
return same_endianness() ? val : pull_val(&val); | |
} | |
int64_t pull_val(const int64_t* val) const { | |
return static_cast<int64_t> | |
(pull_val(reinterpret_cast<const uint64_t*>(val))); | |
} | |
int64_t pull_val(const int64_t& val) const { | |
return static_cast<int64_t> | |
(pull_val(reinterpret_cast<const uint64_t&>(val))); | |
} | |
//============================================================================= | |
// ELF file section management. | |
//============================================================================= | |
public: | |
/* Gets a string contained in ELF's string section by index. | |
* Param: | |
* index - String index (byte offset) in the ELF's string section. | |
* Return: | |
* Pointer to the requested string, or NULL if string index exceeds ELF's | |
* string section size. | |
* NOTE: pointer returned from this method points to a mapped section of | |
* ELF file. | |
*/ | |
const char* get_str_sec_str(Elf_Xword index) const { | |
assert(string_section_.is_mapped() && index < string_section_.size()); | |
if (string_section_.is_mapped() && index < string_section_.size()) { | |
return INC_CPTR_T(char, string_section_.data(), index); | |
} else { | |
_set_errno(EINVAL); | |
return NULL; | |
} | |
} | |
/* Gets a string contained in ELF's debug string section (.debug_str) | |
* by index. | |
* Param: | |
* index - String index (byte offset) in the ELF's debug string section. | |
* Return: | |
* Pointer to the requested string, or NULL if string index exceeds ELF's | |
* debug string section size. | |
* NOTE: pointer returned from this method points to a mapped section of | |
* ELF file. | |
*/ | |
const char* get_debug_str(Elf_Xword index) const { | |
assert(debug_str_.is_mapped() && index < debug_str_.size()); | |
if (debug_str_.is_mapped() && index < debug_str_.size()) { | |
return INC_CPTR_T(char, debug_str_.data(), index); | |
} else { | |
_set_errno(EINVAL); | |
return NULL; | |
} | |
} | |
protected: | |
/* Gets pointer to a section header, given section index within ELF's | |
* section table. | |
* Param: | |
* index - Section index within ELF's section table. | |
* Return: | |
* Pointer to a section header (ElfXX_SHdr flavor, depending on ELF's CPU | |
* architecture) on success, or NULL if section index exceeds number of | |
* sections for this ELF file. | |
*/ | |
const void* get_section_by_index(Elf_Half index) const { | |
assert(index < sec_count_); | |
if (index < sec_count_) { | |
return INC_CPTR(sec_table_, static_cast<size_t>(index) * sec_entry_size_); | |
} else { | |
_set_errno(EINVAL); | |
return NULL; | |
} | |
} | |
//============================================================================= | |
// DWARF management. | |
//============================================================================= | |
protected: | |
/* Parses DWARF, and buids a list of compilation units for this ELF file. | |
* Compilation unit, collected with this methods are linked together in a | |
* list, head of which is available via last_cu() method of this class. | |
* NOTE: CUs in the list returned via last_cu() method are in reverse order | |
* relatively to the order in which CUs are stored in .debug_info section. | |
* This is ELF and DWARF data format - dependent method. | |
* Param: | |
* parse_context - Parsing context that defines which tags, and which | |
* properties for which tag should be collected during parsing. NULL | |
* passed in this parameter indicates that all properties for all tags | |
* should be collected. | |
* Return: | |
* Number of compilation units, collected in this method on success, | |
* or -1 on failure. | |
*/ | |
virtual int parse_compilation_units(const DwarfParseContext* parse_context) = 0; | |
public: | |
/* Gets PC address information. | |
* Param: | |
* address - PC address to get information for. The address must be relative | |
* to the beginning of ELF file represented by this class. | |
* address_info - Upon success contains information about routine(s) that | |
* contain the given address. | |
* Return: | |
* true if routine(s) containing has been found and its information has been | |
* saved into address_info, or false if no appropriate routine for that | |
* address has been found, or there was a memory error when collecting | |
* routine(s) information. In case of failure, errno contains extended error | |
* information. | |
*/ | |
bool get_pc_address_info(Elf_Xword address, Elf_AddressInfo* address_info); | |
/* Frees resources aqcuired for address information in successful call to | |
* get_pc_address_info(). | |
* Param: | |
* address_info - Address information structure, initialized in successful | |
* call to get_pc_address_info() routine. | |
*/ | |
void free_pc_address_info(Elf_AddressInfo* address_info) const; | |
/* Gets beginning of the .debug_info section data. | |
* Return: | |
* Beginning of the .debug_info section data. | |
* NOTE: pointer returned from this method points to a mapped section of | |
* ELF file. | |
*/ | |
const void* get_debug_info_data() const { | |
return debug_info_.data(); | |
} | |
/* Gets beginning of the .debug_abbrev section data. | |
* Return: | |
* Beginning of the .debug_abbrev section data. | |
* NOTE: pointer returned from this method points to a mapped section of | |
* ELF file. | |
*/ | |
const void* get_debug_abbrev_data() const { | |
return debug_abbrev_.data(); | |
} | |
/* Gets beginning of the .debug_ranges section data. | |
* Return: | |
* Beginning of the .debug_ranges section data. | |
* NOTE: pointer returned from this method points to a mapped section of | |
* ELF file. | |
*/ | |
const void* get_debug_ranges_data() const { | |
return debug_ranges_.data(); | |
} | |
/* Gets beginning of the .debug_line section data. | |
* Return: | |
* Beginning of the .debug_line section data. | |
* NOTE: pointer returned from this method points to a mapped section of | |
* ELF file. | |
*/ | |
const void* get_debug_line_data() const { | |
return debug_line_.data(); | |
} | |
/* Checks, if given address range is contained in the mapped .debug_info | |
* section of this file. | |
* Param: | |
* ptr - Starting address of the range. | |
* size - Range size in bytes. | |
* Return: | |
* true if given address range is contained in the mapped .debug_info | |
* section of this file, or false if any part of the range doesn't belong | |
* to that section. | |
*/ | |
bool is_valid_die_ptr(const void* ptr, size_t size) const { | |
return debug_info_.is_contained(ptr, size); | |
} | |
/* Checks, if given address range is contained in the mapped .debug_abbrev | |
* section of this file. | |
* Param: | |
* ptr - Starting address of the range. | |
* size - Range size in bytes. | |
* Return: | |
* true if given address range is contained in the mapped .debug_abbrev | |
* section of this file, or false if any part of the range doesn't belong | |
* to that section. | |
*/ | |
bool is_valid_abbr_ptr(const void* ptr, size_t size) const { | |
return debug_abbrev_.is_contained(ptr, size); | |
} | |
/* Checks if given pointer addresses a valid compilation unit header in the | |
* mapped .debug_info section of the ELF file. | |
* Param: | |
* cu_header - Pointer to a compilation unit header to check. | |
* Return | |
* true, if given pointer addresses a valid compilation unit header, or | |
* false, if it's not. A valid CU header must be fully conained inside | |
* .debug_info section of the ELF file, and its size must not be zero. | |
*/ | |
bool is_valid_cu(const void* cu_header) const { | |
if (is_DWARF_64()) { | |
return is_valid_die_ptr(cu_header, sizeof(Dwarf64_CUHdr)) && | |
reinterpret_cast<const Dwarf64_CUHdr*>(cu_header)->size_hdr.size != 0; | |
} else { | |
return is_valid_die_ptr(cu_header, sizeof(Dwarf32_CUHdr)) && | |
reinterpret_cast<const Dwarf32_CUHdr*>(cu_header)->size_hdr.size != 0; | |
} | |
} | |
/* Gets range's low and high pc for the given range reference in the mapped | |
* .debug_ranges section of an ELF file. | |
* Template param: | |
* AddrType - Defines pointer type for the CU the range belongs to. CU's | |
* pointer type can be defined independently from ELF and DWARF types, | |
* and is encoded in address_size field of the CU header in .debug_info | |
* section of ELF file. | |
* Param: | |
* offset - Byte offset within .debug_ranges section of the range record. | |
* low - Upon successful return contains value for range's low pc. | |
* high - Upon successful return contains value for range's high pc. | |
* Return: | |
* true on success, or false, if requested record is not fully contained | |
* in the .debug_ranges section. | |
*/ | |
template<typename AddrType> | |
bool get_range(Elf_Word offset, AddrType* low, AddrType* high) { | |
const AddrType* ptr = INC_CPTR_T(AddrType, debug_ranges_.data(), offset); | |
assert(debug_ranges_.is_contained(ptr, sizeof(AddrType) * 2)); | |
if (!debug_ranges_.is_contained(ptr, sizeof(AddrType) * 2)) { | |
_set_errno(EINVAL); | |
return false; | |
} | |
*low = pull_val(ptr); | |
*high = pull_val(ptr + 1); | |
return true; | |
} | |
protected: | |
/* Mapped ELF string section. */ | |
ElfMappedSection string_section_; | |
/* Mapped .debug_info section. */ | |
ElfMappedSection debug_info_; | |
/* Mapped .debug_abbrev section. */ | |
ElfMappedSection debug_abbrev_; | |
/* Mapped .debug_str section. */ | |
ElfMappedSection debug_str_; | |
/* Mapped .debug_line section. */ | |
ElfMappedSection debug_line_; | |
/* Mapped .debug_ranges section. */ | |
ElfMappedSection debug_ranges_; | |
/* Base address of the loaded module (if fixed), or 0 if module doesn't get | |
* loaded at fixed address. */ | |
Elf_Xword fixed_base_address_; | |
/* Handle to the ELF file represented with this instance. */ | |
MapFile* elf_handle_; | |
/* Path to the ELF file represented with this instance. */ | |
char* elf_file_path_; | |
/* DWARF objects allocator for this instance. */ | |
class ElfAllocator* allocator_; | |
/* Beginning of the cached ELF's section table. */ | |
void* sec_table_; | |
/* Number of sections in the ELF file wrapped by this instance. */ | |
Elf_Half sec_count_; | |
/* Byte size of an entry in the section table. */ | |
Elf_Half sec_entry_size_; | |
/* Head of compilation unit list, collected during the parsing. */ | |
class DwarfCU* last_cu_; | |
/* Number of compilation units in last_cu_ list. */ | |
int cu_count_; | |
/* Flags ELF's CPU architecture: 64 (true), or 32 bits (false). */ | |
bool is_ELF_64_; | |
/* Flags endianness of the processed ELF file. true indicates that ELF file | |
* data is stored in big-endian form, false indicates that ELF file data is | |
* stored in big-endian form. | |
*/ | |
bool is_elf_big_endian_; | |
/* Flags whether or not endianness of CPU this library is built for matches | |
* endianness of the ELF file that is represented with this instance. | |
*/ | |
bool same_endianness_; | |
/* Flags DWARF format: 64, or 32 bits. DWARF format is determined by looking | |
* at the first 4 bytes of .debug_info section (which is the beginning of the | |
* first compilation unit header). If first 4 bytes contain 0xFFFFFFFF, the | |
* DWARF is 64 bit. Otherwise, DWARF is 32 bit. */ | |
bool is_DWARF_64_; | |
/* Flags executable file. If this member is 1, ELF file represented with this | |
* instance is an executable. If this member is 0, file is a shared library. | |
*/ | |
bool is_exec_; | |
}; | |
/* Encapsulates architecture-dependent functionality of an ELF file. | |
* Template param: | |
* Elf_Addr - type for an address field in ELF file. Must be: | |
* - Elf32_Addr for 32-bit CPU, or | |
* - Elf64_Addr for 64-bit CPU. | |
* Elf_Off - type for an offset field in ELF file. Must be: | |
* - Elf64_Off for 32-bit CPU, or | |
* - Elf64_Off for 64-bit CPU. | |
*/ | |
template <typename Elf_Addr, typename Elf_Off> | |
class ElfFileImpl : protected ElfFile { | |
/* Instance of this class must be instantiated from | |
* ElfFile::Create() method only. */ | |
friend class ElfFile; | |
protected: | |
/* Constructs ElfFileImpl instance. */ | |
ElfFileImpl() { | |
}; | |
/* Destructs ElfFileImpl instance. */ | |
~ElfFileImpl() { | |
} | |
protected: | |
/* Initializes instance. This is an override of the base class method. | |
* See ElfFile::initialize(). | |
*/ | |
bool initialize(const Elf_CommonHdr* elf_hdr, const char* path); | |
/* Parses DWARF, and buids list of compilation units for this ELF file. | |
* This is an implementation of the base class' abstract method. | |
* See ElfFile::parse_compilation_units(). | |
*/ | |
virtual int parse_compilation_units(const DwarfParseContext* parse_context); | |
/* Gets section information by section name. | |
* Param: | |
* name - Name of the section to get information for. | |
* offset - Upon success contains offset of the section data in ELF file. | |
* size - Upon success contains size of the section data in ELF file. | |
* Return: | |
* true on sucess, or false if section with such name doesn't exist in | |
* this ELF file. | |
*/ | |
bool get_section_info_by_name(const char* name, | |
Elf_Off* offset, | |
Elf_Word* size); | |
/* Maps section by its name. | |
* Param: | |
* name - Name of the section to map. | |
* section - Upon success contains section's mapping information. | |
* Return: | |
* true on sucess, or false if section with such name doesn't exist in | |
* this ELF file, or mapping has failed. | |
*/ | |
bool map_section_by_name(const char* name, ElfMappedSection* section); | |
}; | |
#endif // ELFF_ELF_FILE_H_ |