| ... | @@ -692,7 +692,7 @@ pub fn printSourceAtAddressDwarf( | ... | @@ -692,7 +692,7 @@ pub fn printSourceAtAddressDwarf( |
| 692 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | 692 | 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| { | 693 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { |
| 694 | defer line_info.deinit(); | 694 | defer line_info.deinit(); |
| 695 | const symbol_name = "???"; | 695 | const symbol_name = getSymbolNameDwarf(debug_info, address - 1) orelse "???"[0..2]; |
| 696 | try printLineInfo( | 696 | try printLineInfo( |
| 697 | out_stream, | 697 | out_stream, |
| 698 | line_info, | 698 | line_info, |
| ... | @@ -969,6 +969,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec | ... | @@ -969,6 +969,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec |
| 969 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { | 969 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 970 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | 970 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); |
| 971 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | 971 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); |
| | 972 | di.func_list = ArrayList(Func).init(allocator); |
| | 973 | try scanAllFunctions(di); |
| 972 | try scanAllCompileUnits(di); | 974 | try scanAllCompileUnits(di); |
| 973 | } | 975 | } |
| 974 | | 976 | |
| ... | @@ -992,6 +994,7 @@ pub fn openElfDebugInfo( | ... | @@ -992,6 +994,7 @@ pub fn openElfDebugInfo( |
| 992 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), | 994 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), |
| 993 | .abbrev_table_list = undefined, | 995 | .abbrev_table_list = undefined, |
| 994 | .compile_unit_list = undefined, | 996 | .compile_unit_list = undefined, |
| | 997 | .func_list = undefined, |
| 995 | }; | 998 | }; |
| 996 | try openDwarfDebugInfo(&di, allocator); | 999 | try openDwarfDebugInfo(&di, allocator); |
| 997 | return di; | 1000 | return di; |
| ... | @@ -1162,6 +1165,7 @@ pub const DwarfInfo = struct { | ... | @@ -1162,6 +1165,7 @@ pub const DwarfInfo = struct { |
| 1162 | debug_ranges: ?Section, | 1165 | debug_ranges: ?Section, |
| 1163 | abbrev_table_list: ArrayList(AbbrevTableHeader), | 1166 | abbrev_table_list: ArrayList(AbbrevTableHeader), |
| 1164 | compile_unit_list: ArrayList(CompileUnit), | 1167 | compile_unit_list: ArrayList(CompileUnit), |
| | 1168 | func_list: ArrayList(Func), |
| 1165 | | 1169 | |
| 1166 | pub const Section = struct { | 1170 | pub const Section = struct { |
| 1167 | offset: usize, | 1171 | offset: usize, |
| ... | @@ -1301,6 +1305,14 @@ const Die = struct { | ... | @@ -1301,6 +1305,14 @@ const Die = struct { |
| 1301 | }; | 1305 | }; |
| 1302 | } | 1306 | } |
| 1303 | | 1307 | |
| | 1308 | fn getAttrRef(self: *const Die, id: u64) !u64 { |
| | 1309 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| | 1310 | return switch (form_value.*) { |
| | 1311 | FormValue.Ref => |value| value, |
| | 1312 | else => error.InvalidDebugInfo, |
| | 1313 | }; |
| | 1314 | } |
| | 1315 | |
| 1304 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { | 1316 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { |
| 1305 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | 1317 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1306 | return switch (form_value.*) { | 1318 | return switch (form_value.*) { |
| ... | @@ -1440,7 +1452,7 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) ! | ... | @@ -1440,7 +1452,7 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) ! |
| 1440 | return parseFormValueBlockLen(allocator, in_stream, block_len); | 1452 | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 1441 | } | 1453 | } |
| 1442 | | 1454 | |
| 1443 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: i32) !FormValue { | 1455 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue { |
| 1444 | return FormValue{ | 1456 | return FormValue{ |
| 1445 | .Const = Constant{ | 1457 | .Const = Constant{ |
| 1446 | .signed = signed, | 1458 | .signed = signed, |
| ... | @@ -1449,8 +1461,9 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo | ... | @@ -1449,8 +1461,9 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo |
| 1449 | 2 => try in_stream.readIntLittle(u16), | 1461 | 2 => try in_stream.readIntLittle(u16), |
| 1450 | 4 => try in_stream.readIntLittle(u32), | 1462 | 4 => try in_stream.readIntLittle(u32), |
| 1451 | 8 => try in_stream.readIntLittle(u64), | 1463 | 8 => try in_stream.readIntLittle(u64), |
| 1452 | -1 => if (signed) try readULeb128(in_stream) else @intCast(u64, try readILeb128(in_stream)), | 1464 | -1 => if (signed) @intCast(u64, try readILeb128(in_stream)) |
| 1453 | else => unreachable, | 1465 | else try readULeb128(in_stream), |
| | 1466 | else => @compileError("Invalid size"), |
| 1454 | }, | 1467 | }, |
| 1455 | }, | 1468 | }, |
| 1456 | }; | 1469 | }; |
| ... | @@ -1571,6 +1584,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con | ... | @@ -1571,6 +1584,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 1571 | return null; | 1584 | return null; |
| 1572 | } | 1585 | } |
| 1573 | | 1586 | |
| | 1587 | fn parseDie1(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { |
| | 1588 | const abbrev_code = try readULeb128(di.dwarf_in_stream); |
| | 1589 | if (abbrev_code == 0) return null; |
| | 1590 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| | 1591 | |
| | 1592 | var result = Die{ |
| | 1593 | .tag_id = table_entry.tag_id, |
| | 1594 | .has_children = table_entry.has_children, |
| | 1595 | .attrs = ArrayList(Die.Attr).init(di.allocator()), |
| | 1596 | }; |
| | 1597 | try result.attrs.resize(table_entry.attrs.len); |
| | 1598 | for (table_entry.attrs.toSliceConst()) |attr, i| { |
| | 1599 | result.attrs.items[i] = Die.Attr{ |
| | 1600 | .id = attr.attr_id, |
| | 1601 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), |
| | 1602 | }; |
| | 1603 | } |
| | 1604 | return result; |
| | 1605 | } |
| | 1606 | |
| 1574 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | 1607 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { |
| 1575 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | 1608 | const abbrev_code = try readULeb128(di.dwarf_in_stream); |
| 1576 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; | 1609 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| ... | @@ -1954,6 +1987,131 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr | ... | @@ -1954,6 +1987,131 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr |
| 1954 | return error.MissingDebugInfo; | 1987 | return error.MissingDebugInfo; |
| 1955 | } | 1988 | } |
| 1956 | | 1989 | |
| | 1990 | const Func = struct { |
| | 1991 | pc_range: ?PcRange, |
| | 1992 | name: ?[]u8, |
| | 1993 | }; |
| | 1994 | |
| | 1995 | fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]u8 { |
| | 1996 | for (di.func_list.toSliceConst()) |*func| { |
| | 1997 | if (func.pc_range) |range| { |
| | 1998 | if (address >= range.start and address < range.end) { |
| | 1999 | return func.name; |
| | 2000 | } |
| | 2001 | } |
| | 2002 | } |
| | 2003 | |
| | 2004 | return null; |
| | 2005 | } |
| | 2006 | |
| | 2007 | fn scanAllFunctions(di: *DwarfInfo) !void { |
| | 2008 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| | 2009 | var this_unit_offset = di.debug_info.offset; |
| | 2010 | |
| | 2011 | while (this_unit_offset < debug_info_end) { |
| | 2012 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| | 2013 | |
| | 2014 | var is_64: bool = undefined; |
| | 2015 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| | 2016 | if (unit_length == 0) return; |
| | 2017 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); |
| | 2018 | |
| | 2019 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| | 2020 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| | 2021 | |
| | 2022 | 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); |
| | 2023 | |
| | 2024 | const address_size = try di.dwarf_in_stream.readByte(); |
| | 2025 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| | 2026 | |
| | 2027 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); |
| | 2028 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); |
| | 2029 | |
| | 2030 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); |
| | 2031 | |
| | 2032 | const next_unit_pos = this_unit_offset + next_offset; |
| | 2033 | |
| | 2034 | while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { |
| | 2035 | const die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse continue; |
| | 2036 | const after_die_offset = try di.dwarf_seekable_stream.getPos(); |
| | 2037 | |
| | 2038 | switch (die_obj.tag_id) { |
| | 2039 | DW.TAG_subprogram, |
| | 2040 | DW.TAG_inlined_subroutine, |
| | 2041 | DW.TAG_subroutine, |
| | 2042 | DW.TAG_entry_point => { |
| | 2043 | const fn_name = x: { |
| | 2044 | var depth: i32 = 3; |
| | 2045 | var this_die_obj = die_obj; |
| | 2046 | // Prenvent endless loops |
| | 2047 | while (depth > 0) : (depth -= 1) { |
| | 2048 | if (this_die_obj.getAttr(DW.AT_name)) |_| { |
| | 2049 | const name = try this_die_obj.getAttrString(di, DW.AT_name); |
| | 2050 | break :x name; |
| | 2051 | } |
| | 2052 | else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |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 | } |
| | 2059 | else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { |
| | 2060 | // Follow the DIE it points to and repeat |
| | 2061 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); |
| | 2062 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| | 2063 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| | 2064 | this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| | 2065 | } |
| | 2066 | else { |
| | 2067 | break :x null; |
| | 2068 | } |
| | 2069 | } |
| | 2070 | |
| | 2071 | break :x null; |
| | 2072 | }; |
| | 2073 | |
| | 2074 | const pc_range = x: { |
| | 2075 | if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { |
| | 2076 | if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { |
| | 2077 | const pc_end = switch (high_pc_value.*) { |
| | 2078 | FormValue.Address => |value| value, |
| | 2079 | FormValue.Const => |value| b: { |
| | 2080 | const offset = try value.asUnsignedLe(); |
| | 2081 | break :b (low_pc + offset); |
| | 2082 | }, |
| | 2083 | else => return error.InvalidDebugInfo, |
| | 2084 | }; |
| | 2085 | break :x PcRange{ |
| | 2086 | .start = low_pc, |
| | 2087 | .end = pc_end, |
| | 2088 | }; |
| | 2089 | } else { |
| | 2090 | break :x null; |
| | 2091 | } |
| | 2092 | } else |err| { |
| | 2093 | if (err != error.MissingDebugInfo) return err; |
| | 2094 | break :x null; |
| | 2095 | } |
| | 2096 | }; |
| | 2097 | |
| | 2098 | try di.func_list.append(Func{ |
| | 2099 | .name = fn_name, |
| | 2100 | .pc_range = pc_range, |
| | 2101 | }); |
| | 2102 | }, |
| | 2103 | else => { |
| | 2104 | continue; |
| | 2105 | } |
| | 2106 | } |
| | 2107 | |
| | 2108 | try di.dwarf_seekable_stream.seekTo(after_die_offset); |
| | 2109 | } |
| | 2110 | |
| | 2111 | this_unit_offset += next_offset; |
| | 2112 | } |
| | 2113 | } |
| | 2114 | |
| 1957 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | 2115 | fn scanAllCompileUnits(di: *DwarfInfo) !void { |
| 1958 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | 2116 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| 1959 | var this_unit_offset = di.debug_info.offset; | 2117 | var this_unit_offset = di.debug_info.offset; |