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
