| ... | @@ -376,7 +376,6 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres | ... | @@ -376,7 +376,6 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 376 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) | 376 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) |
| 377 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, | 377 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, |
| 378 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. | 378 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. |
| 379 | | | |
| 380 | const subsection_end_index = sect_offset + subsect_hdr.Length; | 379 | const subsection_end_index = sect_offset + subsect_hdr.Length; |
| 381 | | 380 | |
| 382 | while (line_index < subsection_end_index) { | 381 | while (line_index < subsection_end_index) { |
| ... | @@ -692,7 +691,7 @@ pub fn printSourceAtAddressDwarf( | ... | @@ -692,7 +691,7 @@ pub fn printSourceAtAddressDwarf( |
| 692 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | 691 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); |
| 693 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { | 692 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { |
| 694 | defer line_info.deinit(); | 693 | defer line_info.deinit(); |
| 695 | const symbol_name = "???"; | 694 | const symbol_name = getSymbolNameDwarf(debug_info, address - 1) orelse "???"; |
| 696 | try printLineInfo( | 695 | try printLineInfo( |
| 697 | out_stream, | 696 | out_stream, |
| 698 | line_info, | 697 | line_info, |
| ... | @@ -969,6 +968,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec | ... | @@ -969,6 +968,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec |
| 969 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { | 968 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 970 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | 969 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); |
| 971 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | 970 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); |
| | 971 | di.func_list = ArrayList(Func).init(allocator); |
| | 972 | try scanAllFunctions(di); |
| 972 | try scanAllCompileUnits(di); | 973 | try scanAllCompileUnits(di); |
| 973 | } | 974 | } |
| 974 | | 975 | |
| ... | @@ -992,6 +993,7 @@ pub fn openElfDebugInfo( | ... | @@ -992,6 +993,7 @@ pub fn openElfDebugInfo( |
| 992 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), | 993 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), |
| 993 | .abbrev_table_list = undefined, | 994 | .abbrev_table_list = undefined, |
| 994 | .compile_unit_list = undefined, | 995 | .compile_unit_list = undefined, |
| | 996 | .func_list = undefined, |
| 995 | }; | 997 | }; |
| 996 | try openDwarfDebugInfo(&di, allocator); | 998 | try openDwarfDebugInfo(&di, allocator); |
| 997 | return di; | 999 | return di; |
| ... | @@ -1162,6 +1164,7 @@ pub const DwarfInfo = struct { | ... | @@ -1162,6 +1164,7 @@ pub const DwarfInfo = struct { |
| 1162 | debug_ranges: ?Section, | 1164 | debug_ranges: ?Section, |
| 1163 | abbrev_table_list: ArrayList(AbbrevTableHeader), | 1165 | abbrev_table_list: ArrayList(AbbrevTableHeader), |
| 1164 | compile_unit_list: ArrayList(CompileUnit), | 1166 | compile_unit_list: ArrayList(CompileUnit), |
| | 1167 | func_list: ArrayList(Func), |
| 1165 | | 1168 | |
| 1166 | pub const Section = struct { | 1169 | pub const Section = struct { |
| 1167 | offset: usize, | 1170 | offset: usize, |
| ... | @@ -1301,6 +1304,14 @@ const Die = struct { | ... | @@ -1301,6 +1304,14 @@ const Die = struct { |
| 1301 | }; | 1304 | }; |
| 1302 | } | 1305 | } |
| 1303 | | 1306 | |
| | 1307 | fn getAttrRef(self: *const Die, id: u64) !u64 { |
| | 1308 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| | 1309 | return switch (form_value.*) { |
| | 1310 | FormValue.Ref => |value| value, |
| | 1311 | else => error.InvalidDebugInfo, |
| | 1312 | }; |
| | 1313 | } |
| | 1314 | |
| 1304 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { | 1315 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { |
| 1305 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | 1316 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1306 | return switch (form_value.*) { | 1317 | return switch (form_value.*) { |
| ... | @@ -1440,17 +1451,17 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) ! | ... | @@ -1440,17 +1451,17 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) ! |
| 1440 | return parseFormValueBlockLen(allocator, in_stream, block_len); | 1451 | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 1441 | } | 1452 | } |
| 1442 | | 1453 | |
| 1443 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: i32) !FormValue { | 1454 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue { |
| 1444 | return FormValue{ | 1455 | return FormValue{ |
| 1445 | .Const = Constant{ | 1456 | .Const = Constant{ |
| 1446 | .signed = signed, | 1457 | .signed = signed, |
| 1447 | .payload = switch (size) { | 1458 | .payload = switch (size) { |
| 1448 | 1 => try in_stream.readIntLittle(u8), | 1459 | 1 => try in_stream.readIntLittle(u8), |
| 1449 | 2 => try in_stream.readIntLittle(u16), | 1460 | 2 => try in_stream.readIntLittle(u16), |
| 1450 | 4 => try in_stream.readIntLittle(u32), | 1461 | 4 => try in_stream.readIntLittle(u32), |
| 1451 | 8 => try in_stream.readIntLittle(u64), | 1462 | 8 => try in_stream.readIntLittle(u64), |
| 1452 | -1 => if (signed) try readULeb128(in_stream) else @intCast(u64, try readILeb128(in_stream)), | 1463 | -1 => if (signed) @intCast(u64, try readILeb128(in_stream)) else try readULeb128(in_stream), |
| 1453 | else => unreachable, | 1464 | else => @compileError("Invalid size"), |
| 1454 | }, | 1465 | }, |
| 1455 | }, | 1466 | }, |
| 1456 | }; | 1467 | }; |
| ... | @@ -1467,10 +1478,10 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 { | ... | @@ -1467,10 +1478,10 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 { |
| 1467 | fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue { | 1478 | fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue { |
| 1468 | return FormValue{ | 1479 | return FormValue{ |
| 1469 | .Ref = switch (size) { | 1480 | .Ref = switch (size) { |
| 1470 | 1 => try in_stream.readIntLittle(u8), | 1481 | 1 => try in_stream.readIntLittle(u8), |
| 1471 | 2 => try in_stream.readIntLittle(u16), | 1482 | 2 => try in_stream.readIntLittle(u16), |
| 1472 | 4 => try in_stream.readIntLittle(u32), | 1483 | 4 => try in_stream.readIntLittle(u32), |
| 1473 | 8 => try in_stream.readIntLittle(u64), | 1484 | 8 => try in_stream.readIntLittle(u64), |
| 1474 | -1 => try readULeb128(in_stream), | 1485 | -1 => try readULeb128(in_stream), |
| 1475 | else => unreachable, | 1486 | else => unreachable, |
| 1476 | }, | 1487 | }, |
| ... | @@ -1571,6 +1582,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con | ... | @@ -1571,6 +1582,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 1571 | return null; | 1582 | return null; |
| 1572 | } | 1583 | } |
| 1573 | | 1584 | |
| | 1585 | fn parseDie1(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { |
| | 1586 | const abbrev_code = try readULeb128(di.dwarf_in_stream); |
| | 1587 | if (abbrev_code == 0) return null; |
| | 1588 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| | 1589 | |
| | 1590 | var result = Die{ |
| | 1591 | .tag_id = table_entry.tag_id, |
| | 1592 | .has_children = table_entry.has_children, |
| | 1593 | .attrs = ArrayList(Die.Attr).init(di.allocator()), |
| | 1594 | }; |
| | 1595 | try result.attrs.resize(table_entry.attrs.len); |
| | 1596 | for (table_entry.attrs.toSliceConst()) |attr, i| { |
| | 1597 | result.attrs.items[i] = Die.Attr{ |
| | 1598 | .id = attr.attr_id, |
| | 1599 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), |
| | 1600 | }; |
| | 1601 | } |
| | 1602 | return result; |
| | 1603 | } |
| | 1604 | |
| 1574 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | 1605 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { |
| 1575 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | 1606 | const abbrev_code = try readULeb128(di.dwarf_in_stream); |
| 1576 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; | 1607 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| ... | @@ -1954,6 +1985,125 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr | ... | @@ -1954,6 +1985,125 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr |
| 1954 | return error.MissingDebugInfo; | 1985 | return error.MissingDebugInfo; |
| 1955 | } | 1986 | } |
| 1956 | | 1987 | |
| | 1988 | const Func = struct { |
| | 1989 | pc_range: ?PcRange, |
| | 1990 | name: ?[]u8, |
| | 1991 | }; |
| | 1992 | |
| | 1993 | fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]const u8 { |
| | 1994 | for (di.func_list.toSliceConst()) |*func| { |
| | 1995 | if (func.pc_range) |range| { |
| | 1996 | if (address >= range.start and address < range.end) { |
| | 1997 | return func.name; |
| | 1998 | } |
| | 1999 | } |
| | 2000 | } |
| | 2001 | |
| | 2002 | return null; |
| | 2003 | } |
| | 2004 | |
| | 2005 | fn scanAllFunctions(di: *DwarfInfo) !void { |
| | 2006 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| | 2007 | var this_unit_offset = di.debug_info.offset; |
| | 2008 | |
| | 2009 | while (this_unit_offset < debug_info_end) { |
| | 2010 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| | 2011 | |
| | 2012 | var is_64: bool = undefined; |
| | 2013 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| | 2014 | if (unit_length == 0) return; |
| | 2015 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); |
| | 2016 | |
| | 2017 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| | 2018 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| | 2019 | |
| | 2020 | const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); |
| | 2021 | |
| | 2022 | const address_size = try di.dwarf_in_stream.readByte(); |
| | 2023 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| | 2024 | |
| | 2025 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); |
| | 2026 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); |
| | 2027 | |
| | 2028 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); |
| | 2029 | |
| | 2030 | const next_unit_pos = this_unit_offset + next_offset; |
| | 2031 | |
| | 2032 | while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { |
| | 2033 | const die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse continue; |
| | 2034 | const after_die_offset = try di.dwarf_seekable_stream.getPos(); |
| | 2035 | |
| | 2036 | switch (die_obj.tag_id) { |
| | 2037 | DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { |
| | 2038 | const fn_name = x: { |
| | 2039 | var depth: i32 = 3; |
| | 2040 | var this_die_obj = die_obj; |
| | 2041 | // Prenvent endless loops |
| | 2042 | while (depth > 0) : (depth -= 1) { |
| | 2043 | if (this_die_obj.getAttr(DW.AT_name)) |_| { |
| | 2044 | const name = try this_die_obj.getAttrString(di, DW.AT_name); |
| | 2045 | break :x name; |
| | 2046 | } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| { |
| | 2047 | // Follow the DIE it points to and repeat |
| | 2048 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); |
| | 2049 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| | 2050 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| | 2051 | this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| | 2052 | } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { |
| | 2053 | // Follow the DIE it points to and repeat |
| | 2054 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); |
| | 2055 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| | 2056 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| | 2057 | this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| | 2058 | } else { |
| | 2059 | break :x null; |
| | 2060 | } |
| | 2061 | } |
| | 2062 | |
| | 2063 | break :x null; |
| | 2064 | }; |
| | 2065 | |
| | 2066 | const pc_range = x: { |
| | 2067 | if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { |
| | 2068 | if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { |
| | 2069 | const pc_end = switch (high_pc_value.*) { |
| | 2070 | FormValue.Address => |value| value, |
| | 2071 | FormValue.Const => |value| b: { |
| | 2072 | const offset = try value.asUnsignedLe(); |
| | 2073 | break :b (low_pc + offset); |
| | 2074 | }, |
| | 2075 | else => return error.InvalidDebugInfo, |
| | 2076 | }; |
| | 2077 | break :x PcRange{ |
| | 2078 | .start = low_pc, |
| | 2079 | .end = pc_end, |
| | 2080 | }; |
| | 2081 | } else { |
| | 2082 | break :x null; |
| | 2083 | } |
| | 2084 | } else |err| { |
| | 2085 | if (err != error.MissingDebugInfo) return err; |
| | 2086 | break :x null; |
| | 2087 | } |
| | 2088 | }; |
| | 2089 | |
| | 2090 | try di.func_list.append(Func{ |
| | 2091 | .name = fn_name, |
| | 2092 | .pc_range = pc_range, |
| | 2093 | }); |
| | 2094 | }, |
| | 2095 | else => { |
| | 2096 | continue; |
| | 2097 | }, |
| | 2098 | } |
| | 2099 | |
| | 2100 | try di.dwarf_seekable_stream.seekTo(after_die_offset); |
| | 2101 | } |
| | 2102 | |
| | 2103 | this_unit_offset += next_offset; |
| | 2104 | } |
| | 2105 | } |
| | 2106 | |
| 1957 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | 2107 | fn scanAllCompileUnits(di: *DwarfInfo) !void { |
| 1958 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | 2108 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| 1959 | var this_unit_offset = di.debug_info.offset; | 2109 | var this_unit_offset = di.debug_info.offset; |