diff --git a/Userland/Libraries/LibDebug/DebugInfo.cpp b/Userland/Libraries/LibDebug/DebugInfo.cpp index 43d7c0230c..1199999059 100644 --- a/Userland/Libraries/LibDebug/DebugInfo.cpp +++ b/Userland/Libraries/LibDebug/DebugInfo.cpp @@ -92,7 +92,7 @@ void DebugInfo::prepare_lines() Vector all_lines; while (!stream.eof()) { - Dwarf::LineProgram program(stream); + Dwarf::LineProgram program(m_dwarf_info, stream); all_lines.append(program.lines()); } diff --git a/Userland/Libraries/LibDebug/Dwarf/LineProgram.cpp b/Userland/Libraries/LibDebug/Dwarf/LineProgram.cpp index b83b024fe6..1c662ba77a 100644 --- a/Userland/Libraries/LibDebug/Dwarf/LineProgram.cpp +++ b/Userland/Libraries/LibDebug/Dwarf/LineProgram.cpp @@ -6,13 +6,15 @@ #include "LineProgram.h" #include +#include #include #include namespace Debug::Dwarf { -LineProgram::LineProgram(InputMemoryStream& stream) - : m_stream(stream) +LineProgram::LineProgram(DwarfInfo& dwarf_info, InputMemoryStream& stream) + : m_dwarf_info(dwarf_info) + , m_stream(stream) { m_unit_offset = m_stream.offset(); parse_unit_header(); @@ -23,45 +25,100 @@ LineProgram::LineProgram(InputMemoryStream& stream) void LineProgram::parse_unit_header() { - m_stream >> Bytes { &m_unit_header, sizeof(m_unit_header) }; + m_stream >> m_unit_header; - VERIFY(m_unit_header.version == DWARF_VERSION); - VERIFY(m_unit_header.opcode_base == SPECIAL_OPCODES_BASE); + VERIFY(m_unit_header.version() >= MIN_DWARF_VERSION && m_unit_header.version() <= MAX_DWARF_VERSION); + VERIFY(m_unit_header.opcode_base() <= sizeof(m_unit_header.std_opcode_lengths) / sizeof(m_unit_header.std_opcode_lengths[0]) + 1); - dbgln_if(DWARF_DEBUG, "unit length: {}", m_unit_header.length); + dbgln_if(DWARF_DEBUG, "unit length: {}", m_unit_header.length()); +} + +void LineProgram::parse_path_entries(Function callback, PathListType list_type) +{ + if (m_unit_header.version() >= 5) { + u8 path_entry_format_count = 0; + m_stream >> path_entry_format_count; + + Vector format_descriptions; + + for (u8 i = 0; i < path_entry_format_count; i++) { + size_t content_type = 0; + m_stream.read_LEB128_unsigned(content_type); + + size_t data_form = 0; + m_stream.read_LEB128_unsigned(data_form); + + format_descriptions.empend((ContentType)content_type, (AttributeDataForm)data_form); + } + + size_t paths_count = 0; + m_stream.read_LEB128_unsigned(paths_count); + + for (size_t i = 0; i < paths_count; i++) { + PathEntry entry; + for (auto& format_description : format_descriptions) { + auto value = m_dwarf_info.get_attribute_value(format_description.form, m_stream); + switch (format_description.type) { + case ContentType::Path: + entry.path = value.data.as_string; + break; + case ContentType::DirectoryIndex: + entry.directory_index = value.data.as_u32; + break; + default: + dbgln_if(DWARF_DEBUG, "Unhandled path list attribute: {}", (int)format_description.type); + } + } + callback(entry); + } + } else { + while (m_stream.peek_or_error()) { + String path; + m_stream >> path; + dbgln_if(DWARF_DEBUG, "path: {}", path); + PathEntry entry; + entry.path = path; + if (list_type == PathListType::Filenames) { + size_t directory_index = 0; + m_stream.read_LEB128_unsigned(directory_index); + size_t _unused = 0; + m_stream.read_LEB128_unsigned(_unused); // skip modification time + m_stream.read_LEB128_unsigned(_unused); // skip file size + entry.directory_index = directory_index; + dbgln_if(DWARF_DEBUG, "file: {}, directory index: {}", path, directory_index); + } + callback(entry); + } + + m_stream.handle_recoverable_error(); + m_stream.discard_or_error(1); + } + + VERIFY(!m_stream.has_any_error()); } void LineProgram::parse_source_directories() { - m_source_directories.append("."); - - while (m_stream.peek_or_error()) { - String directory; - m_stream >> directory; - dbgln_if(DWARF_DEBUG, "directory: {}", directory); - m_source_directories.append(move(directory)); + if (m_unit_header.version() < 5) { + m_source_directories.append("."); } - m_stream.handle_recoverable_error(); - m_stream.discard_or_error(1); - VERIFY(!m_stream.has_any_error()); + + parse_path_entries([this](PathEntry& entry) { + m_source_directories.append(entry.path); + }, + PathListType::Directories); } void LineProgram::parse_source_files() { - m_source_files.append({ ".", 0 }); - while (!m_stream.eof() && m_stream.peek_or_error()) { - String file_name; - m_stream >> file_name; - size_t directory_index = 0; - m_stream.read_LEB128_unsigned(directory_index); - size_t _unused = 0; - m_stream.read_LEB128_unsigned(_unused); // skip modification time - m_stream.read_LEB128_unsigned(_unused); // skip file size - dbgln_if(DWARF_DEBUG, "file: {}, directory index: {}", file_name, directory_index); - m_source_files.append({ file_name, directory_index }); + if (m_unit_header.version() < 5) { + m_source_files.append({ ".", 0 }); } - m_stream.discard_or_error(1); - VERIFY(!m_stream.has_any_error()); + + parse_path_entries([this](PathEntry& entry) { + m_source_files.append({ entry.path, entry.directory_index }); + }, + PathListType::Filenames); } void LineProgram::append_to_line_info() @@ -88,7 +145,7 @@ void LineProgram::reset_registers() m_address = 0; m_line = 1; m_file_index = 1; - m_is_statement = m_unit_header.default_is_stmt == 1; + m_is_statement = m_unit_header.default_is_stmt() == 1; } void LineProgram::handle_extended_opcode() @@ -131,7 +188,7 @@ void LineProgram::handle_standard_opcode(u8 opcode) case StandardOpcodes::AdvancePc: { size_t operand = 0; m_stream.read_LEB128_unsigned(operand); - size_t delta = operand * m_unit_header.min_instruction_length; + size_t delta = operand * m_unit_header.min_instruction_length(); dbgln_if(DWARF_DEBUG, "AdvancePC by: {} to: {:p}", delta, m_address + delta); m_address += delta; break; @@ -165,9 +222,9 @@ void LineProgram::handle_standard_opcode(u8 opcode) break; } case StandardOpcodes::ConstAddPc: { - u8 adjusted_opcode = 255 - SPECIAL_OPCODES_BASE; - ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range) * m_unit_header.min_instruction_length; - address_increment *= m_unit_header.min_instruction_length; + u8 adjusted_opcode = 255 - m_unit_header.opcode_base(); + ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range()) * m_unit_header.min_instruction_length(); + address_increment *= m_unit_header.min_instruction_length(); dbgln_if(DWARF_DEBUG, "ConstAddPc: advance pc by: {} to: {}", address_increment, (m_address + address_increment)); m_address += address_increment; break; @@ -192,9 +249,9 @@ void LineProgram::handle_standard_opcode(u8 opcode) } void LineProgram::handle_special_opcode(u8 opcode) { - u8 adjusted_opcode = opcode - SPECIAL_OPCODES_BASE; - ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range) * m_unit_header.min_instruction_length; - ssize_t line_increment = m_unit_header.line_base + (adjusted_opcode % m_unit_header.line_range); + u8 adjusted_opcode = opcode - m_unit_header.opcode_base(); + ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range()) * m_unit_header.min_instruction_length(); + ssize_t line_increment = m_unit_header.line_base() + (adjusted_opcode % m_unit_header.line_range()); m_address += address_increment; m_line += line_increment; @@ -211,7 +268,7 @@ void LineProgram::run_program() { reset_registers(); - while ((size_t)m_stream.offset() < m_unit_offset + sizeof(u32) + m_unit_header.length) { + while ((size_t)m_stream.offset() < m_unit_offset + sizeof(u32) + m_unit_header.length()) { u8 opcode = 0; m_stream >> opcode; diff --git a/Userland/Libraries/LibDebug/Dwarf/LineProgram.h b/Userland/Libraries/LibDebug/Dwarf/LineProgram.h index a0b92ff3ae..5c103ad01e 100644 --- a/Userland/Libraries/LibDebug/Dwarf/LineProgram.h +++ b/Userland/Libraries/LibDebug/Dwarf/LineProgram.h @@ -6,27 +6,104 @@ #pragma once +#include "DwarfInfo.h" #include #include #include namespace Debug::Dwarf { -struct [[gnu::packed]] LineProgramUnitHeader32 { +struct [[gnu::packed]] LineProgramUnitHeader32Common { u32 length; u16 version; +}; + +struct [[gnu::packed]] LineProgramUnitHeader32V4Ext { u32 header_length; u8 min_instruction_length; + u8 max_instruction_length; u8 default_is_stmt; i8 line_base; u8 line_range; u8 opcode_base; - u8 std_opcode_lengths[12]; }; +struct [[gnu::packed]] LineProgramUnitHeader32V5Ext { + u8 address_size; + u8 segment_selector_size; + u32 header_length; + u8 min_instruction_length; + u8 max_instruction_length; + u8 default_is_stmt; + i8 line_base; + u8 line_range; + u8 opcode_base; +}; + +struct [[gnu::packed]] LineProgramUnitHeader32 { + LineProgramUnitHeader32Common common; + + union { + LineProgramUnitHeader32V4Ext v4; + LineProgramUnitHeader32V5Ext v5; + }; + + u8 std_opcode_lengths[13]; + + size_t header_size() const + { + return sizeof(common) + ((common.version <= 4) ? sizeof(v4) : sizeof(v5)) + (opcode_base() - 1) * sizeof(std_opcode_lengths[0]); + } + + u32 length() const { return common.length; } + u16 version() const { return common.version; } + u32 header_length() const { return (common.version <= 4) ? v4.header_length : v5.header_length; } + u8 min_instruction_length() const { return (common.version <= 4) ? v4.min_instruction_length : v5.min_instruction_length; } + u8 default_is_stmt() const { return (common.version <= 4) ? v4.default_is_stmt : v5.default_is_stmt; } + i8 line_base() const { return (common.version <= 4) ? v4.line_base : v5.line_base; } + u8 line_range() const { return (common.version <= 4) ? v4.line_range : v5.line_range; } + u8 opcode_base() const { return (common.version <= 4) ? v4.opcode_base : v5.opcode_base; } +}; + +enum class ContentType { + Path = 1, + DirectoryIndex = 2, + Timestamp = 3, + Size = 4, + MD5 = 5, + LoUser = 0x2000, + HiUser = 0x3fff, +}; + +struct PathEntryFormat { + ContentType type; + AttributeDataForm form; +}; + +struct PathEntry { + String path; + size_t directory_index { 0 }; +}; + +enum class PathListType { + Directories, + Filenames, +}; + +inline InputStream& operator>>(InputStream& stream, LineProgramUnitHeader32& header) +{ + stream.read_or_error(Bytes { &header.common, sizeof(header.common) }); + if (header.common.version <= 4) + stream.read_or_error(Bytes { &header.v4, sizeof(header.v4) }); + else + stream.read_or_error(Bytes { &header.v5, sizeof(header.v5) }); + stream.read_or_error(Bytes { &header.std_opcode_lengths, min(sizeof(header.std_opcode_lengths), (header.opcode_base() - 1) * sizeof(header.std_opcode_lengths[0])) }); + return stream; +} + class LineProgram { public: - explicit LineProgram(InputMemoryStream& stream); + explicit LineProgram(DwarfInfo& dwarf_info, InputMemoryStream& stream); struct LineInfo { u32 address { 0 }; @@ -49,6 +126,8 @@ private: void handle_standard_opcode(u8 opcode); void handle_special_opcode(u8 opcode); + void parse_path_entries(Function callback, PathListType list_type); + enum StandardOpcodes { Copy = 1, AdvancePc, @@ -76,9 +155,10 @@ private: size_t directory_index { 0 }; }; - static constexpr u16 DWARF_VERSION = 3; - static constexpr u8 SPECIAL_OPCODES_BASE = 13; + static constexpr u16 MIN_DWARF_VERSION = 3; + static constexpr u16 MAX_DWARF_VERSION = 5; + DwarfInfo& m_dwarf_info; InputMemoryStream& m_stream; size_t m_unit_offset { 0 };