| ... | ... | @@ -376,7 +376,6 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 376 | 376 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) |
| 377 | 377 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, |
| 378 | 378 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. |
| 379 | | |
| 380 | 379 | const subsection_end_index = sect_offset + subsect_hdr.Length; |
| 381 | 380 | |
| 382 | 381 | while (line_index < subsection_end_index) { |
| ... | ... | @@ -692,7 +691,7 @@ pub fn printSourceAtAddressDwarf( |
| 692 | 691 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); |
| 693 | 692 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { |
| 694 | 693 | defer line_info.deinit(); |
| 695 | | const symbol_name = "???"; |
| 694 | const symbol_name = getSymbolNameDwarf(debug_info, address - 1) orelse "???"; |
| 696 | 695 | try printLineInfo( |
| 697 | 696 | out_stream, |
| 698 | 697 | line_info, |
| ... | ... | @@ -969,6 +968,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec |
| 969 | 968 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 970 | 969 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); |
| 971 | 970 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); |
| 971 | di.func_list = ArrayList(Func).init(allocator); |
| 972 | try scanAllFunctions(di); |
| 972 | 973 | try scanAllCompileUnits(di); |
| 973 | 974 | } |
| 974 | 975 | |
| ... | ... | @@ -992,6 +993,7 @@ pub fn openElfDebugInfo( |
| 992 | 993 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), |
| 993 | 994 | .abbrev_table_list = undefined, |
| 994 | 995 | .compile_unit_list = undefined, |
| 996 | .func_list = undefined, |
| 995 | 997 | }; |
| 996 | 998 | try openDwarfDebugInfo(&di, allocator); |
| 997 | 999 | return di; |
| ... | ... | @@ -1162,6 +1164,7 @@ pub const DwarfInfo = struct { |
| 1162 | 1164 | debug_ranges: ?Section, |
| 1163 | 1165 | abbrev_table_list: ArrayList(AbbrevTableHeader), |
| 1164 | 1166 | compile_unit_list: ArrayList(CompileUnit), |
| 1167 | func_list: ArrayList(Func), |
| 1165 | 1168 | |
| 1166 | 1169 | pub const Section = struct { |
| 1167 | 1170 | offset: usize, |
| ... | ... | @@ -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 | 1315 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { |
| 1305 | 1316 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1306 | 1317 | return switch (form_value.*) { |
| ... | ... | @@ -1440,17 +1451,17 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) ! |
| 1440 | 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 | 1455 | return FormValue{ |
| 1445 | 1456 | .Const = Constant{ |
| 1446 | 1457 | .signed = signed, |
| 1447 | 1458 | .payload = switch (size) { |
| 1448 | | 1 => try in_stream.readIntLittle(u8), |
| 1449 | | 2 => try in_stream.readIntLittle(u16), |
| 1450 | | 4 => try in_stream.readIntLittle(u32), |
| 1451 | | 8 => try in_stream.readIntLittle(u64), |
| 1452 | | -1 => if (signed) try readULeb128(in_stream) else @intCast(u64, try readILeb128(in_stream)), |
| 1453 | | else => unreachable, |
| 1459 | 1 => try in_stream.readIntLittle(u8), |
| 1460 | 2 => try in_stream.readIntLittle(u16), |
| 1461 | 4 => try in_stream.readIntLittle(u32), |
| 1462 | 8 => try in_stream.readIntLittle(u64), |
| 1463 | -1 => if (signed) @intCast(u64, try readILeb128(in_stream)) else try readULeb128(in_stream), |
| 1464 | else => @compileError("Invalid size"), |
| 1454 | 1465 | }, |
| 1455 | 1466 | }, |
| 1456 | 1467 | }; |
| ... | ... | @@ -1467,10 +1478,10 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 { |
| 1467 | 1478 | fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue { |
| 1468 | 1479 | return FormValue{ |
| 1469 | 1480 | .Ref = switch (size) { |
| 1470 | | 1 => try in_stream.readIntLittle(u8), |
| 1471 | | 2 => try in_stream.readIntLittle(u16), |
| 1472 | | 4 => try in_stream.readIntLittle(u32), |
| 1473 | | 8 => try in_stream.readIntLittle(u64), |
| 1481 | 1 => try in_stream.readIntLittle(u8), |
| 1482 | 2 => try in_stream.readIntLittle(u16), |
| 1483 | 4 => try in_stream.readIntLittle(u32), |
| 1484 | 8 => try in_stream.readIntLittle(u64), |
| 1474 | 1485 | -1 => try readULeb128(in_stream), |
| 1475 | 1486 | else => unreachable, |
| 1476 | 1487 | }, |
| ... | ... | @@ -1571,6 +1582,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 1571 | 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 | 1605 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { |
| 1575 | 1606 | const abbrev_code = try readULeb128(di.dwarf_in_stream); |
| 1576 | 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 | 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 | 2107 | fn scanAllCompileUnits(di: *DwarfInfo) !void { |
| 1958 | 2108 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| 1959 | 2109 | var this_unit_offset = di.debug_info.offset; |