| /* 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 implementation of ElfFile classes that encapsulate an ELF file. |
| */ |
| |
| #include "string.h" |
| #include "elf_file.h" |
| #include "elf_alloc.h" |
| #include "dwarf_cu.h" |
| #include "dwarf_utils.h" |
| |
| #include <fcntl.h> |
| #ifndef O_BINARY |
| #define O_BINARY 0 |
| #endif |
| |
| /* Tags to parse when collecting info about routines. */ |
| static const Dwarf_Tag parse_rt_tags[] = { |
| DW_TAG_compile_unit, |
| DW_TAG_partial_unit, |
| DW_TAG_inlined_subroutine, |
| DW_TAG_subprogram, |
| 0 |
| }; |
| static const DwarfParseContext parse_rt_context = { parse_rt_tags }; |
| |
| //============================================================================= |
| // Base ElfFile implementation |
| //============================================================================= |
| |
| ElfFile::ElfFile() |
| : fixed_base_address_(0), |
| elf_handle_((MapFile*)-1), |
| elf_file_path_(NULL), |
| allocator_(NULL), |
| sec_table_(NULL), |
| sec_count_(0), |
| sec_entry_size_(0), |
| last_cu_(NULL), |
| cu_count_(0), |
| is_exec_(0) { |
| } |
| |
| ElfFile::~ElfFile() { |
| DwarfCU* cu_to_del = last_cu_; |
| while (cu_to_del != NULL) { |
| DwarfCU* next_cu_to_del = cu_to_del->prev_cu_; |
| delete cu_to_del; |
| cu_to_del = next_cu_to_del; |
| } |
| |
| if (mapfile_is_valid(elf_handle_)) { |
| mapfile_close(elf_handle_); |
| } |
| |
| if (elf_file_path_ != NULL) { |
| delete[] elf_file_path_; |
| } |
| |
| if (sec_table_ != NULL) { |
| delete[] reinterpret_cast<Elf_Byte*>(sec_table_); |
| } |
| |
| /* Must be deleted last! */ |
| if (allocator_ != NULL) { |
| delete allocator_; |
| } |
| } |
| |
| ElfFile* ElfFile::Create(const char* path) { |
| ElfFile* ret = NULL; |
| /* Allocate enough space on the stack to fit the largest ELF file header. */ |
| Elf64_FHdr header; |
| const Elf_CommonHdr* elf_hdr = &header.common; |
| |
| assert(path != NULL && *path != '\0'); |
| if (path == NULL || *path == '\0') { |
| _set_errno(EINVAL); |
| return NULL; |
| } |
| |
| /* |
| * Open ELF file, and read its header (the largest one possible). |
| */ |
| MapFile* file_handle = mapfile_open(path, O_RDONLY | O_BINARY, 0); |
| if (!mapfile_is_valid(file_handle)) { |
| return NULL; |
| } |
| const ssize_t read_bytes = mapfile_read(file_handle, &header, sizeof(header)); |
| mapfile_close(file_handle); |
| assert(read_bytes != -1 && read_bytes == sizeof(header)); |
| if (read_bytes == -1 || read_bytes != sizeof(header)) { |
| if (read_bytes != -1) { |
| _set_errno(EINVAL); |
| } |
| return NULL; |
| } |
| |
| /* Lets see if this is an ELF file at all. */ |
| if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG) != 0) { |
| /* File is not an ELF file. */ |
| _set_errno(ENOEXEC); |
| return NULL; |
| } |
| |
| /* Lets check ELF's "bitness". */ |
| assert(elf_hdr->ei_info.ei_class == ELFCLASS32 || |
| elf_hdr->ei_info.ei_class == ELFCLASS64); |
| if (elf_hdr->ei_info.ei_class != ELFCLASS32 && |
| elf_hdr->ei_info.ei_class != ELFCLASS64) { |
| /* Neither 32, or 64-bit ELF file. Something wrong here. */ |
| _set_errno(EBADF); |
| return NULL; |
| } |
| |
| /* Lets instantiate appropriate ElfFileImpl object for this ELF. */ |
| if (elf_hdr->ei_info.ei_class == ELFCLASS32) { |
| ret = new ElfFileImpl<Elf32_Addr, Elf32_Off>; |
| } else { |
| ret = new ElfFileImpl<Elf64_Addr, Elf64_Off>; |
| } |
| assert(ret != NULL); |
| if (ret != NULL) { |
| if (!ret->initialize(elf_hdr, path)) { |
| delete ret; |
| ret = NULL; |
| } |
| } else { |
| _set_errno(ENOMEM); |
| } |
| |
| return ret; |
| } |
| |
| bool ElfFile::initialize(const Elf_CommonHdr* elf_hdr, const char* path) { |
| /* Must be created first! */ |
| allocator_ = new ElfAllocator(); |
| assert(allocator_ != NULL); |
| if (allocator_ == NULL) { |
| _set_errno(ENOMEM); |
| return false; |
| } |
| |
| /* Copy file path. */ |
| size_t path_len = strlen(path) + 1; |
| elf_file_path_ = new char[path_len]; |
| assert(elf_file_path_ != NULL); |
| if (elf_file_path_ == NULL) { |
| _set_errno(ENOMEM); |
| return false; |
| } |
| memcpy(elf_file_path_, path, path_len); |
| |
| /* Cache some basic ELF properties. */ |
| is_ELF_64_ = elf_hdr->ei_info.ei_class == ELFCLASS64; |
| is_elf_big_endian_ = elf_hdr->ei_info.ei_data == ELFDATA2MSB; |
| same_endianness_ = is_elf_little_endian() == is_little_endian_cpu(); |
| is_exec_ = elf_hdr->e_type == 2; |
| |
| /* Reopen file for further reads and mappings. */ |
| elf_handle_ = mapfile_open(elf_file_path_, O_RDONLY | O_BINARY, 0); |
| return mapfile_is_valid(elf_handle_); |
| } |
| |
| bool ElfFile::get_pc_address_info(Elf_Xword address, |
| Elf_AddressInfo* address_info) { |
| assert(address_info != NULL); |
| if (address_info == NULL) { |
| _set_errno(EINVAL); |
| return false; |
| } |
| |
| /* Collect routine information for all CUs in this file. */ |
| if (parse_compilation_units(&parse_rt_context) == -1) { |
| return false; |
| } |
| |
| /* Iterate through the collected CUs looking for the one that |
| * contains the given address. */ |
| address_info->inline_stack = NULL; |
| DwarfCU* cu = last_cu(); |
| while (cu != NULL) { |
| /* Find a leaf DIE object in the current CU that contains the address. */ |
| Dwarf_AddressInfo info; |
| info.die_obj = cu->get_leaf_die_for_address(address); |
| if (info.die_obj != NULL) { |
| /* Convert the address to a location inside source file. */ |
| if (cu->get_pc_address_file_info(address, &info)) { |
| /* Copy location information to the returning structure. */ |
| address_info->file_name = info.file_name; |
| address_info->dir_name = info.dir_name; |
| address_info->line_number = info.line_number; |
| } else { |
| address_info->file_name = NULL; |
| address_info->dir_name = NULL; |
| address_info->line_number = 0; |
| } |
| |
| /* Lets see if the DIE represents a routine (rather than |
| * a lexical block, for instance). */ |
| Dwarf_Tag tag = info.die_obj->get_tag(); |
| while (!dwarf_tag_is_routine(tag)) { |
| /* This is not a routine DIE. Lets loop trhough the parents of that |
| * DIE looking for the first routine DIE. */ |
| info.die_obj = info.die_obj->parent_die(); |
| if (info.die_obj == NULL) { |
| /* Reached compilation unit DIE. Can't go any further. */ |
| address_info->routine_name = "<unknown>"; |
| return true; |
| } |
| tag = info.die_obj->get_tag(); |
| } |
| |
| /* Save name of the routine that contains the address. */ |
| address_info->routine_name = info.die_obj->get_name(); |
| if (address_info->routine_name == NULL) { |
| /* In some cases (minimum debugging info in the file) routine |
| * name may be not avaible. We, however, are obliged by API |
| * considerations to return something in this field. */ |
| address_info->routine_name = "<unknown>"; |
| } |
| |
| /* Lets see if address belongs to an inlined routine. */ |
| if (tag != DW_TAG_inlined_subroutine) { |
| address_info->inline_stack = NULL; |
| return true; |
| } |
| |
| /* |
| * Address belongs to an inlined routine. Create inline stack. |
| */ |
| |
| /* Allocate inline stack array big enough to fit all parent entries. */ |
| address_info->inline_stack = |
| new Elf_InlineInfo[info.die_obj->get_level() + 1]; |
| assert(address_info->inline_stack != NULL); |
| if (address_info->inline_stack == NULL) { |
| _set_errno(ENOMEM); |
| return false; |
| } |
| memset(address_info->inline_stack, 0, |
| sizeof(Elf_InlineInfo) * (info.die_obj->get_level() + 1)); |
| |
| /* Reverse DIEs filling in inline stack entries for inline |
| * routine tags. */ |
| int inl_index = 0; |
| do { |
| /* Save source file information. */ |
| DIEAttrib file_desc; |
| if (info.die_obj->get_attrib(DW_AT_call_file, &file_desc)) { |
| const Dwarf_STMTL_FileDesc* desc = |
| cu->get_stmt_file_info(file_desc.value()->u32); |
| if (desc != NULL) { |
| address_info->inline_stack[inl_index].inlined_in_file = |
| desc->file_name; |
| address_info->inline_stack[inl_index].inlined_in_file_dir = |
| cu->get_stmt_dir_name(desc->get_dir_index()); |
| } |
| } |
| if (address_info->inline_stack[inl_index].inlined_in_file == NULL) { |
| address_info->inline_stack[inl_index].inlined_in_file = "<unknown>"; |
| address_info->inline_stack[inl_index].inlined_in_file_dir = NULL; |
| } |
| |
| /* Save source line information. */ |
| if (info.die_obj->get_attrib(DW_AT_call_line, &file_desc)) { |
| address_info->inline_stack[inl_index].inlined_at_line = file_desc.value()->u32; |
| } |
| |
| /* Advance DIE to the parent routine, and save its name. */ |
| info.die_obj = info.die_obj->parent_die(); |
| assert(info.die_obj != NULL); |
| if (info.die_obj != NULL) { |
| tag = info.die_obj->get_tag(); |
| while (!dwarf_tag_is_routine(tag)) { |
| info.die_obj = info.die_obj->parent_die(); |
| if (info.die_obj == NULL) { |
| break; |
| } |
| tag = info.die_obj->get_tag(); |
| } |
| if (info.die_obj != NULL) { |
| address_info->inline_stack[inl_index].routine_name = |
| info.die_obj->get_name(); |
| } |
| } |
| if (address_info->inline_stack[inl_index].routine_name == NULL) { |
| address_info->inline_stack[inl_index].routine_name = "<unknown>"; |
| } |
| |
| /* Continue with the parent DIE. */ |
| inl_index++; |
| } while (info.die_obj != NULL && tag == DW_TAG_inlined_subroutine); |
| |
| return true; |
| } |
| cu = cu->prev_cu(); |
| } |
| |
| return false; |
| } |
| |
| void ElfFile::free_pc_address_info(Elf_AddressInfo* address_info) const { |
| assert(address_info != NULL); |
| if (address_info != NULL && address_info->inline_stack != NULL) { |
| delete address_info->inline_stack; |
| address_info->inline_stack = NULL; |
| } |
| } |
| |
| //============================================================================= |
| // ElfFileImpl |
| //============================================================================= |
| |
| template <typename Elf_Addr, typename Elf_Off> |
| bool ElfFileImpl<Elf_Addr, Elf_Off>::initialize(const Elf_CommonHdr* elf_hdr, |
| const char* path) { |
| /* Must be called first! */ |
| if (!ElfFile::initialize(elf_hdr, path)) { |
| return false; |
| } |
| |
| /* Cache some header data, so later we can discard the header. */ |
| const Elf_FHdr<Elf_Addr, Elf_Off>* header = |
| reinterpret_cast<const Elf_FHdr<Elf_Addr, Elf_Off>*>(elf_hdr); |
| sec_count_ = pull_val(header->e_shnum); |
| sec_entry_size_ = pull_val(header->e_shentsize); |
| fixed_base_address_ = pull_val(header->e_entry) & ~0xFFF; |
| |
| /* Cache section table (must have one!) */ |
| const Elf_Off sec_table_off = pull_val(header->e_shoff); |
| assert(sec_table_off != 0 && sec_count_ != 0); |
| if (sec_table_off == 0 || sec_count_ == 0) { |
| _set_errno(EBADF); |
| return false; |
| } |
| const size_t sec_table_size = sec_count_ * sec_entry_size_; |
| sec_table_ = new Elf_Byte[sec_table_size]; |
| assert(sec_table_ != NULL); |
| if (sec_table_ == NULL) { |
| _set_errno(ENOMEM); |
| return false; |
| } |
| if (mapfile_read_at(elf_handle_, sec_table_off, sec_table_, |
| sec_table_size) < 0) { |
| return false; |
| } |
| |
| /* Map ELF's string section (must have one!). */ |
| const Elf_Half str_sec_index = pull_val(header->e_shstrndx); |
| assert(str_sec_index != SHN_UNDEF); |
| if (str_sec_index == SHN_UNDEF) { |
| _set_errno(EBADF); |
| return false; |
| } |
| const Elf_SHdr<Elf_Addr, Elf_Off>* str_sec = |
| reinterpret_cast<const Elf_SHdr<Elf_Addr, Elf_Off>*> |
| (get_section_by_index(str_sec_index)); |
| assert(str_sec != NULL); |
| if (str_sec == NULL) { |
| _set_errno(EBADF); |
| return false; |
| } |
| if (!string_section_.map(elf_handle_, pull_val(str_sec->sh_offset), |
| pull_val(str_sec->sh_size))) { |
| return false; |
| } |
| |
| /* Lets determine DWARF format. According to the docs, DWARF is 64 bit, if |
| * first 4 bytes in the compilation unit header are set to 0xFFFFFFFF. |
| * .debug_info section of the ELF file begins with the first CU header. */ |
| if (!map_section_by_name(".debug_info", &debug_info_)) { |
| _set_errno(EBADF); |
| return false; |
| } |
| |
| /* Note that we don't care about endianness here, since 0xFFFFFFFF is an |
| * endianness-independent value, so we don't have to pull_val here. */ |
| is_DWARF_64_ = |
| *reinterpret_cast<const Elf_Word*>(debug_info_.data()) == 0xFFFFFFFF; |
| |
| return true; |
| } |
| |
| template <typename Elf_Addr, typename Elf_Off> |
| int ElfFileImpl<Elf_Addr, Elf_Off>::parse_compilation_units( |
| const DwarfParseContext* parse_context) { |
| /* Lets see if we already parsed the file. */ |
| if (last_cu() != NULL) { |
| return cu_count_; |
| } |
| |
| /* Cache sections required for this parsing. */ |
| if (!map_section_by_name(".debug_abbrev", &debug_abbrev_) || |
| !map_section_by_name(".debug_ranges", &debug_ranges_) || |
| !map_section_by_name(".debug_line", &debug_line_) || |
| !map_section_by_name(".debug_str", &debug_str_)) { |
| _set_errno(EBADF); |
| return false; |
| } |
| |
| /* .debug_info section opens with the first CU header. */ |
| const void* next_cu = debug_info_.data(); |
| |
| /* Iterate through CUs until we reached the end of .debug_info section, or |
| * advanced to a CU with zero size, indicating the end of CU list for this |
| * file. */ |
| while (is_valid_cu(next_cu)) { |
| /* Instatiate CU, depending on DWARF "bitness". */ |
| DwarfCU* cu = DwarfCU::create_instance(this, next_cu); |
| if (cu == NULL) { |
| _set_errno(ENOMEM); |
| return -1; |
| } |
| |
| if (cu->parse(parse_context, &next_cu)) { |
| cu->set_prev_cu(last_cu_); |
| last_cu_ = cu; |
| cu_count_++; |
| } else { |
| delete cu; |
| return -1; |
| } |
| }; |
| |
| return cu_count_; |
| } |
| |
| template <typename Elf_Addr, typename Elf_Off> |
| bool ElfFileImpl<Elf_Addr, Elf_Off>::get_section_info_by_name(const char* name, |
| Elf_Off* offset, |
| Elf_Word* size) { |
| const Elf_SHdr<Elf_Addr, Elf_Off>* cur_section = |
| reinterpret_cast<const Elf_SHdr<Elf_Addr, Elf_Off>*>(sec_table_); |
| |
| for (Elf_Half sec = 0; sec < sec_count_; sec++) { |
| const char* sec_name = get_str_sec_str(pull_val(cur_section->sh_name)); |
| if (sec_name != NULL && strcmp(name, sec_name) == 0) { |
| *offset = pull_val(cur_section->sh_offset); |
| *size = pull_val(cur_section->sh_size); |
| return true; |
| } |
| cur_section = reinterpret_cast<const Elf_SHdr<Elf_Addr, Elf_Off>*> |
| (INC_CPTR(cur_section, sec_entry_size_)); |
| } |
| _set_errno(EINVAL); |
| return false; |
| } |
| |
| template <typename Elf_Addr, typename Elf_Off> |
| bool ElfFileImpl<Elf_Addr, Elf_Off>::map_section_by_name( |
| const char* name, |
| ElfMappedSection* section) { |
| if (section->is_mapped()) { |
| return true; |
| } |
| |
| Elf_Off offset; |
| Elf_Word size; |
| if (!get_section_info_by_name(name, &offset, &size)) { |
| return false; |
| } |
| |
| return section->map(elf_handle_, offset, size); |
| } |