| /* |
| * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code 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 |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "precompiled.hpp" |
| |
| #if !defined(_WINDOWS) && !defined(__APPLE__) |
| |
| #include "jvm_io.h" |
| #include "logging/log.hpp" |
| #include "memory/allocation.inline.hpp" |
| #include "utilities/decoder.hpp" |
| #include "utilities/elfFile.hpp" |
| #include "utilities/elfFuncDescTable.hpp" |
| #include "utilities/elfStringTable.hpp" |
| #include "utilities/elfSymbolTable.hpp" |
| #include "utilities/ostream.hpp" |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <limits.h> |
| #include <new> |
| |
| const char* ElfFile::USR_LIB_DEBUG_DIRECTORY = "/usr/lib/debug"; |
| |
| // For test only, disable elf section cache and force to read from file directly. |
| bool ElfFile::_do_not_cache_elf_section = false; |
| |
| ElfSection::ElfSection(FILE* fd, const Elf_Shdr& hdr) : _section_data(nullptr) { |
| _stat = load_section(fd, hdr); |
| } |
| |
| ElfSection::~ElfSection() { |
| if (_section_data != nullptr) { |
| os::free(_section_data); |
| } |
| } |
| |
| NullDecoder::decoder_status ElfSection::load_section(FILE* const fd, const Elf_Shdr& shdr) { |
| memcpy((void*)&_section_hdr, (const void*)&shdr, sizeof(shdr)); |
| |
| if (ElfFile::_do_not_cache_elf_section) { |
| log_develop_debug(decoder)("Elf section cache is disabled"); |
| return NullDecoder::no_error; |
| } |
| |
| _section_data = os::malloc(shdr.sh_size, mtInternal); |
| // No enough memory for caching. It is okay, we can try to read from |
| // file instead. |
| if (_section_data == nullptr) return NullDecoder::no_error; |
| |
| MarkedFileReader mfd(fd); |
| if (mfd.has_mark() && |
| mfd.set_position(shdr.sh_offset) && |
| mfd.read(_section_data, shdr.sh_size)) { |
| return NullDecoder::no_error; |
| } else { |
| os::free(_section_data); |
| _section_data = nullptr; |
| return NullDecoder::file_invalid; |
| } |
| } |
| |
| bool FileReader::read(void* buf, size_t size) { |
| assert(buf != nullptr, "no buffer"); |
| assert(size > 0, "no space"); |
| return fread(buf, size, 1, _fd) == 1; |
| } |
| |
| size_t FileReader::read_buffer(void* buf, size_t size) { |
| assert(buf != nullptr, "no buffer"); |
| assert(size > 0, "no space"); |
| return fread(buf, 1, size, _fd); |
| } |
| |
| bool FileReader::set_position(long offset) { |
| return fseek(_fd, offset, SEEK_SET) == 0; |
| } |
| |
| MarkedFileReader::MarkedFileReader(FILE* fd) : FileReader(fd), _marked_pos(ftell(fd)) { |
| } |
| |
| MarkedFileReader::~MarkedFileReader() { |
| if (_marked_pos != -1) { |
| set_position(_marked_pos); |
| } |
| } |
| |
| ElfFile::ElfFile(const char* filepath) : |
| _next(nullptr), _filepath(os::strdup(filepath)), _file(nullptr), |
| _symbol_tables(nullptr), _string_tables(nullptr), _shdr_string_table(nullptr), _funcDesc_table(nullptr), |
| _status(NullDecoder::no_error), _dwarf_file(nullptr) { |
| memset(&_elfHdr, 0, sizeof(_elfHdr)); |
| if (_filepath == nullptr) { |
| _status = NullDecoder::out_of_memory; |
| } else { |
| _status = parse_elf(filepath); |
| } |
| } |
| |
| ElfFile::~ElfFile() { |
| cleanup_tables(); |
| |
| if (_file != nullptr) { |
| fclose(_file); |
| } |
| |
| if (_filepath != nullptr) { |
| os::free((void*) _filepath); |
| _filepath = nullptr; |
| } |
| |
| if (_shdr_string_table != nullptr) { |
| delete _shdr_string_table; |
| _shdr_string_table = nullptr; |
| } |
| |
| if (_next != nullptr) { |
| delete _next; |
| _next = nullptr; |
| } |
| |
| if (_dwarf_file != nullptr) { |
| delete _dwarf_file; |
| _dwarf_file = nullptr; |
| } |
| } |
| |
| void ElfFile::cleanup_tables() { |
| if (_string_tables != nullptr) { |
| delete _string_tables; |
| _string_tables = nullptr; |
| } |
| if (_symbol_tables != nullptr) { |
| delete _symbol_tables; |
| _symbol_tables = nullptr; |
| } |
| if (_funcDesc_table != nullptr) { |
| delete _funcDesc_table; |
| _funcDesc_table = nullptr; |
| } |
| } |
| |
| NullDecoder::decoder_status ElfFile::parse_elf(const char* filepath) { |
| assert(filepath, "null file path"); |
| |
| _file = os::fopen(filepath, "r"); |
| if (_file != nullptr) { |
| return load_tables(); |
| } else { |
| return NullDecoder::file_not_found; |
| } |
| } |
| |
| //Check elf header to ensure the file is valid. |
| bool ElfFile::is_elf_file(Elf_Ehdr& hdr) { |
| return (ELFMAG0 == hdr.e_ident[EI_MAG0] && |
| ELFMAG1 == hdr.e_ident[EI_MAG1] && |
| ELFMAG2 == hdr.e_ident[EI_MAG2] && |
| ELFMAG3 == hdr.e_ident[EI_MAG3] && |
| ELFCLASSNONE != hdr.e_ident[EI_CLASS] && |
| ELFDATANONE != hdr.e_ident[EI_DATA]); |
| } |
| |
| NullDecoder::decoder_status ElfFile::load_tables() { |
| assert(_file, "file not open"); |
| assert(!NullDecoder::is_error(_status), "already in error"); |
| |
| FileReader freader(fd()); |
| // read elf file header |
| if (!freader.read(&_elfHdr, sizeof(_elfHdr))) { |
| return NullDecoder::file_invalid; |
| } |
| |
| // Check signature |
| if (!is_elf_file(_elfHdr)) { |
| return NullDecoder::file_invalid; |
| } |
| |
| // walk elf file's section headers, and load string tables |
| Elf_Shdr shdr; |
| if (!freader.set_position(_elfHdr.e_shoff)) { |
| return NullDecoder::file_invalid; |
| } |
| |
| for (int index = 0; index < _elfHdr.e_shnum; index ++) { |
| if (!freader.read(&shdr, sizeof(shdr))) { |
| return NullDecoder::file_invalid; |
| } |
| |
| if (shdr.sh_type == SHT_STRTAB) { |
| // string tables |
| ElfStringTable* table = new (std::nothrow) ElfStringTable(fd(), shdr, index); |
| if (table == nullptr) { |
| return NullDecoder::out_of_memory; |
| } |
| if (index == _elfHdr.e_shstrndx) { |
| assert(_shdr_string_table == nullptr, "Only set once"); |
| _shdr_string_table = table; |
| } else { |
| add_string_table(table); |
| } |
| } else if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { |
| // symbol tables |
| ElfSymbolTable* table = new (std::nothrow) ElfSymbolTable(fd(), shdr); |
| if (table == nullptr) { |
| return NullDecoder::out_of_memory; |
| } |
| add_symbol_table(table); |
| } |
| } |
| #if defined(PPC64) && !defined(ABI_ELFv2) |
| // Now read the .opd section which contains the PPC64 function descriptor table. |
| // The .opd section is only available on PPC64 (see for example: |
| // http://refspecs.linuxfoundation.org/LSB_3.1.1/LSB-Core-PPC64/LSB-Core-PPC64/specialsections.html) |
| // so this code should do no harm on other platforms but because of performance reasons we only |
| // execute it on PPC64 platforms. |
| // Notice that we can only find the .opd section after we have successfully read in the string |
| // tables in the previous loop, because we need to query the name of each section which is |
| // contained in one of the string tables (i.e. the one with the index m_elfHdr.e_shstrndx). |
| |
| // Reset the file pointer |
| int sect_index = section_by_name(".opd", shdr); |
| |
| if (sect_index == -1) { |
| return NullDecoder::file_invalid; |
| } |
| |
| _funcDesc_table = new (std::nothrow) ElfFuncDescTable(_file, shdr, sect_index); |
| if (_funcDesc_table == nullptr) { |
| return NullDecoder::out_of_memory; |
| } |
| #endif |
| return NullDecoder::no_error; |
| } |
| |
| #if defined(PPC64) && !defined(ABI_ELFv2) |
| int ElfFile::section_by_name(const char* name, Elf_Shdr& hdr) { |
| assert(name != nullptr, "No section name"); |
| size_t len = strlen(name) + 1; |
| char* buf = (char*)os::malloc(len, mtInternal); |
| if (buf == nullptr) { |
| return -1; |
| } |
| |
| assert(_shdr_string_table != nullptr, "Section header string table should be loaded"); |
| ElfStringTable* const table = _shdr_string_table; |
| MarkedFileReader mfd(fd()); |
| if (!mfd.has_mark() || !mfd.set_position(_elfHdr.e_shoff)) return -1; |
| |
| int sect_index = -1; |
| for (int index = 0; index < _elfHdr.e_shnum; index ++) { |
| if (!mfd.read((void*)&hdr, sizeof(hdr))) { |
| break; |
| } |
| if (table->string_at(hdr.sh_name, buf, len)) { |
| if (strncmp(buf, name, len) == 0) { |
| sect_index = index; |
| break; |
| } |
| } |
| } |
| |
| os::free(buf); |
| |
| return sect_index; |
| } |
| #endif |
| |
| bool ElfFile::decode(address addr, char* buf, int buflen, int* offset) { |
| // something already went wrong, just give up |
| if (NullDecoder::is_error(_status)) { |
| return false; |
| } |
| |
| int string_table_index; |
| int pos_in_string_table; |
| int off = INT_MAX; |
| bool found_symbol = false; |
| ElfSymbolTable* symbol_table = _symbol_tables; |
| |
| while (symbol_table != nullptr) { |
| if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off, _funcDesc_table)) { |
| found_symbol = true; |
| break; |
| } |
| symbol_table = symbol_table->next(); |
| } |
| if (!found_symbol) { |
| return false; |
| } |
| |
| ElfStringTable* string_table = get_string_table(string_table_index); |
| |
| if (string_table == nullptr) { |
| _status = NullDecoder::file_invalid; |
| return false; |
| } |
| if (offset) *offset = off; |
| |
| return string_table->string_at(pos_in_string_table, buf, buflen); |
| } |
| |
| void ElfFile::add_symbol_table(ElfSymbolTable* table) { |
| if (_symbol_tables == nullptr) { |
| _symbol_tables = table; |
| } else { |
| table->set_next(_symbol_tables); |
| _symbol_tables = table; |
| } |
| } |
| |
| void ElfFile::add_string_table(ElfStringTable* table) { |
| if (_string_tables == nullptr) { |
| _string_tables = table; |
| } else { |
| table->set_next(_string_tables); |
| _string_tables = table; |
| } |
| } |
| |
| ElfStringTable* ElfFile::get_string_table(int index) { |
| ElfStringTable* p = _string_tables; |
| while (p != nullptr) { |
| if (p->index() == index) return p; |
| p = p->next(); |
| } |
| return nullptr; |
| } |
| |
| // Use unified logging to report errors rather than assert() throughout this method as this code is already part of the error reporting |
| // and the debug symbols might be in an unsupported DWARF version or wrong format. |
| bool ElfFile::get_source_info(const uint32_t offset_in_library, char* filename, const size_t filename_len, int* line, bool is_pc_after_call) { |
| if (!load_dwarf_file()) { |
| // Some ELF libraries do not provide separate .debuginfo files. Check if the current ELF file has the required |
| // DWARF sections. If so, treat the current ELF file as DWARF file. |
| if (!is_valid_dwarf_file()) { |
| DWARF_LOG_ERROR("Failed to load DWARF file for library %s or find DWARF sections directly inside it.", _filepath); |
| return false; |
| } |
| DWARF_LOG_INFO("No separate .debuginfo file for library %s. It already contains the required DWARF sections.", |
| _filepath); |
| if (!create_new_dwarf_file(_filepath)) { |
| return false; |
| } |
| } |
| |
| // Store result in filename and line pointer. |
| if (!_dwarf_file->get_filename_and_line_number(offset_in_library, filename, filename_len, line, is_pc_after_call)) { |
| DWARF_LOG_ERROR("Failed to retrieve file and line number information for %s at offset: " UINT32_FORMAT_X_0, _filepath, |
| offset_in_library); |
| return false; |
| } |
| return true; |
| } |
| |
| bool ElfFile::is_valid_dwarf_file() const { |
| Elf_Shdr shdr; |
| return read_section_header(".debug_abbrev", shdr) && read_section_header(".debug_aranges", shdr) |
| && read_section_header(".debug_info", shdr) && read_section_header(".debug_line", shdr); |
| } |
| |
| // (1) Load the debuginfo file from the path specified in this ELF file in the .gnu_debuglink section. |
| // Adapted from Serviceability Agent. |
| bool ElfFile::load_dwarf_file() { |
| if (_dwarf_file != nullptr) { |
| return true; // Already opened. |
| } |
| |
| DebugInfo debug_info; |
| if (!read_debug_info(&debug_info)) { |
| DWARF_LOG_DEBUG("Could not read debug info from .gnu_debuglink section"); |
| return false; |
| } |
| |
| DwarfFilePath dwarf_file_path(debug_info); |
| return load_dwarf_file_from_same_directory(dwarf_file_path) |
| || load_dwarf_file_from_env_var_path(dwarf_file_path) |
| || load_dwarf_file_from_debug_sub_directory(dwarf_file_path) |
| || load_dwarf_file_from_usr_lib_debug(dwarf_file_path); |
| } |
| |
| // Read .gnu_debuglink section which contains: |
| // Filename (null terminated) + 0-3 padding bytes (to 4 byte align) + CRC (4 bytes) |
| bool ElfFile::read_debug_info(DebugInfo* debug_info) const { |
| Elf_Shdr shdr; |
| if (!read_section_header(".gnu_debuglink", shdr)) { |
| DWARF_LOG_DEBUG("Failed to read the .gnu_debuglink header."); |
| return false; |
| } |
| |
| if (shdr.sh_size % 4 != 0) { |
| DWARF_LOG_ERROR(".gnu_debuglink section is not 4 byte aligned (i.e. file is corrupted)"); |
| return false; |
| } |
| |
| MarkedFileReader mfd(fd()); |
| if (!mfd.has_mark() || !mfd.set_position(_elfHdr.e_shoff)) { |
| return false; |
| } |
| |
| uint64_t filename_max_len = shdr.sh_size - DebugInfo::CRC_LEN; |
| mfd.set_position(shdr.sh_offset); |
| if (!mfd.read(&debug_info->_dwarf_filename, filename_max_len)) { |
| return false; |
| } |
| |
| if (debug_info->_dwarf_filename[filename_max_len - 1] != '\0') { |
| // Filename not null-terminated (i.e. overflowed). |
| DWARF_LOG_ERROR("Dwarf filename is not null-terminated"); |
| return false; |
| } |
| |
| return mfd.read(&debug_info->_crc, DebugInfo::CRC_LEN); |
| } |
| |
| bool ElfFile::DwarfFilePath::set(const char* src) { |
| int bytes_written = jio_snprintf(_path, MAX_DWARF_PATH_LENGTH, "%s", src); |
| if (bytes_written < 0 || bytes_written >= MAX_DWARF_PATH_LENGTH) { |
| DWARF_LOG_ERROR("Dwarf file path buffer is too small"); |
| return false; |
| } |
| update_null_terminator_index(); |
| return check_valid_path(); // Sanity check |
| } |
| |
| bool ElfFile::DwarfFilePath::set_after_last_slash(const char* src) { |
| char* last_slash = strrchr(_path, *os::file_separator()); |
| if (last_slash == nullptr) { |
| // Should always find a slash. |
| return false; |
| } |
| |
| uint16_t index_after_slash = (uint16_t)(last_slash + 1 - _path); |
| return copy_to_path_index(index_after_slash, src); |
| } |
| |
| bool ElfFile::DwarfFilePath::append(const char* src) { |
| return copy_to_path_index(_null_terminator_index, src); |
| } |
| |
| bool ElfFile::DwarfFilePath::copy_to_path_index(uint16_t index_in_path, const char* src) { |
| if (index_in_path >= MAX_DWARF_PATH_LENGTH - 1) { |
| // Should not override '\0' at _path[MAX_DWARF_PATH_LENGTH - 1] |
| DWARF_LOG_ERROR("Dwarf file path buffer is too small"); |
| return false; |
| } |
| |
| uint16_t max_len = MAX_DWARF_PATH_LENGTH - index_in_path; |
| int bytes_written = jio_snprintf(_path + index_in_path, max_len, "%s", src); |
| if (bytes_written < 0 || bytes_written >= max_len) { |
| DWARF_LOG_ERROR("Dwarf file path buffer is too small"); |
| return false; |
| } |
| update_null_terminator_index(); |
| return check_valid_path(); // Sanity check |
| } |
| |
| // Try to load the dwarf file from the same directory as the library file. |
| bool ElfFile::load_dwarf_file_from_same_directory(DwarfFilePath& dwarf_file_path) { |
| if (!dwarf_file_path.set(_filepath) |
| || !dwarf_file_path.set_filename_after_last_slash()) { |
| return false; |
| } |
| return open_valid_debuginfo_file(dwarf_file_path); |
| } |
| |
| // Try to load the dwarf file from a user specified path in environmental variable _JVM_DWARF_PATH. |
| bool ElfFile::load_dwarf_file_from_env_var_path(DwarfFilePath& dwarf_file_path) { |
| const char* dwarf_path_from_env = ::getenv("_JVM_DWARF_PATH"); |
| if (dwarf_path_from_env != nullptr) { |
| DWARF_LOG_DEBUG("_JVM_DWARF_PATH: %s", dwarf_path_from_env); |
| return (load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/lib/server/") |
| || load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/lib/") |
| || load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/bin/") |
| || load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/")); |
| } |
| return false; |
| } |
| |
| bool ElfFile::load_dwarf_file_from_env_path_folder(DwarfFilePath& dwarf_file_path, const char* dwarf_path_from_env, |
| const char* folder) { |
| if (!dwarf_file_path.set(dwarf_path_from_env) |
| || !dwarf_file_path.append(folder) |
| || !dwarf_file_path.append(dwarf_file_path.filename())) { |
| DWARF_LOG_ERROR("Dwarf file path buffer is too small"); |
| return false; |
| } |
| return open_valid_debuginfo_file(dwarf_file_path); |
| } |
| |
| // Try to load the dwarf file from a subdirectory named .debug within the directory of the library file. |
| bool ElfFile::load_dwarf_file_from_debug_sub_directory(DwarfFilePath& dwarf_file_path) { |
| if (!dwarf_file_path.set(_filepath) |
| || !dwarf_file_path.set_after_last_slash(".debug/") |
| || !dwarf_file_path.append(dwarf_file_path.filename())) { |
| DWARF_LOG_ERROR("Dwarf file path buffer is too small"); |
| return false; |
| } |
| return open_valid_debuginfo_file(dwarf_file_path); |
| } |
| |
| // Try to load the dwarf file from /usr/lib/debug + the full pathname. |
| bool ElfFile::load_dwarf_file_from_usr_lib_debug(DwarfFilePath& dwarf_file_path) { |
| if (!dwarf_file_path.set(USR_LIB_DEBUG_DIRECTORY) |
| || !dwarf_file_path.append(_filepath) |
| || !dwarf_file_path.set_filename_after_last_slash()) { |
| DWARF_LOG_ERROR("Dwarf file path buffer is too small"); |
| return false; |
| } |
| return open_valid_debuginfo_file(dwarf_file_path); |
| } |
| |
| bool ElfFile::read_section_header(const char* name, Elf_Shdr& hdr) const { |
| if (_shdr_string_table == nullptr) { |
| assert(false, "section header string table should be loaded"); |
| return false; |
| } |
| const uint8_t buf_len = 24; |
| char buf[buf_len]; |
| size_t len = strlen(name) + 1; |
| if (len > buf_len) { |
| DWARF_LOG_ERROR("Section header name buffer is too small: Required: %zu, Found: %d", len, buf_len); |
| return false; |
| } |
| |
| MarkedFileReader mfd(fd()); |
| if (!mfd.has_mark() || !mfd.set_position(_elfHdr.e_shoff)) { |
| return false; |
| } |
| |
| for (int index = 0; index < _elfHdr.e_shnum; index++) { |
| if (!mfd.read((void*)&hdr, sizeof(hdr))) { |
| return false; |
| } |
| if (_shdr_string_table->string_at(hdr.sh_name, buf, buf_len)) { |
| if (strncmp(buf, name, buf_len) == 0) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| // Taken from https://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html#Separate-Debug-Files |
| static const uint32_t crc32_table[256] = { |
| 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, |
| 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, |
| 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, |
| 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, |
| 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, |
| 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, |
| 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, |
| 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, |
| 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, |
| 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, |
| 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, |
| 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, |
| 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, |
| 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, |
| 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, |
| 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, |
| 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, |
| 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, |
| 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, |
| 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, |
| 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, |
| 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, |
| 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, |
| 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, |
| 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, |
| 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, |
| 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, |
| 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, |
| 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, |
| 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, |
| 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, |
| 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, |
| 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, |
| 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, |
| 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, |
| 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, |
| 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, |
| 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, |
| 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, |
| 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, |
| 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, |
| 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, |
| 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, |
| 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, |
| 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, |
| 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, |
| 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, |
| 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, |
| 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, |
| 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, |
| 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, |
| 0x2d02ef8d |
| }; |
| |
| bool ElfFile::open_valid_debuginfo_file(const DwarfFilePath& dwarf_file_path) { |
| if (_dwarf_file != nullptr) { |
| // Already opened. |
| return true; |
| } |
| |
| const char* filepath = dwarf_file_path.path(); |
| FILE* file = fopen(filepath, "r"); |
| if (file == nullptr) { |
| DWARF_LOG_DEBUG("Could not open dwarf file %s (%s)", filepath, os::strerror(errno)); |
| return false; |
| } |
| |
| uint32_t file_crc = get_file_crc(file); |
| fclose(file); // Close it here to reopen it again when the DwarfFile object is created below. |
| |
| if (dwarf_file_path.crc() != file_crc) { |
| // Must be equal, otherwise the file is corrupted. |
| DWARF_LOG_ERROR("CRC did not match. Expected: " INT32_FORMAT_X_0 ", found: " INT32_FORMAT_X_0, dwarf_file_path.crc(), |
| file_crc); |
| return false; |
| } |
| return create_new_dwarf_file(filepath); |
| } |
| |
| uint32_t ElfFile::get_file_crc(FILE* const file) { |
| uint32_t file_crc = 0; |
| uint8_t buffer[8 * 1024]; |
| MarkedFileReader reader(file); |
| while (true) { |
| size_t len = reader.read_buffer(buffer, sizeof(buffer)); |
| if (len == 0) { |
| break; |
| } |
| file_crc = gnu_debuglink_crc32(file_crc, buffer, len); |
| } |
| return file_crc; |
| } |
| |
| // The CRC used in gnu_debuglink, retrieved from |
| // http://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html#Separate-Debug-Files. |
| uint32_t ElfFile::gnu_debuglink_crc32(uint32_t crc, uint8_t* buf, const size_t len) { |
| crc = ~crc; |
| for (uint8_t* end = buf + len; buf < end; buf++) { |
| crc = crc32_table[(crc ^ *buf) & 0xffu] ^ (crc >> 8u); |
| } |
| return ~crc; |
| } |
| |
| bool ElfFile::create_new_dwarf_file(const char* filepath) { |
| DWARF_LOG_SUMMARY("Open DWARF file: %s", filepath); |
| _dwarf_file = new (std::nothrow) DwarfFile(filepath); |
| if (_dwarf_file == nullptr) { |
| DWARF_LOG_ERROR("Failed to create new DwarfFile object for %s.", _filepath); |
| return false; |
| } |
| if (!_dwarf_file->is_valid_dwarf_file()) { |
| DWARF_LOG_ERROR("Did not find required DWARF sections in %s", filepath); |
| return false; |
| } |
| return true; |
| } |
| |
| // Starting point of reading line number and filename information from the DWARF file. |
| bool DwarfFile::get_filename_and_line_number(const uint32_t offset_in_library, char* filename, const size_t filename_len, |
| int* line, const bool is_pc_after_call) { |
| DebugAranges debug_aranges(this); |
| uint32_t compilation_unit_offset = 0; // 4-bytes for 32-bit DWARF |
| if (!debug_aranges.find_compilation_unit_offset(offset_in_library, &compilation_unit_offset)) { |
| DWARF_LOG_ERROR("Failed to find .debug_info offset for the compilation unit."); |
| return false; |
| } |
| DWARF_LOG_INFO(".debug_info offset: " INT32_FORMAT_X_0, compilation_unit_offset); |
| |
| CompilationUnit compilation_unit(this, compilation_unit_offset); |
| uint32_t debug_line_offset = 0; // 4-bytes for 32-bit DWARF |
| if (!compilation_unit.find_debug_line_offset(&debug_line_offset)) { |
| DWARF_LOG_ERROR("Failed to find .debug_line offset for the line number program."); |
| return false; |
| } |
| DWARF_LOG_INFO(".debug_line offset: " INT32_FORMAT_X_0, debug_line_offset); |
| |
| LineNumberProgram line_number_program(this, offset_in_library, debug_line_offset, is_pc_after_call); |
| if (!line_number_program.find_filename_and_line_number(filename, filename_len, line)) { |
| DWARF_LOG_ERROR("Failed to process the line number program correctly."); |
| return false; |
| } |
| return true; |
| } |
| |
| // (2) The .debug_aranges section contains a number of entries/sets. Each set contains one or multiple address range descriptors of the |
| // form [beginning_address, beginning_address+length). Start reading these sets and their descriptors until we find one that contains |
| // 'offset_in_library'. Read the debug_info_offset field from the header of this set which defines the offset for the compilation unit. |
| // This process is described in section 6.1.2 of the DWARF 4 spec. |
| bool DwarfFile::DebugAranges::find_compilation_unit_offset(const uint32_t offset_in_library, uint32_t* compilation_unit_offset) { |
| if (!read_section_header()) { |
| DWARF_LOG_ERROR("Failed to read a .debug_aranges header."); |
| return false; |
| } |
| |
| DebugArangesSetHeader set_header; |
| bool found_matching_set = false; |
| while (_reader.has_bytes_left()) { |
| // Read multiple sets and therefore multiple headers. |
| if (!read_set_header(set_header)) { |
| DWARF_LOG_ERROR("Failed to read a .debug_aranges header."); |
| return false; |
| } |
| |
| if (!read_address_descriptors(set_header, offset_in_library, found_matching_set)) { |
| return false; |
| } |
| |
| if (found_matching_set) { |
| // Found the correct set, read the debug_info_offset from the header of this set. |
| DWARF_LOG_INFO(".debug_aranges offset: " UINT32_FORMAT, (uint32_t)_reader.get_position()); |
| *compilation_unit_offset = set_header._debug_info_offset; |
| return true; |
| } |
| } |
| |
| assert(false, "No address descriptor found containing offset_in_library."); |
| return false; |
| } |
| |
| bool DwarfFile::DebugAranges::read_section_header() { |
| Elf_Shdr shdr; |
| if (!_dwarf_file->read_section_header(".debug_aranges", shdr)) { |
| return false; |
| } |
| |
| _section_start_address = shdr.sh_offset; |
| _reader.set_max_pos(shdr.sh_offset + shdr.sh_size); |
| return _reader.set_position(shdr.sh_offset); |
| } |
| |
| // Parse set header as specified in section 6.1.2 of the DWARF 4 spec. |
| bool DwarfFile::DebugAranges::read_set_header(DebugArangesSetHeader& header) { |
| if (!_reader.read_dword(&header._unit_length) || header._unit_length == 0xFFFFFFFF) { |
| // For 64-bit DWARF, the first 32-bit value is 0xFFFFFFFF. The current implementation only supports 32-bit DWARF |
| // format since GCC only emits 32-bit DWARF. |
| DWARF_LOG_ERROR("64-bit DWARF is not supported for .debug_aranges") |
| return false; |
| } |
| |
| _entry_end = _reader.get_position() + header._unit_length; |
| |
| if (!_reader.read_word(&header._version) || header._version != 2) { |
| // DWARF 4 uses version 2 as specified in Appendix F of the DWARF 4 spec. |
| DWARF_LOG_ERROR(".debug_aranges in unsupported DWARF version %" PRIu16, header._version) |
| return false; |
| } |
| |
| if (!_reader.read_dword(&header._debug_info_offset)) { |
| return false; |
| } |
| |
| if (!_reader.read_byte(&header._address_size) || header._address_size != DwarfFile::ADDRESS_SIZE) { |
| // Addresses must be either 4 bytes for 32-bit architectures or 8 bytes for 64-bit architectures. |
| DWARF_LOG_ERROR(".debug_aranges specifies wrong address size %" PRIu8, header._address_size); |
| return false; |
| } |
| |
| if (!_reader.read_byte(&header._segment_size) || header._segment_size != 0) { |
| // Segment size should be 0. |
| DWARF_LOG_ERROR(".debug_aranges segment size is non-zero: %" PRIu8, header._segment_size); |
| return false; |
| } |
| |
| // We must align to twice the address size. |
| uint8_t alignment = DwarfFile::ADDRESS_SIZE * 2; |
| uint8_t padding = alignment - (_reader.get_position() - _section_start_address) % alignment; |
| return _reader.move_position(padding); |
| } |
| |
| bool DwarfFile::DebugAranges::read_address_descriptors(const DwarfFile::DebugAranges::DebugArangesSetHeader& header, |
| const uint32_t offset_in_library, bool& found_matching_set) { |
| AddressDescriptor descriptor; |
| do { |
| if (!read_address_descriptor(descriptor)) { |
| return false; |
| } |
| |
| if (does_match_offset(offset_in_library, descriptor)) { |
| found_matching_set = true; |
| return true; |
| } |
| } while (!is_terminating_entry(header, descriptor) && _reader.has_bytes_left()); |
| |
| // Set does not match offset_in_library. Continue with next. |
| return true; |
| } |
| |
| bool DwarfFile::DebugAranges::read_address_descriptor(AddressDescriptor& descriptor) { |
| return _reader.read_address_sized(&descriptor.beginning_address) |
| && _reader.read_address_sized(&descriptor.range_length); |
| } |
| |
| bool DwarfFile::DebugAranges::does_match_offset(const uint32_t offset_in_library, const AddressDescriptor& descriptor) { |
| return descriptor.beginning_address <= offset_in_library |
| && offset_in_library < descriptor.beginning_address + descriptor.range_length; |
| } |
| |
| bool DwarfFile::DebugAranges::is_terminating_entry(const DwarfFile::DebugAranges::DebugArangesSetHeader& header, |
| const AddressDescriptor& descriptor) { |
| bool is_terminating = _reader.get_position() >= _entry_end; |
| assert(!is_terminating || (descriptor.beginning_address == 0 && descriptor.range_length == 0), |
| "a terminating entry needs a pair of zero"); |
| return is_terminating; |
| } |
| |
| // Find the .debug_line offset for the line number program by reading from the .debug_abbrev and .debug_info section. |
| bool DwarfFile::CompilationUnit::find_debug_line_offset(uint32_t* debug_line_offset) { |
| // (3a,b) |
| if (!read_header()) { |
| DWARF_LOG_ERROR("Failed to read the compilation unit header."); |
| return false; |
| } |
| |
| // (3c) Read the abbreviation code immediately following the compilation unit header which is an offset to the |
| // correct abbreviation table in .debug_abbrev for this compilation unit. |
| uint64_t abbrev_code; |
| if (!_reader.read_uleb128(&abbrev_code)) { |
| return false; |
| } |
| |
| DebugAbbrev debug_abbrev(_dwarf_file, this); |
| if (!debug_abbrev.read_section_header(_header._debug_abbrev_offset)) { |
| DWARF_LOG_ERROR("Failed to read the .debug_abbrev header at " UINT32_FORMAT_X_0, _header._debug_abbrev_offset); |
| return false; |
| } |
| if (!debug_abbrev.find_debug_line_offset(abbrev_code)) { |
| return false; |
| } |
| *debug_line_offset = _debug_line_offset; // Result was stored in _debug_line_offset. |
| return true; |
| } |
| |
| // (3a) Parse header as specified in section 7.5.1.1 of the DWARF 4 spec. |
| bool DwarfFile::CompilationUnit::read_header() { |
| Elf_Shdr shdr; |
| if (!_dwarf_file->read_section_header(".debug_info", shdr)) { |
| DWARF_LOG_ERROR("Failed to read the .debug_info section header."); |
| return false; |
| } |
| |
| if (!_reader.set_position(shdr.sh_offset + _compilation_unit_offset)) { |
| return false; |
| } |
| |
| if (!_reader.read_dword(&_header._unit_length) || _header._unit_length == 0xFFFFFFFF) { |
| // For 64-bit DWARF, the first 32-bit value is 0xFFFFFFFF. The current implementation only supports 32-bit DWARF |
| // format since GCC only emits 32-bit DWARF. |
| DWARF_LOG_ERROR("64-bit DWARF is not supported for .debug_info") |
| return false; |
| } |
| |
| if (!_reader.read_word(&_header._version) || _header._version != 4) { |
| // DWARF 4 uses version 4 as specified in Appendix F of the DWARF 4 spec. |
| DWARF_LOG_ERROR(".debug_info in unsupported DWARF version %" PRIu16, _header._version) |
| return false; |
| } |
| |
| // (3b) Offset into .debug_abbrev section. |
| if (!_reader.read_dword(&_header._debug_abbrev_offset)) { |
| return false; |
| } |
| |
| if (!_reader.read_byte(&_header._address_size) || _header._address_size != DwarfFile::ADDRESS_SIZE) { |
| // Addresses must be either 4 bytes for 32-bit architectures or 8 bytes for 64-bit architectures. |
| DWARF_LOG_ERROR(".debug_info specifies wrong address size %" PRIu8, _header._address_size); |
| return false; |
| } |
| |
| // Add because _unit_length is not included. |
| _reader.set_max_pos(_reader.get_position() + _header._unit_length + 4); |
| return true; |
| } |
| |
| bool DwarfFile::DebugAbbrev::read_section_header(uint32_t debug_abbrev_offset) { |
| Elf_Shdr shdr; |
| if (!_dwarf_file->read_section_header(".debug_abbrev", shdr)) { |
| return false; |
| } |
| |
| _reader.set_max_pos(shdr.sh_offset + shdr.sh_size); |
| if (!_reader.set_position(shdr.sh_offset + debug_abbrev_offset)) { |
| return false; |
| } |
| return true; |
| } |
| |
| // (3d) The abbreviations table for a compilation unit consists of a series of abbreviation declarations. Each declaration |
| // specifies an abbrev code and a tag. Parse all declarations until we find the declaration which matches 'abbrev_code'. |
| // Read the attribute values from the compilation unit in .debug_info by using the format described in the declaration. |
| // This process is described in section 7.5 and 7.5.3 of the DWARF 4 spec. |
| bool DwarfFile::DebugAbbrev::find_debug_line_offset(const uint64_t abbrev_code) { |
| DWARF_LOG_TRACE("Series of declarations [code, tag]:"); |
| AbbreviationDeclaration declaration; |
| while (_reader.has_bytes_left()) { |
| if (!read_declaration(declaration)) { |
| return false; |
| } |
| |
| DWARF_LOG_TRACE(" Series of attributes [name, form]:"); |
| if (declaration._abbrev_code == abbrev_code) { |
| // Found the correct declaration. |
| if (is_wrong_or_unsupported_format(declaration)) { |
| return false; |
| } |
| DWARF_LOG_INFO(".debug_abbrev offset: " UINT32_FORMAT_X_0, (uint32_t)_reader.get_position()); |
| DWARF_LOG_TRACE(" Read the following attribute values from compilation unit:"); |
| return read_attribute_specifications(true); |
| } else { |
| // Not the correct declaration. Read its attributes and continue with the next declaration. |
| if (!read_attribute_specifications(false)) { |
| return false; |
| } |
| } |
| } |
| |
| assert(false, ".debug_line offset not found"); |
| return false; |
| } |
| |
| bool DwarfFile::DebugAbbrev::read_declaration(DwarfFile::DebugAbbrev::AbbreviationDeclaration& declaration) { |
| if (!_reader.read_uleb128(&declaration._abbrev_code)) { |
| return false; |
| } |
| |
| if (declaration._abbrev_code == 0) { |
| // Reached the end of the abbreviation declarations for this compilation unit. |
| DWARF_LOG_ERROR("abbrev_code not found in any declaration"); |
| return false; |
| } |
| |
| if (!_reader.read_uleb128(&declaration._tag) || !_reader.read_byte(&declaration._has_children)) { |
| return false; |
| } |
| |
| DWARF_LOG_TRACE("Code: " UINT64_FORMAT_X ", Tag: " UINT64_FORMAT_X, declaration._abbrev_code, declaration._tag); |
| return true; |
| } |
| |
| bool DwarfFile::DebugAbbrev::is_wrong_or_unsupported_format(const DwarfFile::DebugAbbrev::AbbreviationDeclaration& declaration) { |
| if (declaration._tag != DW_TAG_compile_unit) { |
| // Is not DW_TAG_compile_unit as specified in Figure 18 in section 7.5 of the DWARF 4 spec. It could also |
| // be DW_TAG_partial_unit (0x3c) which is currently not supported by this parser. |
| DWARF_LOG_ERROR("Found unsupported tag in compilation unit: " UINT64_FORMAT_X, declaration._tag); |
| return true; |
| } |
| if (declaration._has_children != DW_CHILDREN_yes) { |
| DWARF_LOG_ERROR("Must have children but none specified"); |
| return true; |
| } |
| return false; |
| } |
| |
| // Read the attribute names and forms which define the actual attribute values that follow the abbrev code in the compilation unit. All |
| // attributes need to be read from the compilation unit until we reach the DW_AT_stmt_list attribute which specifies the offset for the |
| // line number program into the .debug_line section. The offset is stored in the _debug_line_offset field of the compilation unit. |
| bool DwarfFile::DebugAbbrev::read_attribute_specifications(const bool is_DW_TAG_compile_unit) { |
| AttributeSpecification attribute_specification; |
| while (_reader.has_bytes_left()) { |
| if (!read_attribute_specification(attribute_specification)) { |
| return false; |
| } |
| |
| if (is_terminating_specification(attribute_specification)) { |
| // Parsed all attributes of this declaration. |
| if (is_DW_TAG_compile_unit) { |
| DWARF_LOG_ERROR("Did not find DW_AT_stmt_list in .debug_abbrev"); |
| return false; |
| } else { |
| // Continue with next declaration if this was not DW_TAG_compile_unit. |
| return true; |
| } |
| } |
| |
| if (is_DW_TAG_compile_unit) { |
| // Read attribute from compilation unit |
| if (attribute_specification._name == DW_AT_stmt_list) { |
| // This attribute represents the .debug_line offset. Read it and then stop parsing. |
| return _compilation_unit->read_attribute_value(attribute_specification._form, true); |
| } else { |
| // Not DW_AT_stmt_list, read it and continue with the next attribute. |
| if (!_compilation_unit->read_attribute_value(attribute_specification._form, false)) { |
| return false; |
| } |
| } |
| } |
| } |
| |
| assert(false, ".debug_abbrev section appears to be corrupted"); |
| return false; |
| } |
| |
| bool DwarfFile::DebugAbbrev::read_attribute_specification(DwarfFile::DebugAbbrev::AttributeSpecification& specification) { |
| bool result = _reader.read_uleb128(&specification._name) && _reader.read_uleb128(&specification._form); |
| DWARF_LOG_TRACE(" Name: " UINT64_FORMAT_X ", Form: " UINT64_FORMAT_X, |
| specification._name, specification._form); |
| return result; |
| } |
| |
| bool DwarfFile::DebugAbbrev::is_terminating_specification(const DwarfFile::DebugAbbrev::AttributeSpecification& specification) { |
| return specification._name == 0 && specification._form == 0; |
| } |
| |
| |
| // (3e) Read the actual attribute values from the compilation unit in the .debug_info section. Each attribute has an encoding |
| // that specifies which values need to be read for it. This is specified in section 7.5.4 of the DWARF 4 spec. |
| // If is_DW_AT_stmt_list_attribute is: |
| // - False: Ignore the read attribute value. |
| // - True: We are going to read the attribute value of the DW_AT_stmt_list attribute which specifies the offset into the |
| // .debug_line section for the line number program. Store this offset in the _debug_line_offset field. |
| bool DwarfFile::CompilationUnit::read_attribute_value(const uint64_t attribute_form, const bool is_DW_AT_stmt_list_attribute) { |
| // Reset to the stored _cur_pos of the reader since the DebugAbbrev reader changed the index into the file with its reader. |
| _reader.update_to_stored_position(); |
| uint8_t next_byte = 0; |
| uint16_t next_word = 0; |
| uint32_t next_dword = 0; |
| uint64_t next_qword = 0; |
| |
| switch (attribute_form) { |
| case DW_FORM_addr: |
| // Move position by the size of an address. |
| _reader.move_position(DwarfFile::ADDRESS_SIZE); |
| break; |
| case DW_FORM_block2: |
| // New position: length + data length (next_word) |
| if (!_reader.read_word(&next_word) || !_reader.move_position(next_word)) { |
| return false; |
| } |
| break; |
| case DW_FORM_block4: |
| // New position: length + data length (next_dword) |
| if (!_reader.read_dword(&next_dword) || !_reader.move_position(next_dword)) { |
| return false; |
| } |
| break; |
| case DW_FORM_data2: |
| case DW_FORM_ref2: |
| if (!_reader.move_position(2)) { |
| return false; |
| } |
| break; |
| case DW_FORM_data4: |
| case DW_FORM_strp: // 4 bytes in 32-bit DWARF |
| case DW_FORM_ref_addr: // second type of reference: 4 bytes in 32-bit DWARF |
| case DW_FORM_ref4: |
| if (!_reader.move_position(4)) { |
| return false; |
| } |
| break; |
| case DW_FORM_data8: |
| case DW_FORM_ref8: |
| case DW_FORM_ref_sig8: // 64-bit type signature |
| if (!_reader.move_position(8)) { |
| return false; |
| } |
| break; |
| case DW_FORM_string: |
| if (!_reader.read_string()) { |
| return false; |
| } |
| break; |
| case DW_FORM_block: |
| case DW_FORM_exprloc: |
| // New position: length + data length (next_qword). |
| if (!_reader.read_uleb128(&next_qword) || !_reader.move_position(next_qword)) { |
| return false; |
| } |
| break; |
| case DW_FORM_block1: |
| // New position: length + data length (next_byte). |
| if (!_reader.read_byte(&next_byte) || !_reader.move_position(next_byte)) { |
| return false; |
| } |
| break; |
| case DW_FORM_data1: |
| case DW_FORM_ref1: |
| case DW_FORM_flag: |
| case DW_FORM_flag_present: |
| if (!_reader.move_position(1)) { |
| return false; |
| } |
| break; |
| case DW_FORM_sdata: |
| case DW_FORM_udata: |
| case DW_FORM_ref_udata: |
| if (!_reader.read_uleb128(&next_qword)) { |
| return false; |
| } |
| break; |
| case DW_FORM_indirect: |
| // Should not be used and therefore is not supported by this parser. |
| DWARF_LOG_ERROR("DW_FORM_indirect is not supported."); |
| return false; |
| case DW_FORM_sec_offset: |
| if (is_DW_AT_stmt_list_attribute) { |
| // DW_AT_stmt_list has the DW_FORM_sec_offset attribute encoding. Store the result in _debug_line_offset. |
| // 4 bytes for 32-bit DWARF. |
| DWARF_LOG_TRACE(" Name: DW_AT_stmt_list, Form: DW_FORM_sec_offset"); |
| DWARF_LOG_TRACE(" Reading .debug_line offset from compilation unit at " UINT32_FORMAT_X_0, |
| (uint32_t)_reader.get_position()); |
| if (!_reader.read_dword(&_debug_line_offset)) { |
| return false; |
| } |
| break; |
| } else { |
| if (!_reader.move_position(DwarfFile::DWARF_SECTION_OFFSET_SIZE)) { |
| return false; |
| } |
| break; |
| } |
| default: |
| assert(false, "Unknown DW_FORM_* attribute encoding."); |
| return false; |
| } |
| // Reset the index into the file to the original position where the DebugAbbrev reader stopped reading before calling this method. |
| _reader.reset_to_previous_position(); |
| return true; |
| } |
| |
| bool DwarfFile::LineNumberProgram::find_filename_and_line_number(char* filename, const size_t filename_len, int* line) { |
| if (!read_header()) { |
| DWARF_LOG_ERROR("Failed to parse the line number program header correctly."); |
| return false; |
| } |
| return run_line_number_program(filename, filename_len, line); |
| } |
| |
| // Parsing header as specified in section 6.2.4 of DWARF 4 spec. We do not read the file_names field, yet. |
| bool DwarfFile::LineNumberProgram::read_header() { |
| Elf_Shdr shdr; |
| if (!_dwarf_file->read_section_header(".debug_line", shdr)) { |
| DWARF_LOG_ERROR("Failed to read the .debug_line section header."); |
| return false; |
| } |
| |
| if (!_reader.set_position(shdr.sh_offset + _debug_line_offset)) { |
| return false; |
| } |
| |
| if (!_reader.read_dword(&_header._unit_length) || _header._unit_length == 0xFFFFFFFF) { |
| // For 64-bit DWARF, the first 32-bit value is 0xFFFFFFFF. The current implementation only supports 32-bit DWARF |
| // format since GCC only emits 32-bit DWARF. |
| DWARF_LOG_ERROR("64-bit DWARF is not supported for .debug_line") |
| return false; |
| } |
| |
| if (!_reader.read_word(&_header._version) || _header._version < 2 || _header._version > 4) { |
| // DWARF 3 uses version 3 and DWARF 4 uses version 4 as specified in Appendix F of the DWARF 3 and 4 spec, respectively. |
| // For some reason, GCC is not following the standard here. While GCC emits DWARF 4 for the other parsed sections, |
| // it chooses a different DWARF standard for .debug_line based on the GCC version: |
| // - GCC 8 and earlier: .debug_line is in DWARF 2 format (= version 2). |
| // - GCC 9 and 10: .debug_line is in DWARF 3 format (= version 3). |
| // - GCC 11: .debug_line is in DWARF 4 format (= version 4). |
| DWARF_LOG_ERROR(".debug_line in unsupported DWARF version %" PRIu16, _header._version) |
| return false; |
| } |
| |
| if (!_reader.read_dword(&_header._header_length)) { |
| return false; |
| } |
| |
| // To ensure not to read too many bytes in case of file corruption when reading the path_names field. |
| _reader.set_max_pos(_reader.get_position() + _header._header_length); |
| |
| if (!_reader.read_byte(&_header._minimum_instruction_length)) { |
| return false; |
| } |
| |
| if (_header._version == 4) { |
| if (!_reader.read_byte(&_header._maximum_operations_per_instruction)) { |
| return false; |
| } |
| } |
| |
| if (!_reader.read_byte(&_header._default_is_stmt)) { |
| return false; |
| } |
| |
| if (!_reader.read_byte(&_header._line_base)) { |
| return false; |
| } |
| |
| if (!_reader.read_byte(&_header._line_range)) { |
| return false; |
| } |
| |
| if (!_reader.read_byte(&_header._opcode_base) || _header._opcode_base - 1 != 12) { |
| // There are 12 standard opcodes for DWARF 3 and 4. |
| DWARF_LOG_ERROR("Wrong number of opcodes: %" PRIu8, _header._opcode_base) |
| return false; |
| } |
| |
| for (uint8_t i = 0; i < _header._opcode_base - 1; i++) { |
| if (!_reader.read_byte(&_header._standard_opcode_lengths[i])) { |
| return false; |
| } |
| } |
| |
| // Read field include_directories which is a sequence of path names. These are terminated by a single null byte. |
| // We do not care about them, just read the strings and move on. |
| while (_reader.read_string()) { } |
| |
| // Delay reading file_names until we found the correct file index in the line number program. Store the position where |
| // the file names start to parse them later. We directly jump to the line number program which starts at offset |
| // header_size (=HEADER_DESCRIPTION_BYTES + _header_length) + _debug_line_offset |
| _header._file_names_offset = _reader.get_position(); |
| uint32_t header_size = LineNumberProgramHeader::HEADER_DESCRIPTION_BYTES + _header._header_length; |
| if (!_reader.set_position(shdr.sh_offset + header_size + _debug_line_offset)) { |
| return false; |
| } |
| |
| // Now reset the max position to where the line number information for this compilation unit ends (i.e. where the state |
| // machine gets terminated). Add 4 bytes to the offset because the size of the _unit_length field is not included in this |
| // value. |
| _reader.set_max_pos(shdr.sh_offset + _debug_line_offset + _header._unit_length + 4); |
| return true; |
| } |
| |
| // Create the line number information matrix as described in section 6.2 of the DWARF 4 spec. Try to find the correct entry |
| // by comparing the address register belonging to each matrix row with _offset_in_library. Once it is found, we can read |
| // the line number from the line register and the filename by parsing the file_names list from the header until we reach |
| // the correct filename as specified by the file register. |
| // |
| // If space was not a problem, the .debug_line section could provide a large matrix that contains an entry for each |
| // compiler instruction that contains the line number, the column number, the filename etc. But that's impractical. |
| // Two techniques optimize such a matrix: |
| // (1) If two offsets share the same file, line and column (and discriminator) information, the row is dropped. |
| // (2) We store a stream of bytes that represent opcodes to be executed in a well-defined state machine language |
| // instead of actually storing the entire matrix row by row. |
| // |
| // Let's consider a simple example: |
| // 25: int iFld = 42; |
| // 26: |
| // 27: void bar(int i) { |
| // 28: } |
| // 29: |
| // 30: void foo() { |
| // 31: bar(*iFld); |
| // 32: } |
| // |
| // Disassembly of foo() with source code: |
| // 30: void foo() { |
| // 0x55d132: 55 push rbp |
| // 0x55d133: 48 89 e5 mov rbp,rsp |
| // 31: bar(*iFld); |
| // 0x55d136: 48 8b 05 b3 ee e8 01 mov rax,QWORD PTR [rip+0x1e8eeb3] # 23ebff0 <iFld> |
| // 0x55d13d: 8b 00 mov eax,DWORD PTR [rax] |
| // 0x55d13f: 89 c7 mov edi,eax |
| // 0x55d141: e8 e2 ff ff ff call 55d128 <_Z3bari> |
| // 32: } |
| // 0x55d146: 90 nop |
| // 0x55d147: 5d pop rbp |
| // 0x55d148: c3 ret |
| // |
| // This would produce the following matrix for foo() where duplicated lines (0x55d133, 0x55d13d, 0x55d13f) were removed |
| // according to (1): |
| // Address: Line: Column: File: |
| // 0x55d132 30 12 1 |
| // 0x55d136 31 6 1 |
| // 0x55d146 32 1 1 |
| // |
| // When trying to get the line number for a PC, which is translated into an offset address x into the library file, we can either: |
| // - Directly find the last entry in the matrix for which address == x (there could be multiple entries with the same address). |
| // - If there is no matching address for x: |
| // 1. Find two consecutive entries in the matrix for which: address_entry_1 < x < address_entry_2. |
| // 2. Then take the entry of address_entry_1. |
| // E.g. x = 0x55d13f -> 0x55d136 < 0x55d13f < 0x55d146 -> Take entry 0x55d136. |
| // |
| // Enable logging with debug level to print the generated line number information matrix. |
| bool DwarfFile::LineNumberProgram::run_line_number_program(char* filename, const size_t filename_len, int* line) { |
| DWARF_LOG_DEBUG(""); |
| DWARF_LOG_DEBUG("Line Number Information Matrix"); |
| DWARF_LOG_DEBUG("------------------------------"); |
| #ifndef _LP64 |
| DWARF_LOG_DEBUG("Address: Line: Column: File:"); |
| #else |
| DWARF_LOG_DEBUG("Address: Line: Column: File:"); |
| #endif |
| _state = new (std::nothrow) LineNumberProgramState(_header); |
| if (_state == nullptr) { |
| DWARF_LOG_ERROR("Failed to create new LineNumberProgramState object"); |
| return false; |
| } |
| uintptr_t previous_address = 0; |
| uint32_t previous_file = 0; |
| uint32_t previous_line = 0; |
| bool found_entry = false; |
| bool candidate = false; |
| bool first_in_sequence = true; |
| while (_reader.has_bytes_left()) { |
| if (!apply_opcode()) { |
| assert(false, "Could not apply opcode"); |
| return false; |
| } |
| |
| if (_state->_append_row) { |
| // Append a new line to the line number information matrix. |
| if (_state->_first_entry_in_sequence) { |
| // First entry in sequence: Check if _offset_in_library >= _state->address. If not, then all following entries |
| // belonging to this sequence cannot match our _offset_in_library because the addresses are always increasing |
| // in a sequence. |
| _state->_can_sequence_match_offset = _offset_in_library >= _state->_address; |
| _state->_first_entry_in_sequence = false; |
| } |
| if (does_offset_match_entry(previous_address, previous_file, previous_line)) { |
| // We are using an int for the line number which should never be larger than INT_MAX for any files. |
| *line = (int)_state->_line; |
| return get_filename_from_header(_state->_file, filename, filename_len); |
| } |
| |
| // We do not actually store the matrix while searching the correct entry. Enable logging to print/debug it. |
| DWARF_LOG_DEBUG(INTPTR_FORMAT " %-5u %-3u %-4u", |
| _state->_address, _state->_line, _state->_column, _state->_file); |
| previous_file = _state->_file; |
| previous_line = _state->_line; |
| previous_address = _state->_address; |
| _state->_append_row = false; |
| if (_state->_do_reset) { |
| // Current sequence terminated. |
| _state->reset_fields(); |
| } |
| } |
| } |
| |
| assert(false, "Did not find an entry in the line number information matrix that matches " UINT32_FORMAT_X_0, _offset_in_library); |
| return false; |
| } |
| |
| // Apply next opcode to update the state machine. |
| bool DwarfFile::LineNumberProgram::apply_opcode() { |
| uint8_t opcode; |
| if (!_reader.read_byte(&opcode)) { |
| return false; |
| } |
| |
| DWARF_LOG_TRACE(" Opcode: 0x%02x ", opcode); |
| if (opcode == 0) { |
| // Extended opcodes start with a zero byte. |
| if (!apply_extended_opcode()) { |
| assert(false, "Could not apply extended opcode"); |
| return false; |
| } |
| } else if (opcode <= 12) { |
| // 12 standard opcodes in DWARF 3 and 4. |
| if (!apply_standard_opcode(opcode)) { |
| assert(false, "Could not apply standard opcode"); |
| return false; |
| } |
| } else { |
| // Special opcodes range from 13 until 255. |
| apply_special_opcode(opcode); |
| } |
| return true; |
| } |
| |
| // Specified in section 6.2.5.3 of the DWARF 4 spec. |
| bool DwarfFile::LineNumberProgram::apply_extended_opcode() { |
| uint64_t extended_opcode_length; // Does not include the already written zero byte and the length leb128. |
| uint8_t extended_opcode; |
| if (!_reader.read_uleb128(&extended_opcode_length) || !_reader.read_byte(&extended_opcode)) { |
| return false; |
| } |
| |
| switch (extended_opcode) { |
| case DW_LNE_end_sequence: // No operands |
| DWARF_LOG_TRACE(" DW_LNE_end_sequence"); |
| _state->_end_sequence = true; |
| _state->_append_row = true; |
| _state->_do_reset = true; |
| break; |
| case DW_LNE_set_address: // 1 operand |
| if (!_reader.read_address_sized(&_state->_address)) { |
| return false; |
| } |
| DWARF_LOG_TRACE(" DW_LNE_set_address " INTPTR_FORMAT, _state->_address); |
| if (_state->_dwarf_version == 4) { |
| _state->_op_index = 0; |
| } |
| break; |
| case DW_LNE_define_file: // 4 operands |
| DWARF_LOG_TRACE(" DW_LNE_define_file"); |
| if (!_reader.read_string()) { |
| return false; |
| } |
| // Operand 2-4: uleb128 numbers we do not care about. |
| if (!_reader.read_uleb128_ignore() |
| || !_reader.read_uleb128_ignore() |
| || !_reader.read_uleb128_ignore()) { |
| return false; |
| } |
| break; |
| case DW_LNE_set_discriminator: // 1 operand |
| DWARF_LOG_TRACE(" DW_LNE_set_discriminator"); |
| uint64_t discriminator; |
| // For some reason, GCC emits this opcode even for earlier versions than DWARF 4 which introduced this opcode. |
| // We need to consume it. |
| if (!_reader.read_uleb128(&discriminator, 4)) { |
| // Must be an unsigned integer as specified in section 6.2.2 of the DWARF 4 spec for the discriminator register. |
| return false; |
| } |
| _state->_discriminator = discriminator; |
| break; |
| default: |
| assert(false, "Unknown extended opcode"); |
| return false; |
| } |
| return true; |
| } |
| |
| // Specified in section 6.2.5.2 of the DWARF 4 spec. |
| bool DwarfFile::LineNumberProgram::apply_standard_opcode(const uint8_t opcode) { |
| switch (opcode) { |
| case DW_LNS_copy: // No operands |
| DWARF_LOG_TRACE(" DW_LNS_copy"); |
| _state->_append_row = true; |
| _state->_basic_block = false; |
| _state->_prologue_end = false; |
| _state->_epilogue_begin = false; |
| if (_state->_dwarf_version == 4) { |
| _state->_discriminator = 0; |
| } |
| break; |
| case DW_LNS_advance_pc: { // 1 operand |
| uint64_t operation_advance; |
| if (!_reader.read_uleb128(&operation_advance, 4)) { |
| // Must be at most 4 bytes because the index register is only 4 bytes wide. |
| return false; |
| } |
| _state->add_to_address_register(operation_advance, _header); |
| if (_state->_dwarf_version == 4) { |
| _state->set_index_register(operation_advance, _header); |
| } |
| DWARF_LOG_TRACE(" DW_LNS_advance_pc (" INTPTR_FORMAT ")", _state->_address); |
| break; |
| } |
| case DW_LNS_advance_line: // 1 operand |
| int64_t line; |
| if (!_reader.read_sleb128(&line, 4)) { |
| // line register is 4 bytes wide. |
| return false; |
| } |
| _state->_line += line; |
| DWARF_LOG_TRACE(" DW_LNS_advance_line (%d)", _state->_line); |
| break; |
| case DW_LNS_set_file: // 1 operand |
| uint64_t file; |
| if (!_reader.read_uleb128(&file, 4)) { |
| // file register is 4 bytes wide. |
| return false; |
| } |
| _state->_file = file; |
| DWARF_LOG_TRACE(" DW_LNS_set_file (%u)", _state->_file); |
| break; |
| case DW_LNS_set_column: // 1 operand |
| uint64_t column; |
| if (!_reader.read_uleb128(&column, 4)) { |
| // column register is 4 bytes wide. |
| return false; |
| } |
| _state->_column = column; |
| DWARF_LOG_TRACE(" DW_LNS_set_column (%u)", _state->_column); |
| break; |
| case DW_LNS_negate_stmt: // No operands |
| DWARF_LOG_TRACE(" DW_LNS_negate_stmt"); |
| _state->_is_stmt = !_state->_is_stmt; |
| break; |
| case DW_LNS_set_basic_block: // No operands |
| DWARF_LOG_TRACE(" DW_LNS_set_basic_block"); |
| _state->_basic_block = true; |
| break; |
| case DW_LNS_const_add_pc: { // No operands |
| // Update address and op_index registers by the increments of special opcode 255. |
| uint8_t adjusted_opcode_255 = 255 - _header._opcode_base; |
| uint8_t operation_advance = adjusted_opcode_255 / _header._line_range; |
| uintptr_t old_address = _state->_address; |
| _state->add_to_address_register(operation_advance, _header); |
| if (_state->_dwarf_version == 4) { |
| _state->set_index_register(operation_advance, _header); |
| } |
| DWARF_LOG_TRACE(" DW_LNS_const_add_pc (" INTPTR_FORMAT ")", _state->_address - old_address); |
| break; |
| } |
| case DW_LNS_fixed_advance_pc: // 1 operand |
| uint16_t operand; |
| if (!_reader.read_word(&operand)) { |
| return false; |
| } |
| _state->_address += operand; |
| _state->_op_index = 0; |
| DWARF_LOG_TRACE(" DW_LNS_fixed_advance_pc (" INTPTR_FORMAT ")", _state->_address); |
| break; |
| case DW_LNS_set_prologue_end: // No operands |
| DWARF_LOG_TRACE(" DW_LNS_set_basic_block"); |
| _state->_prologue_end = true; |
| break; |
| case DW_LNS_set_epilogue_begin: // No operands |
| DWARF_LOG_TRACE(" DW_LNS_set_epilogue_begin"); |
| _state->_epilogue_begin = true; |
| break; |
| case DW_LNS_set_isa: // 1 operand |
| uint64_t isa; |
| if (!_reader.read_uleb128(&isa, 4)) { |
| // isa register is 4 bytes wide. |
| return false; |
| } |
| _state->_isa = isa; |
| DWARF_LOG_TRACE(" DW_LNS_set_isa (%u)", _state->_isa); |
| break; |
| default: |
| assert(false, "Unknown standard opcode"); |
| return false; |
| } |
| return true; |
| } |
| |
| // Specified in section 6.2.5.1 of the DWARF 4 spec. |
| void DwarfFile::LineNumberProgram::apply_special_opcode(const uint8_t opcode) { |
| uintptr_t old_address = _state->_address; |
| uint32_t old_line = _state->_line; |
| uint8_t adjusted_opcode = opcode - _header._opcode_base; |
| uint8_t operation_advance = adjusted_opcode / _header._line_range; |
| _state->add_to_address_register(operation_advance, _header); |
| if (_state->_dwarf_version == 4) { |
| _state->set_index_register(operation_advance, _header); |
| _state->_discriminator = 0; |
| } |
| _state->_line += _header._line_base + (adjusted_opcode % _header._line_range); |
| DWARF_LOG_TRACE(" address += " INTPTR_FORMAT ", line += %d", _state->_address - old_address, |
| _state->_line - old_line); |
| _state->_append_row = true; |
| _state->_basic_block = false; |
| _state->_prologue_end = false; |
| _state->_epilogue_begin = false; |
| } |
| |
| bool DwarfFile::LineNumberProgram::does_offset_match_entry(const uintptr_t previous_address, const uint32_t previous_file, |
| const uint32_t previous_line) { |
| if (_state->_can_sequence_match_offset) { |
| bool matches_entry_directly = _offset_in_library == _state->_address; |
| if (matches_entry_directly |
| || (_offset_in_library > previous_address && _offset_in_library < _state->_address)) { // in between two entries |
| _state->_found_match = true; |
| if (!matches_entry_directly || _is_pc_after_call) { |
| // We take the previous row in the matrix either when: |
| // - We try to match an offset that is between two entries. |
| // - We have an offset from a PC that is at a call-site in which case we need to get the line information for |
| // the call instruction in the previous entry. |
| print_and_store_prev_entry(previous_file, previous_line); |
| return true; |
| } else if (!_reader.has_bytes_left()) { |
| // We take the current entry when this is the very last entry in the matrix (i.e. must be the right one). |
| DWARF_LOG_DEBUG("^^^ Found line for requested offset " UINT32_FORMAT_X_0 " ^^^", _offset_in_library); |
| return true; |
| } |
| // Else: Exact match. We cannot take this entry because we do not know if there are more entries following this |
| // one with the same offset (we could have multiple entries for the same address in the matrix). Continue |
| // to parse entries. When we have the first non-exact match, then we know that the previous entry is the |
| // correct one to take (handled in the else-if-case below). If this is the very last entry in a matrix, |
| // we will take the current entry (handled in else-if-case above). |
| } else if (_state->_found_match) { |
| // We found an entry before with an exact match. This is now the first entry with a new offset. Pick the previous |
| // entry which matches our offset and is guaranteed to be the last entry which matches our offset (if there are |
| // multiple entries with the same offset). |
| print_and_store_prev_entry(previous_file, previous_line); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void DwarfFile::LineNumberProgram::print_and_store_prev_entry(const uint32_t previous_file, const uint32_t previous_line) { |
| _state->_file = previous_file; |
| _state->_line = previous_line; |
| DWARF_LOG_DEBUG("^^^ Found line for requested offset " UINT32_FORMAT_X_0 " ^^^", _offset_in_library); |
| // Also print the currently parsed entry. |
| DWARF_LOG_DEBUG(INTPTR_FORMAT " %-5u %-3u %-4u", |
| _state->_address, _state->_line, _state->_column, _state->_file); |
| } |
| |
| // Read field file_names from the header as specified in section 6.2.4 of the DWARF 4 spec. |
| bool DwarfFile::LineNumberProgram::get_filename_from_header(const uint32_t file_index, char* filename, const size_t filename_len) { |
| // We do not need to restore the position afterwards as this is the last step of parsing from the file for this compilation unit. |
| _reader.set_position(_header._file_names_offset); |
| uint32_t current_index = 1; // file_names start at index 1 |
| while (_reader.has_bytes_left()) { |
| if (current_index == file_index) { |
| // Found correct file. |
| return read_filename(filename, filename_len); |
| } else if (!_reader.read_string()) { // We don't care about this filename string. Read and ignore it. |
| // Either an error while reading or we have reached the end of the file_names section before reaching the file_index. |
| // Both should not happen. |
| return false; |
| } |
| |
| // We don't care about these values. |
| if (!_reader.read_uleb128_ignore() // Read directory index |
| || !_reader.read_uleb128_ignore() // Read last modification of file |
| || !_reader.read_uleb128_ignore()) { // Read file length |
| return false; |
| } |
| current_index++; |
| } |
| DWARF_LOG_DEBUG("Did not find filename entry at index " UINT32_FORMAT " in .debug_line header", file_index); |
| return false; |
| } |
| |
| // Read the filename into the provided 'filename' buffer. If it does not fit, an alternative smaller tag will be emitted |
| // in order to let the DWARF parser succeed. The line number with a function name will almost always be sufficient to get |
| // to the actual source code location. |
| bool DwarfFile::LineNumberProgram::read_filename(char* filename, const size_t filename_len) { |
| char next_char; |
| if (!_reader.read_non_null_char(&next_char)) { |
| // Either error while reading or read an empty string which indicates the end of the file_names section. |
| // Both should not happen. |
| return false; |
| } |
| |
| filename[0] = next_char; |
| size_t index = 1; |
| bool overflow_filename = false; // Is the currently read filename overflowing the provided 'filename' buffer? |
| while (next_char != '\0' && _reader.has_bytes_left()) { |
| if (!_reader.read_byte(&next_char)) { |
| return false; |
| } |
| if (next_char == *os::file_separator()) { |
| // Skip file separator to get to the actual filename and reset the buffer and overflow flag. GCC does not emit |
| // file separators while Clang does. |
| index = 0; |
| overflow_filename = false; |
| } else if (index == filename_len) { |
| // Just keep reading as we could read another file separator and reset the buffer again. But don't bother to store |
| // the additionally read characters as it would not fit into the buffer anyway. |
| overflow_filename = true; |
| } else { |
| assert(!overflow_filename, "sanity check"); |
| filename[index] = next_char; |
| index++; |
| } |
| } |
| |
| if (overflow_filename) { |
| // 'filename' buffer overflow. Store either a generic overflow message or a minimal filename. |
| write_filename_for_overflow(filename, filename_len); |
| } |
| return true; |
| } |
| |
| // Try to write a generic overflow message to the provided buffer. If it does not fit, store the minimal filename "L" |
| // which always fits to get the source information in the form "L:line_number". |
| void DwarfFile::LineNumberProgram::write_filename_for_overflow(char* filename, const size_t filename_len) { |
| DWARF_LOG_ERROR("DWARF filename string is too large to fit into the provided buffer of size %zu.", filename_len); |
| const size_t filename_overflow_message_length = strlen(overflow_filename) + 1; |
| if (filename_overflow_message_length <= filename_len) { |
| jio_snprintf(filename, filename_overflow_message_length, "%s", overflow_filename); |
| DWARF_LOG_ERROR("Use overflow filename: %s", overflow_filename); |
| } else { |
| // Buffer too small of generic overflow message. |
| DWARF_LOG_ERROR("Too small for overflow filename, use minimal filename: %c", minimal_overflow_filename); |
| assert(filename_len > 1, "sanity check"); |
| filename[0] = minimal_overflow_filename; |
| filename[1] = '\0'; |
| } |
| } |
| |
| void DwarfFile::LineNumberProgram::LineNumberProgramState::reset_fields() { |
| _address = 0; |
| _op_index = 0; |
| _file = 1; |
| _line = 1; |
| _column = 0; |
| _is_stmt = _initial_is_stmt; |
| _basic_block = false; |
| _end_sequence = false; |
| _prologue_end = false; |
| _epilogue_begin = false; |
| _isa = 0; |
| _discriminator = 0; |
| _append_row = false; |
| _do_reset = false; |
| _first_entry_in_sequence = true; |
| _can_sequence_match_offset = false; |
| } |
| |
| // Defined in section 6.2.5.1 of the DWARF 4 spec. |
| void DwarfFile::LineNumberProgram::LineNumberProgramState::add_to_address_register(const uint32_t operation_advance, |
| const LineNumberProgramHeader& header) { |
| if (_dwarf_version == 2 || _dwarf_version == 3) { |
| _address += (uintptr_t)(operation_advance * header._minimum_instruction_length); |
| } else if (_dwarf_version == 4) { |
| _address += (uintptr_t)(header._minimum_instruction_length * |
| ((_op_index + operation_advance) / header._maximum_operations_per_instruction)); |
| } |
| } |
| |
| // Defined in section 6.2.5.1 of the DWARF 4 spec. |
| void DwarfFile::LineNumberProgram::LineNumberProgramState::set_index_register(const uint32_t operation_advance, |
| const LineNumberProgramHeader& header) { |
| _op_index = (_op_index + operation_advance) % header._maximum_operations_per_instruction; |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::set_position(const long new_pos) { |
| if (new_pos < 0) { |
| return false; |
| } |
| _current_pos = new_pos; |
| return FileReader::set_position(new_pos); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::has_bytes_left() const { |
| if (_max_pos == -1) { |
| return false; |
| } |
| return _current_pos < _max_pos; |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::update_to_stored_position() { |
| _marked_pos = ftell(_fd); |
| if (_marked_pos < 0) { |
| return false; |
| } |
| return FileReader::set_position(_current_pos); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::reset_to_previous_position() { |
| return FileReader::set_position(_marked_pos); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::move_position(const long offset) { |
| if (offset == 0) { |
| return true; |
| } |
| return set_position(_current_pos + offset); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_byte(void* result) { |
| _current_pos++; |
| return read(result, 1); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_word(uint16_t* result) { |
| _current_pos += 2; |
| return read(result, 2); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_dword(uint32_t* result) { |
| _current_pos += 4; |
| return read(result, 4); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_qword(uint64_t* result) { |
| _current_pos += 8; |
| return read(result, 8); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_address_sized(uintptr_t* result) { |
| _current_pos += DwarfFile::ADDRESS_SIZE; |
| return read(result, DwarfFile::ADDRESS_SIZE); |
| } |
| |
| // See Figure 46/47 in Appendix C of the DWARF 4 spec. |
| bool DwarfFile::MarkedDwarfFileReader::read_leb128(uint64_t* result, const int8_t check_size, bool is_signed) { |
| *result = 0; // Ensure a proper result by zeroing it first. |
| uint8_t buf; |
| uint8_t shift = 0; |
| uint8_t bytes_read = 0; |
| // leb128 is not larger than 8 bytes. |
| while (bytes_read < 8) { |
| if (!read_byte(&buf)) { |
| return false; |
| } |
| bytes_read++; |
| *result |= (buf & 0x7fu) << shift; |
| shift += 7; |
| if ((buf & 0x80u) == 0) { |
| break; |
| } |
| } |
| if (bytes_read > 8 || (check_size != -1 && bytes_read > check_size)) { |
| // Invalid leb128 encoding or the read leb128 was larger than expected. |
| return false; |
| } |
| |
| if (is_signed && (shift < 64) && (buf & 0x40u)) { |
| *result |= static_cast<uint64_t>(-1L) << shift; |
| } |
| return true; |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_uleb128_ignore(const int8_t check_size) { |
| uint64_t dont_care; |
| return read_leb128(&dont_care, check_size, false); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_uleb128(uint64_t* result, const int8_t check_size) { |
| return read_leb128(result, check_size, false); |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_sleb128(int64_t* result, const int8_t check_size) { |
| return read_leb128((uint64_t*)result, check_size, true); |
| } |
| |
| // If result is a null, we do not care about the content of the string being read. |
| bool DwarfFile::MarkedDwarfFileReader::read_string(char* result, const size_t result_len) { |
| char first_char; |
| if (!read_non_null_char(&first_char)) { |
| return false; |
| } |
| |
| if (result != nullptr) { |
| if (result_len < 2) { |
| // Strings must contain at least one non-null byte and a null byte terminator. |
| return false; |
| } |
| result[0] = first_char; |
| } |
| |
| uint8_t next_byte; |
| size_t char_index = 1; |
| bool exceeded_buffer = false; |
| while (has_bytes_left()) { |
| // Read until we find a null byte which terminates the string. |
| if (!read_byte(&next_byte)) { |
| return false; |
| } |
| |
| if (result != nullptr) { |
| if (char_index >= result_len) { |
| // Exceeded buffer size of 'result'. |
| exceeded_buffer = true; |
| } else { |
| result[char_index] = (char)next_byte; |
| } |
| char_index++; |
| } |
| if (next_byte == 0) { |
| if (exceeded_buffer) { |
| result[result_len - 1] = '\0'; // Mark end of string. |
| DWARF_LOG_ERROR("Tried to read " SIZE_FORMAT " bytes but exceeded buffer size of " SIZE_FORMAT ". Truncating string.", |
| char_index, result_len); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool DwarfFile::MarkedDwarfFileReader::read_non_null_char(char* result) { |
| if (!read_byte(result)) { |
| return false; |
| } |
| return *result != '\0'; |
| } |
| |
| #endif // !_WINDOWS && !__APPLE__ |