Procházet zdrojové kódy

LibDebug: Support `addrx*`, `strx*` and `rnglistx` forms

These forms were introduced in DWARF5, and have a fair deal of
advantages over the more traditional encodings: they reduce the size of
the binary and the number of relocations.

GCC does not emit these with `-g1` by default, but Clang does at all
debug levels.
Daniel Bertalan před 3 roky
rodič
revize
ac53569bd1

+ 1 - 0
Userland/Libraries/LibDebug/CMakeLists.txt

@@ -3,6 +3,7 @@ set(SOURCES
     DebugSession.cpp
     Dwarf/AbbreviationsMap.cpp
     Dwarf/AddressRanges.cpp
+    Dwarf/AttributeValue.cpp
     Dwarf/CompilationUnit.cpp
     Dwarf/DIE.cpp
     Dwarf/DwarfInfo.cpp

+ 49 - 0
Userland/Libraries/LibDebug/Dwarf/AttributeValue.cpp

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, Daniel Bertalan <dani@danielbertalan.dev>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "AttributeValue.h"
+#include "CompilationUnit.h"
+
+namespace Debug::Dwarf {
+
+FlatPtr AttributeValue::as_addr() const
+{
+    switch (m_form) {
+    case AttributeDataForm::Addr:
+        return m_data.as_addr;
+    case AttributeDataForm::AddrX:
+    case AttributeDataForm::AddrX1:
+    case AttributeDataForm::AddrX2:
+    case AttributeDataForm::AddrX3:
+    case AttributeDataForm::AddrX4: {
+        auto index = m_data.as_unsigned;
+        return m_compilation_unit->get_address(index);
+    }
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+char const* AttributeValue::as_string() const
+{
+    switch (m_form) {
+    case AttributeDataForm::String:
+    case AttributeDataForm::StringPointer:
+    case AttributeDataForm::LineStrP:
+        return m_data.as_string;
+    case AttributeDataForm::StrX:
+    case AttributeDataForm::StrX1:
+    case AttributeDataForm::StrX2:
+    case AttributeDataForm::StrX3:
+    case AttributeDataForm::StrX4: {
+        auto index = m_data.as_unsigned;
+        return m_compilation_unit->get_string(index);
+    }
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+}

+ 2 - 2
Userland/Libraries/LibDebug/Dwarf/AttributeValue.h

@@ -33,10 +33,10 @@ public:
     Type type() const { return m_type; }
     AttributeDataForm form() const { return m_form; }
 
-    FlatPtr as_addr() const { return m_data.as_addr; }
+    FlatPtr as_addr() const;
     u64 as_unsigned() const { return m_data.as_unsigned; }
     i64 as_signed() const { return m_data.as_signed; }
-    const char* as_string() const { return m_data.as_string; }
+    const char* as_string() const;
     bool as_bool() const { return m_data.as_bool; }
     ReadonlyBytes as_raw_bytes() const { return m_data.as_raw_bytes; }
 

+ 66 - 0
Userland/Libraries/LibDebug/Dwarf/CompilationUnit.cpp

@@ -44,4 +44,70 @@ Optional<FlatPtr> CompilationUnit::base_address() const
     return m_cached_base_address;
 }
 
+u64 CompilationUnit::address_table_base() const
+{
+    if (m_has_cached_address_table_base)
+        return m_cached_address_table_base;
+
+    auto die = root_die();
+    auto res = die.get_attribute(Attribute::AddrBase);
+    if (res.has_value()) {
+        VERIFY(res->form() == AttributeDataForm::SecOffset);
+        m_cached_address_table_base = res->as_unsigned();
+    }
+    m_has_cached_address_table_base = true;
+    return m_cached_address_table_base;
+}
+
+u64 CompilationUnit::string_offsets_base() const
+{
+    if (m_has_cached_string_offsets_base)
+        return m_cached_string_offsets_base;
+
+    auto die = root_die();
+    auto res = die.get_attribute(Attribute::StrOffsetsBase);
+    if (res.has_value()) {
+        VERIFY(res->form() == AttributeDataForm::SecOffset);
+        m_cached_string_offsets_base = res->as_unsigned();
+    }
+    m_has_cached_string_offsets_base = true;
+    return m_cached_string_offsets_base;
+}
+
+u64 CompilationUnit::range_lists_base() const
+{
+    if (m_has_cached_range_lists_base)
+        return m_cached_range_lists_base;
+
+    auto die = root_die();
+    auto res = die.get_attribute(Attribute::RngListsBase);
+    if (res.has_value()) {
+        VERIFY(res->form() == AttributeDataForm::SecOffset);
+        m_cached_range_lists_base = res->as_unsigned();
+    }
+    m_has_cached_range_lists_base = true;
+    return m_cached_range_lists_base;
+}
+
+FlatPtr CompilationUnit::get_address(size_t index) const
+{
+    auto base = address_table_base();
+    auto debug_addr_data = dwarf_info().debug_addr_data();
+    VERIFY(base < debug_addr_data.size());
+    auto addresses = reinterpret_cast<FlatPtr const*>(debug_addr_data.offset(base));
+    VERIFY(base + index * sizeof(FlatPtr) < debug_addr_data.size());
+    return addresses[index];
+}
+
+char const* CompilationUnit::get_string(size_t index) const
+{
+    auto base = string_offsets_base();
+    auto debug_str_offsets_data = dwarf_info().debug_str_offsets_data();
+    VERIFY(base < debug_str_offsets_data.size());
+    // FIXME: This assumes DWARF32
+    auto offsets = reinterpret_cast<u32 const*>(debug_str_offsets_data.offset(base));
+    VERIFY(base + index * sizeof(u32) < debug_str_offsets_data.size());
+    auto offset = offsets[index];
+    return reinterpret_cast<char const*>(dwarf_info().debug_strings_data().offset(offset));
+}
 }

+ 17 - 1
Userland/Libraries/LibDebug/Dwarf/CompilationUnit.h

@@ -31,19 +31,35 @@ public:
     DIE root_die() const;
     DIE get_die_at_offset(u32 offset) const;
 
+    FlatPtr get_address(size_t index) const;
+    char const* get_string(size_t index) const;
+
     DwarfInfo const& dwarf_info() const { return m_dwarf_info; }
     AbbreviationsMap const& abbreviations_map() const { return m_abbreviations; }
     LineProgram const& line_program() const { return *m_line_program; }
     Optional<FlatPtr> base_address() const;
 
+    // DW_AT_addr_base
+    u64 address_table_base() const;
+    // DW_AT_str_offsets_base
+    u64 string_offsets_base() const;
+    // DW_AT_rnglists_base
+    u64 range_lists_base() const;
+
 private:
     DwarfInfo const& m_dwarf_info;
     u32 m_offset { 0 };
     CompilationUnitHeader m_header;
     AbbreviationsMap m_abbreviations;
     NonnullOwnPtr<LineProgram> m_line_program;
-    mutable bool m_has_cached_base_address { false };
+    mutable bool m_has_cached_base_address : 1 { false };
+    mutable bool m_has_cached_address_table_base : 1 { false };
+    mutable bool m_has_cached_string_offsets_base : 1 { false };
+    mutable bool m_has_cached_range_lists_base : 1 { false };
     mutable Optional<FlatPtr> m_cached_base_address;
+    mutable u64 m_cached_address_table_base { 0 };
+    mutable u64 m_cached_string_offsets_base { 0 };
+    mutable u64 m_cached_range_lists_base { 0 };
 };
 
 }

+ 74 - 0
Userland/Libraries/LibDebug/Dwarf/DwarfInfo.cpp

@@ -23,6 +23,8 @@ DwarfInfo::DwarfInfo(ELF::Image const& elf)
     m_debug_line_data = section_data(".debug_line"sv);
     m_debug_line_strings_data = section_data(".debug_line_str"sv);
     m_debug_range_lists_data = section_data(".debug_rnglists"sv);
+    m_debug_str_offsets_data = section_data(".debug_str_offsets"sv);
+    m_debug_addr_data = section_data(".debug_addr"sv);
 
     populate_compilation_units();
 }
@@ -230,6 +232,78 @@ AttributeValue DwarfInfo::get_attribute_value(AttributeDataForm form, ssize_t im
         value.m_data.as_signed = implicit_const_value;
         break;
     }
+    case AttributeDataForm::StrX1: {
+        u8 index;
+        debug_info_stream >> index;
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::String;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::StrX2: {
+        u16 index;
+        debug_info_stream >> index;
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::String;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::StrX4: {
+        u32 index;
+        debug_info_stream >> index;
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::String;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::StrX: {
+        size_t index;
+        debug_info_stream.read_LEB128_unsigned(index);
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::String;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::AddrX1: {
+        u8 index;
+        debug_info_stream >> index;
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::Address;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::AddrX2: {
+        u16 index;
+        debug_info_stream >> index;
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::Address;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::AddrX4: {
+        u32 index;
+        debug_info_stream >> index;
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::Address;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::AddrX: {
+        size_t index;
+        debug_info_stream.read_LEB128_unsigned(index);
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::Address;
+        value.m_data.as_unsigned = index;
+        break;
+    }
+    case AttributeDataForm::RngListX: {
+        size_t index;
+        debug_info_stream.read_LEB128_unsigned(index);
+        VERIFY(!debug_info_stream.has_any_error());
+        value.m_type = AttributeValue::Type::UnsignedNumber;
+        value.m_data.as_unsigned = index;
+        break;
+    }
     default:
         dbgln("Unimplemented AttributeDataForm: {}", (u32)form);
         VERIFY_NOT_REACHED();

+ 4 - 0
Userland/Libraries/LibDebug/Dwarf/DwarfInfo.h

@@ -31,6 +31,8 @@ public:
     ReadonlyBytes debug_strings_data() const { return m_debug_strings_data; }
     ReadonlyBytes debug_line_strings_data() const { return m_debug_line_strings_data; }
     ReadonlyBytes debug_range_lists_data() const { return m_debug_range_lists_data; }
+    ReadonlyBytes debug_str_offsets_data() const { return m_debug_str_offsets_data; }
+    ReadonlyBytes debug_addr_data() const { return m_debug_addr_data; }
 
     template<typename Callback>
     void for_each_compilation_unit(Callback) const;
@@ -60,6 +62,8 @@ private:
     ReadonlyBytes m_debug_line_data;
     ReadonlyBytes m_debug_line_strings_data;
     ReadonlyBytes m_debug_range_lists_data;
+    ReadonlyBytes m_debug_str_offsets_data;
+    ReadonlyBytes m_debug_addr_data;
 
     NonnullOwnPtrVector<Dwarf::CompilationUnit> m_compilation_units;