/* 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_