From ac53569bd11d1425d5ff77bd0b8a22d26222a7ca Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Sat, 9 Oct 2021 17:38:26 +0200 Subject: 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. --- Userland/Libraries/LibDebug/CMakeLists.txt | 1 + .../Libraries/LibDebug/Dwarf/AttributeValue.cpp | 49 ++++++++++++++ Userland/Libraries/LibDebug/Dwarf/AttributeValue.h | 4 +- .../Libraries/LibDebug/Dwarf/CompilationUnit.cpp | 66 +++++++++++++++++++ .../Libraries/LibDebug/Dwarf/CompilationUnit.h | 18 +++++- Userland/Libraries/LibDebug/Dwarf/DwarfInfo.cpp | 74 ++++++++++++++++++++++ Userland/Libraries/LibDebug/Dwarf/DwarfInfo.h | 4 ++ 7 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 Userland/Libraries/LibDebug/Dwarf/AttributeValue.cpp diff --git a/Userland/Libraries/LibDebug/CMakeLists.txt b/Userland/Libraries/LibDebug/CMakeLists.txt index e03bc1f625..f823a550d5 100644 --- a/Userland/Libraries/LibDebug/CMakeLists.txt +++ b/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 diff --git a/Userland/Libraries/LibDebug/Dwarf/AttributeValue.cpp b/Userland/Libraries/LibDebug/Dwarf/AttributeValue.cpp new file mode 100644 index 0000000000..c81809f04f --- /dev/null +++ b/Userland/Libraries/LibDebug/Dwarf/AttributeValue.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, Daniel Bertalan + * + * 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(); + } +} +} diff --git a/Userland/Libraries/LibDebug/Dwarf/AttributeValue.h b/Userland/Libraries/LibDebug/Dwarf/AttributeValue.h index c6cd967384..9343328abf 100644 --- a/Userland/Libraries/LibDebug/Dwarf/AttributeValue.h +++ b/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; } diff --git a/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.cpp b/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.cpp index e7cf1de168..d8b0343d76 100644 --- a/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.cpp +++ b/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.cpp @@ -44,4 +44,70 @@ Optional 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(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(debug_str_offsets_data.offset(base)); + VERIFY(base + index * sizeof(u32) < debug_str_offsets_data.size()); + auto offset = offsets[index]; + return reinterpret_cast(dwarf_info().debug_strings_data().offset(offset)); +} } diff --git a/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.h b/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.h index c860d1237a..483148e061 100644 --- a/Userland/Libraries/LibDebug/Dwarf/CompilationUnit.h +++ b/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 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 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 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 }; }; } diff --git a/Userland/Libraries/LibDebug/Dwarf/DwarfInfo.cpp b/Userland/Libraries/LibDebug/Dwarf/DwarfInfo.cpp index 3f6ae36dd4..fbda566720 100644 --- a/Userland/Libraries/LibDebug/Dwarf/DwarfInfo.cpp +++ b/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(); diff --git a/Userland/Libraries/LibDebug/Dwarf/DwarfInfo.h b/Userland/Libraries/LibDebug/Dwarf/DwarfInfo.h index 24f7e9a2d3..70c63452cc 100644 --- a/Userland/Libraries/LibDebug/Dwarf/DwarfInfo.h +++ b/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 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 m_compilation_units; -- cgit v1.2.3