代码拉取完成,页面将自动刷新
同步操作将从 src-openEuler/openjdk-1.8.0 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
From 24dedc988ac599b3191f6a69c1bce35fcc6bf748 Mon Sep 17 00:00:00 2001
From: eapen <zhangyipeng7@huawei.com>
Date: Thu, 15 Dec 2022 20:05:20 +0800
Subject: [PATCH 23/33] I68TO2: 8242181: Show source information when printing native
stack traces in hs_err files
---
hotspot/src/share/vm/runtime/globals.hpp | 3 +
hotspot/src/share/vm/utilities/debug.cpp | 9 +
hotspot/src/share/vm/utilities/decoder.cpp | 9 +
hotspot/src/share/vm/utilities/decoder.hpp | 17 +
hotspot/src/share/vm/utilities/decoder_elf.cpp | 39 +
hotspot/src/share/vm/utilities/decoder_elf.hpp | 2 +
hotspot/src/share/vm/utilities/elfFile.cpp | 1510 +++++++++++++++++++-
hotspot/src/share/vm/utilities/elfFile.hpp | 732 +++++++++-
hotspot/src/share/vm/utilities/nativeCallStack.cpp | 11 +-
hotspot/src/share/vm/utilities/vmError.cpp | 6 +
hotspot/src/share/vm/utilities/vmError.hpp | 3 +
jdk/test/jdk/java/dwarf/TestDwarf.java | 240 ++++
12 files changed, 2563 insertions(+), 18 deletions(-)
create mode 100644 jdk/test/jdk/java/dwarf/TestDwarf.java
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
index 64d40e0..d1e3cda 100644
--- a/hotspot/src/share/vm/runtime/globals.hpp
+++ b/hotspot/src/share/vm/runtime/globals.hpp
@@ -546,6 +546,9 @@ class CommandLineFlags {
develop(bool, CleanChunkPoolAsync, falseInEmbedded, \
"Clean the chunk pool asynchronously") \
\
+ develop(intx, TraceDwarfLevel, 0, \
+ "Debug levels for the dwarf parser") \
+ \
/* Temporary: See 6948537 */ \
experimental(bool, UseMemSetInBOT, true, \
"(Unstable) uses memset in BOT updates in GC code") \
diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp
index 8cea16d..6a9310e 100644
--- a/hotspot/src/share/vm/utilities/debug.cpp
+++ b/hotspot/src/share/vm/utilities/debug.cpp
@@ -51,6 +51,7 @@
#include "utilities/events.hpp"
#include "utilities/top.hpp"
#include "utilities/vmError.hpp"
+#include "utilities/decoder.hpp"
#ifdef TARGET_OS_FAMILY_linux
# include "os_linux.inline.hpp"
#endif
@@ -751,7 +752,15 @@ void print_native_stack(outputStream* st, frame fr, Thread* t, char* buf, int bu
int count = 0;
while (count++ < StackPrintLimit) {
fr.print_on_error(st, buf, buf_size);
+
+ char filename[128];
+ int line_no;
+ if (Decoder::get_source_info(fr.pc(), filename, sizeof(filename), &line_no, count != 1)) {
+ st->print(" (%s:%d)", filename, line_no);
+ }
+
st->cr();
+
// Compiled code may use EBP register on x86 so it looks like
// non-walkable C frame. Use frame.sender() for java frames.
if (t && t->is_Java_thread()) {
diff --git a/hotspot/src/share/vm/utilities/decoder.cpp b/hotspot/src/share/vm/utilities/decoder.cpp
index 7ed913a..ae58bf5 100644
--- a/hotspot/src/share/vm/utilities/decoder.cpp
+++ b/hotspot/src/share/vm/utilities/decoder.cpp
@@ -143,6 +143,15 @@ bool Decoder::can_decode_C_frame_in_vm() {
return decoder->can_decode_C_frame_in_vm();
}
+bool Decoder::get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
+ if (VMError::is_error_reported_in_current_thread()) {
+ return get_error_handler_instance()->get_source_info(pc, filename, filename_len, line, is_pc_after_call);
+ } else {
+ MutexLockerEx locker(shared_decoder_lock(), Mutex::_no_safepoint_check_flag);
+ return get_shared_instance()->get_source_info(pc, filename, filename_len, line, is_pc_after_call);
+ }
+}
+
/*
* Shutdown shared decoder and replace it with
* _do_nothing_decoder. Do nothing with error handler
diff --git a/hotspot/src/share/vm/utilities/decoder.hpp b/hotspot/src/share/vm/utilities/decoder.hpp
index c6c09e3..e83b87f 100644
--- a/hotspot/src/share/vm/utilities/decoder.hpp
+++ b/hotspot/src/share/vm/utilities/decoder.hpp
@@ -68,6 +68,11 @@ public:
return (status > 0);
}
+ // Get filename and line number information.
+ virtual bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
+ return false;
+ }
+
protected:
decoder_status _decoder_status;
};
@@ -97,6 +102,11 @@ public:
virtual bool can_decode_C_frame_in_vm() const {
return false;
}
+
+ // Get filename and line number information.
+ virtual bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
+ return false;
+ }
};
@@ -107,6 +117,13 @@ public:
static bool demangle(const char* symbol, char* buf, int buflen);
static bool can_decode_C_frame_in_vm();
+ // Attempts to retrieve source file name and line number associated with a pc.
+ // If filename != NULL, points to a buffer of size filename_len which will receive the
+ // file name. File name will be silently truncated if output buffer is too small.
+ // If is_pc_after_call is true, then pc is treated as pointing to the next instruction
+ // after a call. The source information for the call instruction is fetched in that case.
+ static bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call = false);
+
// shutdown shared instance
static void shutdown();
protected:
diff --git a/hotspot/src/share/vm/utilities/decoder_elf.cpp b/hotspot/src/share/vm/utilities/decoder_elf.cpp
index 9730883..bb72ce1 100644
--- a/hotspot/src/share/vm/utilities/decoder_elf.cpp
+++ b/hotspot/src/share/vm/utilities/decoder_elf.cpp
@@ -73,4 +73,43 @@ ElfFile* ElfDecoder::get_elf_file(const char* filepath) {
return file;
}
+
+bool ElfDecoder::get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
+ assert(filename != NULL && filename_len > 0 && line != NULL, "Argument error");
+ filename[0] = '\0';
+ *line = -1;
+
+ char filepath[JVM_MAXPATHLEN];
+ filepath[JVM_MAXPATHLEN - 1] = '\0';
+ int offset_in_library = -1;
+ if (!os::dll_address_to_library_name(pc, filepath, sizeof(filepath), &offset_in_library)) {
+ // Method not found. offset_in_library should not overflow.
+ DWARF_LOG_ERROR("Did not find library for address " INTPTR_FORMAT, p2i(pc))
+ return false;
+ }
+
+ if (filepath[JVM_MAXPATHLEN - 1] != '\0') {
+ DWARF_LOG_ERROR("File path is too large to fit into buffer of size %d", JVM_MAXPATHLEN);
+ return false;
+ }
+
+ const uint32_t unsigned_offset_in_library = (uint32_t)offset_in_library;
+
+ ElfFile* file = get_elf_file(filepath);
+ if (file == NULL) {
+ return false;
+ }
+ DWARF_LOG_INFO("##### Find filename and line number for offset " PTR32_FORMAT " in library %s #####",
+ unsigned_offset_in_library, filepath);
+
+ if (!file->get_source_info(unsigned_offset_in_library, filename, filename_len, line, is_pc_after_call)) {
+ return false;
+ }
+
+ DWARF_LOG_SUMMARY("pc: " INTPTR_FORMAT ", offset: " PTR32_FORMAT ", filename: %s, line: %u",
+ p2i(pc), offset_in_library, filename, *line);
+ DWARF_LOG_INFO("\n") // To structure the debug output better.
+ return true;
+}
+
#endif // !_WINDOWS && !__APPLE__
diff --git a/hotspot/src/share/vm/utilities/decoder_elf.hpp b/hotspot/src/share/vm/utilities/decoder_elf.hpp
index e92c958..5551f42 100644
--- a/hotspot/src/share/vm/utilities/decoder_elf.hpp
+++ b/hotspot/src/share/vm/utilities/decoder_elf.hpp
@@ -48,6 +48,8 @@ public:
return false;
}
+ bool get_source_info(address pc, char* buf, size_t buflen, int* line, bool is_pc_after_call);
+
private:
ElfFile* get_elf_file(const char* filepath);
diff --git a/hotspot/src/share/vm/utilities/elfFile.cpp b/hotspot/src/share/vm/utilities/elfFile.cpp
index ac943bd..81bd441 100644
--- a/hotspot/src/share/vm/utilities/elfFile.cpp
+++ b/hotspot/src/share/vm/utilities/elfFile.cpp
@@ -32,12 +32,41 @@
#include <new>
#include "memory/allocation.inline.hpp"
+#include "memory/resourceArea.hpp"
#include "utilities/decoder.hpp"
#include "utilities/elfFile.hpp"
#include "utilities/elfFuncDescTable.hpp"
#include "utilities/elfStringTable.hpp"
#include "utilities/elfSymbolTable.hpp"
+const char* ElfFile::USR_LIB_DEBUG_DIRECTORY = "/usr/lib/debug";
+
+bool FileReader::read(void* buf, size_t size) {
+ assert(buf != NULL, "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 != NULL, "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) {
assert(filepath, "null file path");
@@ -47,6 +76,8 @@ ElfFile::ElfFile(const char* filepath) {
m_funcDesc_table = NULL;
m_next = NULL;
m_status = NullDecoder::no_error;
+ m_shdr_string_table = NULL;
+ m_dwarf_file = NULL;
int len = strlen(filepath) + 1;
m_filepath = (const char*)os::malloc(len * sizeof(char), mtInternal);
@@ -83,6 +114,16 @@ ElfFile::~ElfFile() {
if (m_next != NULL) {
delete m_next;
}
+
+ if (m_shdr_string_table != NULL) {
+ delete m_shdr_string_table;
+ }
+
+ if (m_dwarf_file != NULL) {
+ delete m_dwarf_file;
+ m_dwarf_file = NULL;
+ }
+
};
@@ -128,7 +169,12 @@ bool ElfFile::load_tables() {
m_status = NullDecoder::out_of_memory;
return false;
}
- add_string_table(table);
+ if (index == m_elfHdr.e_shstrndx) {
+ assert(m_shdr_string_table == NULL, "Only set once");
+ m_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(m_file, shdr);
@@ -270,4 +316,1466 @@ bool ElfFile::specifies_noexecstack() {
}
#endif
+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) {
+ ResourceMark rm;
+ 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.", m_filepath);
+ return false;
+ }
+ DWARF_LOG_INFO("No separate .debuginfo file for library %s. It already contains the required DWARF sections.",
+ m_filepath);
+ if (!create_new_dwarf_file(m_filepath)) {
+ return false;
+ }
+ }
+
+ // Store result in filename and line pointer.
+ if (!m_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: " PTR32_FORMAT, m_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 (m_dwarf_file != NULL) {
+ 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(m_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, '/');
+ if (last_slash == NULL) {
+ // 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(m_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 != NULL) {
+ 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(m_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(m_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 (m_shdr_string_table == NULL) {
+ 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(m_elfHdr.e_shoff)) {
+ return false;
+ }
+
+ for (int index = 0; index < m_elfHdr.e_shnum; index++) {
+ if (!mfd.read((void*)&hdr, sizeof(hdr))) {
+ return false;
+ }
+ if (m_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 (m_dwarf_file != NULL) {
+ // Already opened.
+ return true;
+ }
+
+ const char* filepath = dwarf_file_path.path();
+ FILE* file = fopen(filepath, "r");
+ if (file == NULL) {
+ DWARF_LOG_DEBUG("Could not open dwarf file %s ", filepath);
+ 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: " PTR32_FORMAT ", found: " PTR32_FORMAT, 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);
+ m_dwarf_file = new (std::nothrow) DwarfFile(filepath);
+ if (m_dwarf_file == NULL) {
+ DWARF_LOG_ERROR("Failed to create new DwarfFile object for %s.", m_filepath);
+ return false;
+ }
+ if (!m_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: " PTR32_FORMAT, 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: " PTR32_FORMAT, 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(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: " PTR32_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;
+ }
+
+ 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 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(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 AddressDescriptor& descriptor) {
+ return descriptor.beginning_address == 0 && descriptor.range_length == 0;
+}
+
+// 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 " PTR32_FORMAT, _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: " PTR32_FORMAT, (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: 0x" UINT64_FORMAT_X ", Tag: 0x" 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: 0x" UINT64_FORMAT_X ", Form: 0x" 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 " PTR32_FORMAT,
+ (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_sbyte(&_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 == NULL) {
+ 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;
+ 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();
+ }
+ }
+ }
+
+ 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 " PTR32_FORMAT " ^^^", _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 " PTR32_FORMAT " ^^^", _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 (!_reader.read_string(filename, filename_len)) {
+ // Either an error while reading or we have reached the end of the file_names. Both should not happen.
+ return false;
+ }
+
+ if (current_index == file_index) {
+ // Found correct file.
+ return true;
+ }
+
+ // 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;
+}
+
+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_sbyte(int8_t* result) {
+ _current_pos++;
+ return read(result, 1);
+}
+
+bool DwarfFile::MarkedDwarfFileReader::read_byte(uint8_t* 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 nullptr, we do not care about the content of the string being read.
+bool DwarfFile::MarkedDwarfFileReader::read_string(char* result, const size_t result_len) {
+ uint8_t next_byte;
+ if (!read_byte(&next_byte)) {
+ return false;
+ }
+
+ if (next_byte == 0) {
+ // Strings must contain at least one non-null byte.
+ return false;
+ }
+
+ if (result != NULL) {
+ if (result_len < 2) {
+ // Strings must contain at least one non-null byte and a null byte terminator.
+ return false;
+ }
+ result[0] = (char)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 != NULL) {
+ 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;
+}
+
#endif // !_WINDOWS && !__APPLE__
diff --git a/hotspot/src/share/vm/utilities/elfFile.hpp b/hotspot/src/share/vm/utilities/elfFile.hpp
index 3ce8e92..3277a40 100644
--- a/hotspot/src/share/vm/utilities/elfFile.hpp
+++ b/hotspot/src/share/vm/utilities/elfFile.hpp
@@ -36,6 +36,27 @@
#ifdef _LP64
+#ifdef ASSERT
+// Helper macros to print different log levels during DWARF parsing
+#define DWARF_LOG_SUMMARY(format, ...) DWARF_LOG_WITH_LEVEL(1, format, ##__VA_ARGS__) // Same level as error logging
+#define DWARF_LOG_ERROR(format, ...) DWARF_LOG_WITH_LEVEL(1, format, ##__VA_ARGS__)
+#define DWARF_LOG_INFO(format, ...) DWARF_LOG_WITH_LEVEL(2, format, ##__VA_ARGS__)
+#define DWARF_LOG_DEBUG(format, ...) DWARF_LOG_WITH_LEVEL(3, format, ##__VA_ARGS__)
+#define DWARF_LOG_TRACE(format, ...) DWARF_LOG_WITH_LEVEL(4, format, ##__VA_ARGS__)
+
+#define DWARF_LOG_WITH_LEVEL(level, format, ...) \
+ if (TraceDwarfLevel >= level) { \
+ tty->print("[dwarf] "); \
+ tty->print_cr(format, ##__VA_ARGS__); \
+ }
+#else
+#define DWARF_LOG_SUMMARY(format, ...)
+#define DWARF_LOG_ERROR(format, ...)
+#define DWARF_LOG_INFO(format, ...)
+#define DWARF_LOG_DEBUG(format, ...)
+#define DWARF_LOG_TRACE(format, ...)
+#endif
+
typedef Elf64_Half Elf_Half;
typedef Elf64_Word Elf_Word;
typedef Elf64_Off Elf_Off;
@@ -76,6 +97,29 @@ typedef Elf32_Sym Elf_Sym;
class ElfStringTable;
class ElfSymbolTable;
class ElfFuncDescTable;
+class DwarfFile;
+
+class FileReader : public StackObj {
+ protected:
+ FILE* const _fd;
+ public:
+ FileReader(FILE* const fd) : _fd(fd) {};
+ bool read(void* buf, size_t size);
+ size_t read_buffer(void* buf, size_t size);
+ virtual bool set_position(long offset);
+};
+
+// Mark current position, so we can get back to it after
+// reads.
+class MarkedFileReader : public FileReader {
+ protected:
+ long _marked_pos;
+ public:
+ MarkedFileReader(FILE* const fd);
+ ~MarkedFileReader();
+
+ bool has_mark() const { return _marked_pos >= 0; }
+};
// On Solaris/Linux platforms, libjvm.so does contain all private symbols.
@@ -87,6 +131,34 @@ class ElfFuncDescTable;
class ElfFile: public CHeapObj<mtInternal> {
friend class ElfDecoder;
+
+ protected:
+ ElfFile* m_next;
+
+ private:
+ // file
+ const char* m_filepath;
+ FILE* m_file;
+ DwarfFile* m_dwarf_file;
+
+ static const char* USR_LIB_DEBUG_DIRECTORY;
+
+ // Elf header
+ Elf_Ehdr m_elfHdr;
+
+ // symbol tables
+ ElfSymbolTable* m_symbol_tables;
+
+ // string tables
+ ElfStringTable* m_string_tables;
+
+ // function descriptors table
+ ElfFuncDescTable* m_funcDesc_table;
+
+ NullDecoder::decoder_status m_status;
+
+ ElfStringTable* m_shdr_string_table;
+
public:
ElfFile(const char* filepath);
~ElfFile();
@@ -122,6 +194,9 @@ class ElfFile: public CHeapObj<mtInternal> {
// return a string table at specified section index
ElfStringTable* get_string_table(int index);
+ // Get filename and line number information
+ bool get_source_info(uint32_t offset_in_library, char* filename, size_t filename_len, int* line, bool is_pc_after_call);
+
protected:
ElfFile* next() const { return m_next; }
void set_next(ElfFile* file) { m_next = file; }
@@ -134,27 +209,654 @@ protected:
// On systems other than linux it always returns false.
bool specifies_noexecstack() NOT_LINUX({ return false; });
- protected:
- ElfFile* m_next;
- private:
- // file
- const char* m_filepath;
- FILE* m_file;
+ bool create_new_dwarf_file(const char* filepath);
- // Elf header
- Elf_Ehdr m_elfHdr;
+ // Struct to store the debug info read from the .gnu_debuglink section.
+ struct DebugInfo {
+ static const uint8_t CRC_LEN = 4;
- // symbol tables
- ElfSymbolTable* m_symbol_tables;
+ char _dwarf_filename[JVM_MAXPATHLEN];
+ uint32_t _crc;
+ };
- // string tables
- ElfStringTable* m_string_tables;
+ // Helper class to create DWARF paths when loading a DWARF file.
+ class DwarfFilePath {
+ private:
+ static const uint16_t MAX_DWARF_PATH_LENGTH = JVM_MAXPATHLEN;
+ const char* _filename;
+ char _path[MAX_DWARF_PATH_LENGTH];
+ const uint32_t _crc;
+ uint16_t _null_terminator_index; // Index for the current null terminator of the string stored in _path
- // function descriptors table
- ElfFuncDescTable* m_funcDesc_table;
+ bool check_valid_path() const {
+ return _path[MAX_DWARF_PATH_LENGTH - 1] == '\0';
+ }
- NullDecoder::decoder_status m_status;
+ void update_null_terminator_index() {
+ _null_terminator_index = strlen(_path);
+ }
+
+ bool copy_to_path_index(uint16_t index_in_path, const char* src);
+
+ public:
+ DwarfFilePath(DebugInfo& debug_info)
+ : _filename(debug_info._dwarf_filename), _crc(debug_info._crc), _null_terminator_index(0) {
+ _path[MAX_DWARF_PATH_LENGTH - 1] = '\0'; // Ensures to have a null terminated string and not read beyond the buffer limit.
+ }
+
+ const char* path() const {
+ return _path;
+ }
+
+ const char* filename() const {
+ return _filename;
+ }
+
+ uint32_t crc() const {
+ return _crc;
+ }
+
+ bool set(const char* src);
+
+ bool set_filename_after_last_slash() {
+ return set_after_last_slash(_filename);
+ }
+
+ bool set_after_last_slash(const char* src);
+ bool append(const char* src);
+ };
+
+ // Load the DWARF file (.debuginfo) that belongs to this file either from (checked in listed order):
+ // - Same directory as the library file.
+ // - User defined path in environmental variable _JVM_DWARF_PATH.
+ // - Subdirectory .debug in same directory as the library file.
+ // - /usr/lib/debug directory
+ bool load_dwarf_file();
+
+
+ bool read_debug_info(DebugInfo* debug_info) const;
+
+ bool load_dwarf_file_from_same_directory(DwarfFilePath& dwarf_file_path);
+ bool load_dwarf_file_from_env_var_path(DwarfFilePath& dwarf_file_path);
+ bool load_dwarf_file_from_env_path_folder(DwarfFilePath& dwarf_file_path, const char* dwarf_path_from_env, const char* folder);
+ bool load_dwarf_file_from_debug_sub_directory(DwarfFilePath& dwarf_file_path);
+ bool load_dwarf_file_from_usr_lib_debug(DwarfFilePath& dwarf_file_path);
+ bool open_valid_debuginfo_file(const DwarfFilePath& dwarf_file_path);
+ static uint32_t get_file_crc(FILE* const file);
+ static uint gnu_debuglink_crc32(uint32_t crc, uint8_t* buf, size_t len);
+
+ protected:
+ FILE* const fd() const { return m_file; }
+
+ // Read the section header of section 'name'.
+ bool read_section_header(const char* name, Elf_Shdr& hdr) const;
+ bool is_valid_dwarf_file() const;
+};
+
+/*
+ * This class parses and reads filename and line number information from an associated .debuginfo file that belongs to
+ * this ELF file or directly from this ELF file if there is no separate .debuginfo file. The debug info is written by GCC
+ * in DWARF - a standardized debugging data format. There are special sections where the DWARF info is written to. These
+ * sections can either be put into the same ELF file or a separate .debuginfo file. For simplicity, when referring to the
+ * "DWARF file" or the ".debuginfo file" we just mean the file that contains the required DWARF sections. The current version
+ * of GCC uses DWARF version 4 as default which is defined in the official standard: http://www.dwarfstd.org/doc/DWARF4.pdf.
+ * This class is able to parse 32-bit DWARF version 4 for 32 and 64-bit Linux builds. GCC does not emit 64-bit DWARF and
+ * therefore is not supported by this parser. For some reason, GCC emits DWARF version 3 for the .debug_line section as a
+ * default. This parser was therefore adapted to support DWARF version 3 and 4 for the .debug_line section. Apart from that,
+ * other DWARF versions, especially the newest version 5, are not (yet) supported.
+ *
+ * Description of used DWARF file sections:
+ * - .debug_aranges: A table that consists of sets of variable length entries, each set describing the portion of the
+ * program's address space that is covered by a single compilation unit. In other words, the entries
+ * describe a mapping between addresses and compilation units.
+ * - .debug_info: The core DWARF data containing DWARF Information Entries (DIEs). Each DIE consists of a tag and a
+ * series of attributes. Each (normal) compilation unit is represented by a DIE with the tag
+ * DW_TAG_compile_unit and contains children. For our purposes, we are only interested in this DIE to
+ * get to the .debug_line section. We do not care about the children. This parser currently only
+ * supports normal compilation units and no partial compilation or type units.
+ * - .debug_abbrev: Represents abbreviation tables for all compilation units. A table for a specific compilation unit
+ * consists of a series of abbreviation declarations. Each declaration specifies a tag and attributes
+ * for a DIE. The DIEs from the compilation units in the .debug_info section need the abbreviation table
+ * to decode their attributes (their meaning and size).
+ * - .debug_line: Contains filename and line number information for each compilation unit. To get the information, a
+ * state machine needs to be executed which generates a matrix. Each row of this matrix describes the
+ * filename and line number (among other information) for a specific offset in the associated ELF library
+ * file. The state machine is executed until the row for the requested offset is found. The filename and
+ * line number information can then be fetched with the current register values of the state machine.
+ *
+ * Algorithm
+ * ---------
+ * Given: Offset into the ELF file library.
+ * Return: Filename and line number for this offset.
+ * (1) First, the path to the .debuginfo DWARF file is found by inspecting the .gnu_debuglink section of the library file.
+ * The DWARF file is then opened by calling the constructor of this class. Once this is done, the processing of the
+ * DWARF file is initiated by calling find_filename_and_line_number().
+ * (2) Find the compilation unit offset by reading entries from the section .debug_aranges, which contain address range
+ * descriptors, until we find the correct descriptor that includes the library offset.
+ * (3) Find the .debug_line offset for the line number information program from the .debug_info section:
+ * (a) Parse the compilation unit header from the .debug_info section at the offset obtained by (2).
+ * (b) Read the debug_abbrev_offset into the .debug_abbrev section that belongs to this compilation unit from the
+ * header obtained in (3a).
+ * (c) Read the abbreviation code that immediately follows the compilation unit header from (3a) which is needed to
+ * find the correct entry in the .debug_abbrev section.
+ * (d) Find the correct entry in the abbreviation table in the .debug_abbrev section by starting to parse entries at
+ * the debug_abbrev_offset from (3b) until we find the correct one matching the abbreviation code from (3c).
+ * (e) Read the specified attributes of the abbreviation entry from (3d) from the compilation unit (in the .debug_info
+ * section) until we find the attribute DW_AT_stmt_list. This attributes represents an offset into the .debug_line
+ * section which contains the line number program information to get the filename and the line number.
+ * (4) Find the filename and line number belonging to the given library offset by running the line number program state
+ * machine with its registers. This creates a matrix where each row stores information for specific addresses (library
+ * offsets). The state machine executes different opcodes which modify the state machine registers. Certain opcodes
+ * will add a new row to the matrix by taking the current values of state machine registers. As soon as the correct
+ * matrix row matching the library offset is found, we can read the line number from the line register of the state
+ * machine and parse the filename from the line number program header with the given file index from the file register
+ * of the state machine.
+ *
+ * More details about the different phases can be found at the associated classes and methods. A visualization of the
+ * algorithm inside the different sections can be found in the class comments for DebugAranges, DebugAbbrev and
+ * LineNumberProgram further down in this file.
+ *
+ * Available (develop) log levels (-XX:TraceDwarfLevel=[1,4]) which are only present in debug builds. Each level prints
+ * all the logs of the previous levels and adds some more fine-grained logging:
+ * - Level 1 (summary + errors):
+ * - Prints the path of parsed DWARF file together with the resulting source information.
+ * - Prints all errors.
+ * - Level 2 (info):
+ * - Prints the found offsets of all DWARF sections
+ * - Level 3 (debug):
+ * - Prints the results of the steps (1) - (4) together with the generated line information matrix.
+ * - Level 4 (trace):
+ * - Complete information about intermediate states/results when parsing the DWARF file.
+ */
+class DwarfFile : public ElfFile {
+
+ static const uint8_t ADDRESS_SIZE = NOT_LP64(4) LP64_ONLY(8);
+ // We only support 32-bit DWARF (emitted by GCC) which uses 32-bit values for DWARF section lengths and offsets
+ // relative to the beginning of a section.
+ static const uint8_t DWARF_SECTION_OFFSET_SIZE = 4;
+
+ class MarkedDwarfFileReader : public MarkedFileReader {
+ private:
+ long _current_pos;
+ long _max_pos; // Used to guarantee that we stop reading in case we reached the end of a section.
+
+ bool read_leb128(uint64_t* result, int8_t check_size, bool is_signed);
+ public:
+ MarkedDwarfFileReader(FILE* const fd) : MarkedFileReader(fd), _current_pos(-1), _max_pos(-1) {}
+
+ virtual bool set_position(long new_pos);
+ long get_position() const { return _current_pos; }
+ void set_max_pos(long max_pos) { _max_pos = max_pos; }
+ // Have we reached the limit of maximally allowable bytes to read? Used to ensure to stop reading when a section ends.
+ bool has_bytes_left() const;
+ // Call this if another file reader has changed the position of the same file handle.
+ bool update_to_stored_position();
+ // Must be called to restore the old position before this file reader changed it with update_to_stored_position().
+ bool reset_to_previous_position();
+ bool move_position(long offset);
+ bool read_sbyte(int8_t* result);
+ bool read_byte(uint8_t* result);
+ bool read_word(uint16_t* result);
+ bool read_dword(uint32_t* result);
+ bool read_qword(uint64_t* result);
+ bool read_uleb128_ignore(int8_t check_size = -1);
+ bool read_uleb128(uint64_t* result, int8_t check_size = -1);
+ bool read_sleb128(int64_t* result, int8_t check_size = -1);
+ // Reads 4 bytes for 32-bit and 8 bytes for 64-bit builds.
+ bool read_address_sized(uintptr_t* result);
+ bool read_string(char* result = NULL, size_t result_len = 0);
+ };
+
+ // (2) Processing the .debug_aranges section to find the compilation unit which covers offset_in_library.
+ // This is specified in section 6.1.2 of the DWARF 4 spec.
+ //
+ // Structure of .debug_aranges:
+ // Section Header
+ // % Table of variable length sets describing the address space covered by a compilation unit
+ // % Set 1
+ // ...
+ // % Set i:
+ // % Set header
+ // ...
+ // debug_info_offset -> offset to compilation unit
+ // % Series of address range descriptors [beginning_address, range_length]:
+ // % Descriptor 1
+ // ...
+ // % Descriptor j:
+ // beginning_address <= offset_in_library < beginning_address + range_length?
+ // => Found the correct set covering offset_in_library. Take debug_info_offset from the set header to get
+ // to the correct compilation unit in .debug_info.
+ class DebugAranges {
+
+ // The header is defined in section 6.1.2 of the DWARF 4 spec.
+ struct DebugArangesSetHeader {
+ // The total length of all of the entries for that set, not including the length field itself.
+ uint32_t _unit_length;
+
+ // This number is specific to the address lookup table and is independent of the DWARF version number.
+ uint16_t _version;
+
+ // The offset from the beginning of the .debug_info or .debug_types section of the compilation unit header referenced
+ // by the set. In this parser we only use it as offset into .debug_info. This must be 4 bytes for 32-bit DWARF.
+ uint32_t _debug_info_offset;
+
+ // The size of an address in bytes on the target architecture, 4 bytes for 32-bit and 8 bytes for 64-bit Linux builds.
+ uint8_t _address_size;
+
+ // The size of a segment selector in bytes on the target architecture. This should be 0.
+ uint8_t _segment_size;
+ };
+
+ // Address descriptor defining a range that is covered by a compilation unit. It is defined in section 6.1.2 after
+ // the set header in the DWARF 4 spec.
+ struct AddressDescriptor {
+ uintptr_t beginning_address;
+ uintptr_t range_length;
+ };
+
+ DwarfFile* _dwarf_file;
+ MarkedDwarfFileReader _reader;
+ uint32_t _section_start_address;
+
+ bool read_section_header();
+ bool read_set_header(DebugArangesSetHeader& header);
+ bool read_address_descriptors(uint32_t offset_in_library, bool& found_matching_set);
+ bool read_address_descriptor(AddressDescriptor& descriptor);
+ static bool does_match_offset(uint32_t offset_in_library, const AddressDescriptor& descriptor) ;
+ static bool is_terminating_entry(const AddressDescriptor& descriptor);
+ public:
+ DebugAranges(DwarfFile* dwarf_file) : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _section_start_address(0) {}
+ bool find_compilation_unit_offset(uint32_t offset_in_library, uint32_t* compilation_unit_offset);
+
+ };
+
+ // (3a-c,e) The compilation unit is read from the .debug_info section. The structure of .debug_info is shown in the
+ // comments of class DebugAbbrev.
+ class CompilationUnit {
+
+ // Attribute form encodings from Figure 21 in section 7.5 of the DWARF 4 spec.
+ static const uint8_t DW_FORM_addr = 0x01; // address
+ static const uint8_t DW_FORM_block2 = 0x03; // block
+ static const uint8_t DW_FORM_block4 = 0x04; // block
+ static const uint8_t DW_FORM_data2 = 0x05; // constant
+ static const uint8_t DW_FORM_data4 = 0x06; // constant
+ static const uint8_t DW_FORM_data8 = 0x07; // constant
+ static const uint8_t DW_FORM_string = 0x08; // string
+ static const uint8_t DW_FORM_block = 0x09; // block
+ static const uint8_t DW_FORM_block1 = 0x0a; // block
+ static const uint8_t DW_FORM_data1 = 0x0b; // constant
+ static const uint8_t DW_FORM_flag = 0x0c; // flag
+ static const uint8_t DW_FORM_sdata = 0x0d; // constant
+ static const uint8_t DW_FORM_strp = 0x0e; // string
+ static const uint8_t DW_FORM_udata = 0x0f; // constant
+ static const uint8_t DW_FORM_ref_addr = 0x10; // reference0;
+ static const uint8_t DW_FORM_ref1 = 0x11; // reference
+ static const uint8_t DW_FORM_ref2 = 0x12; // reference
+ static const uint8_t DW_FORM_ref4 = 0x13; // reference
+ static const uint8_t DW_FORM_ref8 = 0x14; // reference
+ static const uint8_t DW_FORM_ref_udata = 0x15; // reference
+ static const uint8_t DW_FORM_indirect = 0x16; // see Section 7.5.3
+ static const uint8_t DW_FORM_sec_offset = 0x17; // lineptr, loclistptr, macptr, rangelistptr
+ static const uint8_t DW_FORM_exprloc = 0x18; // exprloc
+ static const uint8_t DW_FORM_flag_present = 0x19; // flag
+ static const uint8_t DW_FORM_ref_sig8 = 0x20; // reference
+
+ // The header is defined in section 7.5.1.1 of the DWARF 4 spec.
+ struct CompilationUnitHeader {
+ // The length of the .debug_info contribution for that compilation unit, not including the length field itself.
+ uint32_t _unit_length;
+
+ // The version of the DWARF information for the compilation unit. The value in this field is 4 for DWARF 4.
+ uint16_t _version;
+
+ // The offset into the .debug_abbrev section. This offset associates the compilation unit with a particular set of
+ // debugging information entry abbreviations.
+ uint32_t _debug_abbrev_offset;
+
+ // The size in bytes of an address on the target architecture, 4 bytes for 32-bit and 8 bytes for 64-bit Linux builds.
+ uint8_t _address_size;
+ };
+
+ DwarfFile* _dwarf_file;
+ MarkedDwarfFileReader _reader;
+ CompilationUnitHeader _header;
+ const uint32_t _compilation_unit_offset;
+
+ // Result of a request initiated by find_debug_line_offset().
+ uint32_t _debug_line_offset;
+
+ bool read_header();
+ public:
+ CompilationUnit(DwarfFile* dwarf_file, uint32_t compilation_unit_offset)
+ : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _compilation_unit_offset(compilation_unit_offset), _debug_line_offset(0) {}
+
+ bool find_debug_line_offset(uint32_t* debug_line_offset);
+ bool read_attribute_value(uint64_t attribute_form, bool is_DW_AT_stmt_list_attribute);
+ };
+
+ // (3d) Read from the .debug_abbrev section at the debug_abbrev_offset specified by the compilation unit header.
+ //
+ // The interplay between the .debug_info and .debug_abbrev sections is more complex. The following visualization of the structure
+ // of both sections support the comments found in the parsing steps of the CompilationUnit and DebugAbbrev class.
+ //
+ // Structure of .debug_abbrev:
+ // Section Header
+ // % Series of abbreviation tables
+ // % Abbreviation table 1
+ // ...
+ // % Abbreviation table for compilation unit at debug_abbrev_offset:
+ // % Series of declarations:
+ // % Declaration 1:
+ // abbreviation code
+ // tag
+ // DW_CHILDREN_yes/no
+ // % Series of attribute specifications
+ // % Attribute specification 1:
+ // attribute name
+ // attribute form
+ // ...
+ // % Last attribute specification:
+ // 0
+ // 0
+ // ...
+ // % Declaration i:
+ // Abbrev code read from compilation unit [AC]
+ // DW_TAG_compile_unit
+ // DW_CHILDREN_yes
+ // % Series of attribute specifications
+ // % Attribute specification 1 [AS1]
+ // ...
+ // % Attribute specification j [ASj]:
+ // DW_AT_stmt_list
+ // DW_FORM_sec_offset
+ //
+ //
+ // Structure of .debug_info:
+ // Section Header
+ // % Series of compilation units
+ // % Compilation unit 1
+ // ...
+ // % Compilation unit i for library offset fetched from .debug_aranges:
+ // % Compilation unit header:
+ // ...
+ // debug_abbrev_offset -> offset for abbreviation table in .debug_abbrev for this compilation unit
+ // ...
+ // Abbrev code -> used in .debug_abbrev to find the correct declaration [AC]
+ // % Series of attribute values
+ // Attribute value 1 (in the format defined by attribute specification 1 [AS1])
+ // ...
+ // Attribute value j (in the format defined by attribute specification j [ASj]):
+ // => Specifies Offset to line number program for this compilation unit in .debug_line
+ class DebugAbbrev {
+
+ struct AbbreviationDeclaration {
+ uint64_t _abbrev_code;
+ uint64_t _tag;
+ uint8_t _has_children;
+ };
+
+ struct AttributeSpecification {
+ uint64_t _name;
+ uint64_t _form;
+ };
+
+ // Tag encoding from Figure 18 in section 7.5 of the DWARF 4 spec.
+ static const uint8_t DW_TAG_compile_unit = 0x11;
+
+ // Child determination encoding from Figure 19 in section 7.5 of the DWARF 4 spec.
+ static const uint8_t DW_CHILDREN_yes = 0x01;
+
+ // Attribute encoding from Figure 20 in section 7.5 of the DWARF 4 spec.
+ static const uint8_t DW_AT_stmt_list = 0x10;
+
+ /* There is no specific header for this section */
+
+ DwarfFile* _dwarf_file;
+ MarkedDwarfFileReader _reader;
+ CompilationUnit* _compilation_unit; // Need to read from compilation unit while parsing the entries in .debug_abbrev.
+
+ // Result field of a request
+ uint32_t* _debug_line_offset;
+
+ bool read_declaration(AbbreviationDeclaration& declaration);
+ static bool is_wrong_or_unsupported_format(const AbbreviationDeclaration& declaration);
+ bool read_attribute_specifications(bool is_DW_TAG_compile_unit);
+ bool read_attribute_specification(AttributeSpecification& specification);
+ static bool is_terminating_specification(const AttributeSpecification& attribute_specification) ;
+
+ public:
+ DebugAbbrev(DwarfFile* dwarf_file, CompilationUnit* compilation_unit) :
+ _dwarf_file(dwarf_file), _reader(_dwarf_file->fd()), _compilation_unit(compilation_unit),
+ _debug_line_offset(NULL) {}
+
+ bool read_section_header(uint32_t debug_abbrev_offset);
+ bool find_debug_line_offset(uint64_t abbrev_code);
+ };
+
+ // (4) The line number program for the compilation unit at the offset of the .debug_line obtained by (3).
+ // For some reason, earlier GCC versions emit the line number program in DWARF 2 or 3 format even though the
+ // default is DWARF 4. It also mixes the standards (see comments in the parsing code).
+ //
+ // Therefore, this class supports DWARF 2, 3 and 4 parsing as specified in section 6.2 of the DWARF specs.
+ // The parsing of DWARF 2 is already covered by the parsing of DWARF 3 as they use the shared opcodes in the same way.
+ // The parsing of DWARF 4, however, needs some adaptation as it consumes more data for some shared opcodes.
+ //
+ // DWARF 2 standard: https://dwarfstd.org/doc/dwarf-2.0.0.pdf
+ // DWARF 3 standard: https://dwarfstd.org/doc/Dwarf3.pdf
+ //
+ //
+ // Structure of .debug_ling:
+ // Section Header
+ // % Series of line number program entries for each compilation unit
+ // % Line number program 1
+ // ...
+ // % Line number program i for our compilation unit:
+ // % Line program header unit header:
+ // ...
+ // version -> currently emits version 3 by default
+ // ...
+ // file_name -> sequence of file names
+ // % Sequence of opcodes as part of the line number program to build the line number information matrix:
+ // % Format of matrix: [offset, line, directory_index, file_index]
+ // % Line 1
+ // ...
+ // % Line j:
+ // [offset matching offset_in_library, line, directory_index, file_index]
+ // => Get line number + look up file_index in file_name list (pick file_index'th string)
+ class LineNumberProgram {
+
+ // Standard opcodes for the line number program defined in section 6.2.5.2 of the DWARF 4 spec.
+ static const uint8_t DW_LNS_copy = 1;
+ static const uint8_t DW_LNS_advance_pc = 2;
+ static const uint8_t DW_LNS_advance_line = 3;
+ static const uint8_t DW_LNS_set_file = 4;
+ static const uint8_t DW_LNS_set_column = 5;
+ static const uint8_t DW_LNS_negate_stmt = 6;
+ static const uint8_t DW_LNS_set_basic_block = 7;
+ static const uint8_t DW_LNS_const_add_pc = 8;
+ static const uint8_t DW_LNS_fixed_advance_pc = 9;
+ static const uint8_t DW_LNS_set_prologue_end = 10; // Introduced with DWARF 3
+ static const uint8_t DW_LNS_set_epilogue_begin = 11; // Introduced with DWARF 3
+ static const uint8_t DW_LNS_set_isa = 12; // Introduced with DWARF 3
+
+ // Extended opcodes for the line number program defined in section 6.2.5.2 of the DWARF 4 spec.
+ static const uint8_t DW_LNE_end_sequence = 1;
+ static const uint8_t DW_LNE_set_address = 2;
+ static const uint8_t DW_LNE_define_file = 3;
+ static const uint8_t DW_LNE_set_discriminator = 4; // Introduced with DWARF 4
+
+ // The header is defined in section 6.2.4 of the DWARF 4 spec.
+ struct LineNumberProgramHeader {
+ // The size in bytes of the line number information for this compilation unit, not including the unit_length
+ // field itself. 32-bit DWARF uses 4 bytes.
+ uint32_t _unit_length;
+
+ // The version of the DWARF information for the line number program unit. The value in this field should be 4 for
+ // DWARF 4 and version 3 as used for DWARF 3.
+ uint16_t _version;
+
+ // The number of bytes following the header_length field to the beginning of the first byte of the line number
+ // program itself. 32-bit DWARF uses 4 bytes.
+ uint32_t _header_length;
+
+ // The size in bytes of the smallest target machine instruction. Line number program opcodes that alter the address
+ // and op_index registers use this and maximum_operations_per_instruction in their calculations.
+ uint8_t _minimum_instruction_length;
+
+ // The maximum number of individual operations that may be encoded in an instruction. Line number program opcodes
+ // that alter the address and op_index registers use this and minimum_instruction_length in their calculations.
+ // For non-VLIW architectures, this field is 1, the op_index register is always 0, and the operation pointer is
+ // simply the address register. This is only used with DWARF 4.
+ uint8_t _maximum_operations_per_instruction;
+
+ // The initial value of the is_stmt register.
+ uint8_t _default_is_stmt;
+
+ // This parameter affects the meaning of the special opcodes.
+ int8_t _line_base;
+
+ // This parameter affects the meaning of the special opcodes.
+ uint8_t _line_range;
+
+ // The number assigned to the first special opcode.
+ uint8_t _opcode_base;
+
+ // This array specifies the number of LEB128 operands for each of the standard opcodes. The first element of the
+ // array corresponds to the opcode whose value is 1, and the last element corresponds to the opcode whose value is
+ // opcode_base-1. DWARF 2 uses 9 standard opcodes while DWARF 3 and 4 use 12.
+ uint8_t _standard_opcode_lengths[12];
+
+ /*
+ * The following fields are not part of the real header and are only used for the implementation.
+ */
+ // Offset where the filename strings are starting in header.
+ long _file_names_offset;
+
+ // _header_length only specifies the number of bytes following the _header_length field. It does not include
+ // the size of _unit_length, _version and _header_length itself. This constant represents the number of missing
+ // bytes to get the real size of the header:
+ // sizeof(_unit_length) + sizeof(_version) + sizeof(_header_length) = 4 + 2 + 4 = 10
+ static const uint8_t HEADER_DESCRIPTION_BYTES = 10;
+ };
+
+ // The line number program state consists of several registers that hold the current state of the line number program
+ // state machine. The state/different state registers are defined in section 6.2.2 of the DWARF 4 spec. Most of these
+ // fields (state registers) are not used to get the filename and the line number information.
+ struct LineNumberProgramState : public CHeapObj<mtInternal> {
+ // The program-counter value corresponding to a machine instruction generated by the compiler.
+ // 4 bytes on 32-bit and 8 bytes on 64-bit.
+ uintptr_t _address;
+
+ // The index of an operation within a VLIW instruction. The index of the first operation is 0. For non-VLIW
+ // architectures, this register will always be 0.
+ // The address and op_index registers, taken together, form an operation pointer that can reference any
+ // individual operation with the instruction stream. This field was introduced with DWARF 4.
+ uint32_t _op_index;
+
+ // The identity of the source file corresponding to a machine instruction.
+ uint32_t _file;
+
+ // A source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an
+ // instruction cannot be attributed to any source line.
+ uint32_t _line;
+
+ // A column number within a source line. Columns are numbered beginning at 1. The value 0 is reserved to indicate
+ // that a statement begins at the “left edge” of the line.
+ uint32_t _column;
+
+ // Indicates that the current instruction is a recommended breakpoint location.
+ bool _is_stmt;
+
+ // Indicates that the current instruction is the beginning of a basic block.
+ bool _basic_block;
+
+ // Indicates that the current address is that of the first byte after the end of a sequence of target machine
+ // instructions. end_sequence terminates a sequence of lines.
+ bool _end_sequence;
+
+ // Indicates that the current address is one (of possibly many) where execution should be suspended for an entry
+ // breakpoint of a function. This field was introduced with DWARF 3.
+ bool _prologue_end;
+
+ // Indicates that the current address is one (of possibly many) where execution should be suspended for an exit
+ // breakpoint of a function. This field was introduced with DWARF 3.
+ bool _epilogue_begin;
+
+ // Encodes the applicable instruction set architecture for the current instruction.
+ // This field was introduced with DWARF 3.
+ uint32_t _isa;
+
+ // Identifies the block to which the current instruction belongs. This field was introduced with DWARF 4.
+ uint32_t _discriminator;
+
+ /*
+ * Additional fields which are not part of the actual state as described in DWARF spec.
+ */
+ // Header fields
+ // Specifies which DWARF version is used in the .debug_line section. Supported version: DWARF 2, 3, and 4.
+ const uint16_t _dwarf_version;
+ const bool _initial_is_stmt;
+
+ // Implementation specific fields
+ bool _append_row;
+ bool _do_reset;
+ bool _first_entry_in_sequence;
+ bool _can_sequence_match_offset;
+ bool _found_match;
+
+ LineNumberProgramState(const LineNumberProgramHeader& header)
+ : _is_stmt(header._default_is_stmt != 0), _dwarf_version(header._version),
+ _initial_is_stmt(header._default_is_stmt != 0), _found_match(false) {
+ reset_fields();
+ }
+
+ void reset_fields();
+ // Defined in section 6.2.5.1 of the DWARF spec 4. add_to_address_register() must always be executed before set_index_register.
+ void add_to_address_register(uint32_t operation_advance, const LineNumberProgramHeader& header);
+ void set_index_register(uint32_t operation_advance, const LineNumberProgramHeader& header);
+ };
+
+ DwarfFile* _dwarf_file;
+ MarkedDwarfFileReader _reader;
+ LineNumberProgramHeader _header;
+ LineNumberProgramState* _state;
+ const uint32_t _offset_in_library;
+ const uint64_t _debug_line_offset;
+ bool _is_pc_after_call;
+
+ bool read_header();
+ bool run_line_number_program(char* filename, size_t filename_len, int* line);
+ bool apply_opcode();
+ bool apply_extended_opcode();
+ bool apply_standard_opcode(uint8_t opcode);
+ void apply_special_opcode(const uint8_t opcode);
+ bool does_offset_match_entry(uintptr_t previous_address, uint32_t previous_file, uint32_t previous_line);
+ void print_and_store_prev_entry(uint32_t previous_file, uint32_t previous_line);
+ bool get_filename_from_header(uint32_t file_index, char* filename, size_t filename_len);
+
+ public:
+ LineNumberProgram(DwarfFile* dwarf_file, uint32_t offset_in_library, uint64_t debug_line_offset, bool is_pc_after_call)
+ : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _offset_in_library(offset_in_library),
+ _debug_line_offset(debug_line_offset), _is_pc_after_call(is_pc_after_call) {}
+
+ bool find_filename_and_line_number(char* filename, size_t filename_len, int* line);
+ };
+
+ public:
+ DwarfFile(const char* filepath) : ElfFile(filepath) {}
+
+ /*
+ * Starting point of reading line number and filename information from the DWARF file.
+ *
+ * Given: Offset into the ELF library file, a filename buffer of size filename_size, a line number pointer.
+ * Return: True: The filename is set in the 'filename' buffer and the line number at the address pointed to by 'line'.
+ * False: Something went wrong either while reading from the file or during parsing due to an unexpected format.
+ * This could happen if the DWARF file is in an unsupported or wrong format.
+ *
+ * More details about the different phases can be found at the associated methods.
+ */
+ bool get_filename_and_line_number(uint32_t offset_in_library, char* filename, size_t filename_len, int* line, bool is_pc_after_call);
};
#endif // !_WINDOWS && !__APPLE__
diff --git a/hotspot/src/share/vm/utilities/nativeCallStack.cpp b/hotspot/src/share/vm/utilities/nativeCallStack.cpp
index ee6eb31..6b18b49 100644
--- a/hotspot/src/share/vm/utilities/nativeCallStack.cpp
+++ b/hotspot/src/share/vm/utilities/nativeCallStack.cpp
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "runtime/os.hpp"
+#include "utilities/decoder.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/nativeCallStack.hpp"
@@ -93,6 +94,8 @@ void NativeCallStack::print_on(outputStream* out, int indent) const {
address pc;
char buf[1024];
int offset;
+ int line_no;
+
if (is_empty()) {
for (int index = 0; index < indent; index ++) out->print(" ");
#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED
@@ -107,10 +110,14 @@ void NativeCallStack::print_on(outputStream* out, int indent) const {
// Print indent
for (int index = 0; index < indent; index ++) out->print(" ");
if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- out->print_cr("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset);
+ out->print("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset);
} else {
- out->print_cr("[" PTR_FORMAT "]", p2i(pc));
+ out->print("[" PTR_FORMAT "]", p2i(pc));
+ }
+ if (Decoder::get_source_info(pc, buf, sizeof(buf), &line_no, frame != 0)) {
+ out->print(" (%s:%d)", buf, line_no);
}
+ out->cr();
}
}
}
diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp
index 261591d..26408fa 100644
--- a/hotspot/src/share/vm/utilities/vmError.cpp
+++ b/hotspot/src/share/vm/utilities/vmError.cpp
@@ -1209,3 +1209,9 @@ void VMError::report_java_out_of_memory() {
VMThread::execute(&op);
}
}
+
+// Returns true if the current thread reported a fatal error.
+bool VMError::is_error_reported_in_current_thread() {
+ return first_error_tid == os::current_thread_id();
+}
+
diff --git a/hotspot/src/share/vm/utilities/vmError.hpp b/hotspot/src/share/vm/utilities/vmError.hpp
index 299cfaa..21db84d 100644
--- a/hotspot/src/share/vm/utilities/vmError.hpp
+++ b/hotspot/src/share/vm/utilities/vmError.hpp
@@ -140,6 +140,9 @@ public:
static jlong get_first_error_tid() {
return first_error_tid;
}
+
+ // Returns true if the current thread reported a fatal error.
+ static bool is_error_reported_in_current_thread();
};
#endif // SHARE_VM_UTILITIES_VMERROR_HPP
diff --git a/jdk/test/jdk/java/dwarf/TestDwarf.java b/jdk/test/jdk/java/dwarf/TestDwarf.java
new file mode 100644
index 0000000..8e41a28
--- /dev/null
+++ b/jdk/test/jdk/java/dwarf/TestDwarf.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
+ */
+
+/*
+ * @test
+ * @bug 8242181
+ * @library ../../../lib/ ../../../lib/testlibrary
+ * @summary Test DWARF parser with various crashes if debug symbols are available. If the libjvm debug symbols are not
+ * in the same directory as the libjvm.so file, in a subdirectory called .debug, or in the path specified
+ * by the environment variable _JVM_DWARF_PATH, then no verification of the hs_err_file is done for libjvm.so.
+ * @requires vm.compMode != "Xint" & os.family == "linux"
+ * @run main/othervm TestDwarf
+ */
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Platform;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.ProcessTools;
+
+import sun.misc.Unsafe;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TestDwarf {
+ public static void main(String[] args) throws Throwable {
+ if (args.length != 0) {
+ switch (args[0]) {
+ case "outOfMemory" :
+ crashOutOfMemory();
+ Asserts.fail("Should crash in crashOutOfMemory()");
+ case "abortVMOnException" :
+ crashAbortVmOnException();
+ Asserts.fail("Should crash in crashAbortVmOnException()");
+ }
+ } else {
+ try {
+ test();
+ } catch (UnsupportedDwarfVersionException e) {
+ System.out.println("Skip test due to a DWARF section that is in an unsupported version by the parser.");
+ }
+ }
+ }
+
+ // Crash the VM in different ways in order to verify that DWARF parsing is able to print the source information
+ // in the hs_err_files for each VM and C stack frame.
+ private static void test() throws Throwable {
+ runAndCheck(new Flags("-Xcomp", "-XX:CICrashAt=1", "-version"));
+ runAndCheck(new Flags("-Xmx100M", "-XX:ErrorHandlerTest=15", "-version"));
+ runAndCheck(new Flags("-XX:+CrashGCForDumpingJavaThread", "-version"));
+ runAndCheck(new Flags("-Xmx10m", "-XX:+CrashOnOutOfMemoryError", TestDwarf.class.getCanonicalName(), "outOfMemory"));
+ // Use -XX:-TieredCompilation as C1 is currently not aborting the VM (JDK-8264899).
+ runAndCheck(new Flags("-XX:-TieredCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:AbortVMOnException=MyException",
+ TestDwarf.class.getCanonicalName(), "abortVMOnException"));
+ if (Platform.isX64() || Platform.isX86()) {
+ // Not all platforms raise SIGFPE but x86_32 and x86_64 do.
+ }
+ }
+
+ private static void runAndCheck(Flags flags, DwarfConstraint... constraints) throws Throwable {
+ OutputAnalyzer crashOut;
+ ProcessBuilder pb;
+ int flag_size = flags.getFlags().size();
+ pb = ProcessTools.createJavaProcessBuilder(flags.getFlags().toArray(new String[flag_size]));
+ crashOut = ProcessTools.executeProcess(pb);
+ String crashOutputString = crashOut.getOutput();
+ Asserts.assertNotEquals(crashOut.getExitValue(), 0, "Crash JVM should not exit gracefully");
+ Pattern pattern = Pattern.compile("hs_err_pid[0-9]*.log");
+ Matcher matcher = pattern.matcher(crashOutputString);
+ System.out.println(crashOutputString);
+ if (matcher.find()) {
+ String hsErrFileName = matcher.group();
+ System.out.println("hs_err_file: " + hsErrFileName);
+ File hs_err_file = new File(hsErrFileName);
+ BufferedReader reader = new BufferedReader(new FileReader(hs_err_file));
+ String line;
+ boolean foundNativeFrames = false;
+ int matches = 0;
+ int frameIdx = 0;
+ // Check all stack entries after the line starting with "Native frames" in the hs_err_file until an empty line
+ // is found which denotes the end of the stack frames.
+ while ((line = reader.readLine()) != null) {
+ if (foundNativeFrames) {
+ if (line.isEmpty()) {
+ // Done with the entire stack.
+ break;
+ } else if ((line.startsWith("C") || line.startsWith("V"))) {
+ // Could be VM or native C frame. There are usually no symbols available for libpthread.so.
+ matches++;
+ // File and library names are non-empty and may contain English letters, underscores, dots or numbers ([a-zA-Z0-9_.]+).
+ // Line numbers have at least one digit and start with non-zero ([1-9][0-9]*).
+ pattern = Pattern.compile("[CV][\\s\\t]+\\[([a-zA-Z0-9_.]+)\\+0x.+][\\s\\t]+.*\\+0x.+[\\s\\t]+\\([a-zA-Z0-9_.]+\\.[a-z]+:[1-9][0-9]*\\)");
+ matcher = pattern.matcher(line);
+ if (!matcher.find()) {
+ checkNoSourceLine(crashOutputString, line);
+ }
+
+ // Check additional DWARF constraints
+ if (constraints != null) {
+ int finalFrameIdx = frameIdx;
+ String finalLine = line;
+ Arrays.stream(constraints).forEach(c -> c.checkConstraint(finalFrameIdx, finalLine));
+ }
+ }
+ frameIdx++;
+ } else if (line.startsWith("Native frames")) {
+ // Stack starts after this line.
+ foundNativeFrames = true;
+ }
+ }
+ Asserts.assertGreaterThan(matches, 0, "Could not find any stack frames");
+ } else {
+ throw new RuntimeException("Could not find an hs_err_file");
+ }
+ }
+
+ /**
+ * There are some valid cases where we cannot find source information. Check these.
+ */
+ private static void checkNoSourceLine(String crashOutputString, String line) {
+ Pattern pattern = Pattern.compile("[CV][\\s\\t]+\\[([a-zA-Z0-9_.]+)\\+0x.+][\\s\\t]+.*\\+0x");
+ Matcher matcher = pattern.matcher(line);
+ Asserts.assertTrue(matcher.find(), "Must find library in \"" + line + "\"");
+ // Check if there are symbols available for library. If not, then we cannot find any source information for this library.
+ // This can happen if this test is run without any JDK debug symbols at all but also for some libraries like libpthread.so
+ // which usually has no symbols available.
+ String library = matcher.group(1);
+ pattern = Pattern.compile("Failed to load DWARF file for library.*" + library + ".*or find DWARF sections directly inside it");
+ matcher = pattern.matcher(crashOutputString);
+ if (!matcher.find()) {
+ bailoutIfUnsupportedDwarfVersion(crashOutputString);
+ throw new RuntimeException("Could not find filename or line number in \"" + line + "\"");
+ }
+ // We should always find symbols for libTestDwarf.so.
+ Asserts.assertFalse(library.equals("libTestDwarf.so"), "Could not find filename or line number in \"" + line + "\" for libTestDwarf.so");
+ System.out.println("Did not find symbols for " + library + ". If they are not in the same directory as " + library + " consider setting " +
+ "the environmental variable _JVM_DWARF_PATH to point to the debug symbols directory.");
+ }
+
+ /**
+ * Some older GCC versions might emit DWARF sections in an old format that is not supported by the DWARF parser.
+ * If this is the case, skip this entire test by throwing UnsupportedDwarfVersionException.
+ */
+ private static void bailoutIfUnsupportedDwarfVersion(String crashOutputString) {
+ Pattern pattern = Pattern.compile(".debug_\\S+ in unsupported DWARF version \\d+");
+ Matcher matcher = pattern.matcher(crashOutputString);
+ if (matcher.find()) {
+ throw new UnsupportedDwarfVersionException();
+ }
+ }
+
+ // Crash with SIGSEGV.
+ private static void crashUnsafeAccess() throws Exception {
+ Field f = Unsafe.class.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ Unsafe unsafe = (Unsafe)f.get(null);
+ unsafe.putAddress(0, 0); // Crash
+ }
+
+ // Crash with Internal Error: Java heap space.
+ private static void crashOutOfMemory() {
+ Object[] o = null;
+
+ // Loop endlessly and consume memory until we run out. Will crash due to -XX:+CrashOnOutOfMemoryError.
+ while (true) {
+ o = new Object[] {o};
+ }
+ }
+
+ // Crash with Internal Error: Saw java.lang.RuntimeException, aborting.
+ // Crash happens due to an exception raised in combination with -XX:AbortVMOnException.
+ private static void crashAbortVmOnException() {
+ throw new MyException();
+ }
+}
+
+class UnsupportedDwarfVersionException extends RuntimeException { }
+
+class MyException extends RuntimeException { }
+
+class Flags {
+ private final List<String> listOfOptions = new ArrayList<>();
+
+ Flags(String... flags) {
+ listOfOptions.add("-XX:TraceDwarfLevel=2"); // Always add debug flag
+ listOfOptions.addAll(Arrays.asList(flags));
+ }
+
+ public List<String> getFlags() {
+ return listOfOptions;
+ }
+
+}
+class DwarfConstraint {
+ private final int frameIdx;
+ private final String methodName;
+ private final String dwarfInfo;
+
+ DwarfConstraint(int frameIdx, String methodName, String fileName, int lineNo) {
+ this.frameIdx = frameIdx;
+ this.methodName = methodName;
+ this.dwarfInfo = "(" + fileName + ":" + lineNo + ")";
+ }
+
+ public void checkConstraint(int currentFrameIdx, String line) {
+ if (frameIdx == currentFrameIdx) {
+ Asserts.assertTrue(line.contains(methodName), "Could not find method name " + methodName + " in \"" + line + "\"");
+ Asserts.assertTrue(line.contains(dwarfInfo) , "Could not find DWARF info " + dwarfInfo + " in \"" + line + "\"");
+ }
+ }
+}
+
--
1.8.3.1
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。