Serenity Operating System
at master 317 lines 11 kB view raw
1/* 2 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "LineProgram.h" 8#include <AK/Debug.h> 9#include <AK/DeprecatedString.h> 10#include <AK/Function.h> 11#include <AK/LEB128.h> 12#include <AK/StringBuilder.h> 13#include <LibDebug/Dwarf/DwarfInfo.h> 14 15namespace Debug::Dwarf { 16 17LineProgram::LineProgram(DwarfInfo& dwarf_info, SeekableStream& stream) 18 : m_dwarf_info(dwarf_info) 19 , m_stream(stream) 20{ 21 m_unit_offset = m_stream.tell().release_value_but_fixme_should_propagate_errors(); 22 parse_unit_header().release_value_but_fixme_should_propagate_errors(); 23 parse_source_directories().release_value_but_fixme_should_propagate_errors(); 24 parse_source_files().release_value_but_fixme_should_propagate_errors(); 25 run_program().release_value_but_fixme_should_propagate_errors(); 26} 27 28ErrorOr<void> LineProgram::parse_unit_header() 29{ 30 m_unit_header = TRY(m_stream.read_value<LineProgramUnitHeader32>()); 31 32 VERIFY(m_unit_header.version() >= MIN_DWARF_VERSION && m_unit_header.version() <= MAX_DWARF_VERSION); 33 VERIFY(m_unit_header.opcode_base() <= sizeof(m_unit_header.std_opcode_lengths) / sizeof(m_unit_header.std_opcode_lengths[0]) + 1); 34 35 dbgln_if(DWARF_DEBUG, "unit length: {}", m_unit_header.length()); 36 return {}; 37} 38 39ErrorOr<void> LineProgram::parse_path_entries(Function<void(PathEntry& entry)> callback, PathListType list_type) 40{ 41 if (m_unit_header.version() >= 5) { 42 auto path_entry_format_count = TRY(m_stream.read_value<u8>()); 43 44 Vector<PathEntryFormat> format_descriptions; 45 46 for (u8 i = 0; i < path_entry_format_count; i++) { 47 UnderlyingType<ContentType> content_type = TRY(m_stream.read_value<LEB128<UnderlyingType<ContentType>>>()); 48 49 UnderlyingType<AttributeDataForm> data_form = TRY(m_stream.read_value<LEB128<UnderlyingType<AttributeDataForm>>>()); 50 51 format_descriptions.empend(static_cast<ContentType>(content_type), static_cast<AttributeDataForm>(data_form)); 52 } 53 54 size_t paths_count = TRY(m_stream.read_value<LEB128<size_t>>()); 55 56 for (size_t i = 0; i < paths_count; i++) { 57 PathEntry entry; 58 for (auto& format_description : format_descriptions) { 59 auto value = TRY(m_dwarf_info.get_attribute_value(format_description.form, 0, m_stream)); 60 switch (format_description.type) { 61 case ContentType::Path: 62 entry.path = TRY(value.as_string()); 63 break; 64 case ContentType::DirectoryIndex: 65 entry.directory_index = value.as_unsigned(); 66 break; 67 default: 68 dbgln_if(DWARF_DEBUG, "Unhandled path list attribute: {}", to_underlying(format_description.type)); 69 } 70 } 71 callback(entry); 72 } 73 } else { 74 while (true) { 75 StringBuilder builder; 76 while (auto c = TRY(m_stream.read_value<char>())) 77 TRY(builder.try_append(c)); 78 auto path = builder.to_deprecated_string(); 79 if (path.length() == 0) 80 break; 81 dbgln_if(DWARF_DEBUG, "path: {}", path); 82 PathEntry entry; 83 entry.path = path; 84 if (list_type == PathListType::Filenames) { 85 size_t directory_index = TRY(m_stream.read_value<LEB128<size_t>>()); 86 TRY(m_stream.read_value<LEB128<size_t>>()); // skip modification time 87 TRY(m_stream.read_value<LEB128<size_t>>()); // skip file size 88 entry.directory_index = directory_index; 89 dbgln_if(DWARF_DEBUG, "file: {}, directory index: {}", path, directory_index); 90 } 91 callback(entry); 92 } 93 } 94 95 return {}; 96} 97 98ErrorOr<void> LineProgram::parse_source_directories() 99{ 100 if (m_unit_header.version() < 5) { 101 m_source_directories.append("."); 102 } 103 104 TRY(parse_path_entries([this](PathEntry& entry) { 105 m_source_directories.append(entry.path); 106 }, 107 PathListType::Directories)); 108 109 return {}; 110} 111 112ErrorOr<void> LineProgram::parse_source_files() 113{ 114 if (m_unit_header.version() < 5) { 115 m_source_files.append({ ".", 0 }); 116 } 117 118 TRY(parse_path_entries([this](PathEntry& entry) { 119 m_source_files.append({ entry.path, entry.directory_index }); 120 }, 121 PathListType::Filenames)); 122 123 return {}; 124} 125 126void LineProgram::append_to_line_info() 127{ 128 dbgln_if(DWARF_DEBUG, "appending line info: {:p}, {}:{}", m_address, m_source_files[m_file_index].name, m_line); 129 if (!m_is_statement) 130 return; 131 132 if (m_file_index >= m_source_files.size()) 133 return; 134 135 auto const& directory = m_source_directories[m_source_files[m_file_index].directory_index]; 136 137 StringBuilder full_path(directory.length() + m_source_files[m_file_index].name.length() + 1); 138 full_path.append(directory); 139 full_path.append('/'); 140 full_path.append(m_source_files[m_file_index].name); 141 142 m_lines.append({ m_address, DeprecatedFlyString { full_path.string_view() }, m_line }); 143} 144 145void LineProgram::reset_registers() 146{ 147 m_address = 0; 148 m_line = 1; 149 m_file_index = 1; 150 m_is_statement = m_unit_header.default_is_stmt() == 1; 151} 152 153ErrorOr<void> LineProgram::handle_extended_opcode() 154{ 155 size_t length = TRY(m_stream.read_value<LEB128<size_t>>()); 156 157 auto sub_opcode = TRY(m_stream.read_value<u8>()); 158 159 switch (sub_opcode) { 160 case ExtendedOpcodes::EndSequence: { 161 append_to_line_info(); 162 reset_registers(); 163 break; 164 } 165 case ExtendedOpcodes::SetAddress: { 166 VERIFY(length == sizeof(size_t) + 1); 167 m_address = TRY(m_stream.read_value<FlatPtr>()); 168 dbgln_if(DWARF_DEBUG, "SetAddress: {:p}", m_address); 169 break; 170 } 171 case ExtendedOpcodes::SetDiscriminator: { 172 dbgln_if(DWARF_DEBUG, "SetDiscriminator"); 173 [[maybe_unused]] size_t discriminator = TRY(m_stream.read_value<LEB128<size_t>>()); 174 break; 175 } 176 default: 177 dbgln("Encountered unknown sub opcode {} at stream offset {:p}", sub_opcode, TRY(m_stream.tell())); 178 VERIFY_NOT_REACHED(); 179 } 180 181 return {}; 182} 183ErrorOr<void> LineProgram::handle_standard_opcode(u8 opcode) 184{ 185 switch (opcode) { 186 case StandardOpcodes::Copy: { 187 append_to_line_info(); 188 break; 189 } 190 case StandardOpcodes::AdvancePc: { 191 size_t operand = TRY(m_stream.read_value<LEB128<size_t>>()); 192 size_t delta = operand * m_unit_header.min_instruction_length(); 193 dbgln_if(DWARF_DEBUG, "AdvancePC by: {} to: {:p}", delta, m_address + delta); 194 m_address += delta; 195 break; 196 } 197 case StandardOpcodes::SetFile: { 198 size_t new_file_index = TRY(m_stream.read_value<LEB128<size_t>>()); 199 dbgln_if(DWARF_DEBUG, "SetFile: new file index: {}", new_file_index); 200 m_file_index = new_file_index; 201 break; 202 } 203 case StandardOpcodes::SetColumn: { 204 // not implemented 205 dbgln_if(DWARF_DEBUG, "SetColumn"); 206 [[maybe_unused]] size_t new_column = TRY(m_stream.read_value<LEB128<size_t>>()); 207 208 break; 209 } 210 case StandardOpcodes::AdvanceLine: { 211 ssize_t line_delta = TRY(m_stream.read_value<LEB128<ssize_t>>()); 212 VERIFY(line_delta >= 0 || m_line >= (size_t)(-line_delta)); 213 m_line += line_delta; 214 dbgln_if(DWARF_DEBUG, "AdvanceLine: {}", m_line); 215 break; 216 } 217 case StandardOpcodes::NegateStatement: { 218 dbgln_if(DWARF_DEBUG, "NegateStatement"); 219 m_is_statement = !m_is_statement; 220 break; 221 } 222 case StandardOpcodes::ConstAddPc: { 223 u8 adjusted_opcode = 255 - m_unit_header.opcode_base(); 224 ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range()) * m_unit_header.min_instruction_length(); 225 address_increment *= m_unit_header.min_instruction_length(); 226 dbgln_if(DWARF_DEBUG, "ConstAddPc: advance pc by: {} to: {}", address_increment, (m_address + address_increment)); 227 m_address += address_increment; 228 break; 229 } 230 case StandardOpcodes::SetIsa: { 231 size_t isa = TRY(m_stream.read_value<LEB128<size_t>>()); 232 dbgln_if(DWARF_DEBUG, "SetIsa: {}", isa); 233 break; 234 } 235 case StandardOpcodes::FixAdvancePc: { 236 auto delta = TRY(m_stream.read_value<u16>()); 237 dbgln_if(DWARF_DEBUG, "FixAdvancePC by: {} to: {:p}", delta, m_address + delta); 238 m_address += delta; 239 break; 240 } 241 case StandardOpcodes::SetBasicBlock: { 242 m_basic_block = true; 243 break; 244 } 245 case StandardOpcodes::SetPrologueEnd: { 246 m_prologue_end = true; 247 break; 248 } 249 default: 250 dbgln("Unhandled LineProgram opcode {}", opcode); 251 VERIFY_NOT_REACHED(); 252 } 253 254 return {}; 255} 256void LineProgram::handle_special_opcode(u8 opcode) 257{ 258 u8 adjusted_opcode = opcode - m_unit_header.opcode_base(); 259 ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range()) * m_unit_header.min_instruction_length(); 260 ssize_t line_increment = m_unit_header.line_base() + (adjusted_opcode % m_unit_header.line_range()); 261 262 m_address += address_increment; 263 m_line += line_increment; 264 265 if constexpr (DWARF_DEBUG) { 266 dbgln("Special adjusted_opcode: {}, address_increment: {}, line_increment: {}", adjusted_opcode, address_increment, line_increment); 267 dbgln("Address is now: {:p}, and line is: {}:{}", m_address, m_source_files[m_file_index].name, m_line); 268 } 269 270 append_to_line_info(); 271 272 m_basic_block = false; 273 m_prologue_end = false; 274} 275 276ErrorOr<void> LineProgram::run_program() 277{ 278 reset_registers(); 279 280 while (TRY(m_stream.tell()) < m_unit_offset + sizeof(u32) + m_unit_header.length()) { 281 auto opcode = TRY(m_stream.read_value<u8>()); 282 283 dbgln_if(DWARF_DEBUG, "{:p}: opcode: {}", TRY(m_stream.tell()) - 1, opcode); 284 285 if (opcode == 0) { 286 TRY(handle_extended_opcode()); 287 } else if (opcode >= 1 && opcode <= 12) { 288 TRY(handle_standard_opcode(opcode)); 289 } else { 290 handle_special_opcode(opcode); 291 } 292 } 293 294 return {}; 295} 296 297LineProgram::DirectoryAndFile LineProgram::get_directory_and_file(size_t file_index) const 298{ 299 VERIFY(file_index < m_source_files.size()); 300 auto file_entry = m_source_files[file_index]; 301 VERIFY(file_entry.directory_index < m_source_directories.size()); 302 auto directory_entry = m_source_directories[file_entry.directory_index]; 303 return { directory_entry, file_entry.name }; 304} 305 306bool LineProgram::looks_like_embedded_resource() const 307{ 308 if (source_files().size() == 1) 309 return source_files()[0].name.view().contains("serenity_icon_"sv); 310 311 if (source_files().size() == 2 && source_files()[0].name.view() == "."sv) 312 return source_files()[1].name.view().contains("serenity_icon_"sv); 313 314 return false; 315} 316 317}