| ... | ... | @@ -692,7 +692,7 @@ pub fn printSourceAtAddressDwarf( |
| 692 | 692 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); |
| 693 | 693 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { |
| 694 | 694 | defer line_info.deinit(); |
| 695 | | const symbol_name = "???"; |
| 695 | const symbol_name = getSymbolNameDwarf(debug_info, address - 1) orelse "???"[0..2]; |
| 696 | 696 | try printLineInfo( |
| 697 | 697 | out_stream, |
| 698 | 698 | line_info, |
| ... | ... | @@ -969,6 +969,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec |
| 969 | 969 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 970 | 970 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); |
| 971 | 971 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); |
| 972 | di.func_list = ArrayList(Func).init(allocator); |
| 973 | try scanAllFunctions(di); |
| 972 | 974 | try scanAllCompileUnits(di); |
| 973 | 975 | } |
| 974 | 976 | |
| ... | ... | @@ -992,6 +994,7 @@ pub fn openElfDebugInfo( |
| 992 | 994 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), |
| 993 | 995 | .abbrev_table_list = undefined, |
| 994 | 996 | .compile_unit_list = undefined, |
| 997 | .func_list = undefined, |
| 995 | 998 | }; |
| 996 | 999 | try openDwarfDebugInfo(&di, allocator); |
| 997 | 1000 | return di; |
| ... | ... | @@ -1162,6 +1165,7 @@ pub const DwarfInfo = struct { |
| 1162 | 1165 | debug_ranges: ?Section, |
| 1163 | 1166 | abbrev_table_list: ArrayList(AbbrevTableHeader), |
| 1164 | 1167 | compile_unit_list: ArrayList(CompileUnit), |
| 1168 | func_list: ArrayList(Func), |
| 1165 | 1169 | |
| 1166 | 1170 | pub const Section = struct { |
| 1167 | 1171 | offset: usize, |
| ... | ... | @@ -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 | 1316 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { |
| 1305 | 1317 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1306 | 1318 | return switch (form_value.*) { |
| ... | ... | @@ -1572,6 +1584,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 1572 | 1584 | return null; |
| 1573 | 1585 | } |
| 1574 | 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 | |
| 1575 | 1607 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { |
| 1576 | 1608 | const abbrev_code = try readULeb128(di.dwarf_in_stream); |
| 1577 | 1609 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| ... | ... | @@ -1955,6 +1987,131 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr |
| 1955 | 1987 | return error.MissingDebugInfo; |
| 1956 | 1988 | } |
| 1957 | 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 | |
| 1958 | 2115 | fn scanAllCompileUnits(di: *DwarfInfo) !void { |
| 1959 | 2116 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| 1960 | 2117 | var this_unit_offset = di.debug_info.offset; |