Commit c2542bb7b7

LemonBoy <thatlemon@gmail.com>
2019-04-20 10:35:39
Recover symbol infos from DWARF sections
1 parent 0286be1
Changed files (2)
std/debug.zig
@@ -692,7 +692,7 @@ pub fn printSourceAtAddressDwarf(
     const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
     if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| {
         defer line_info.deinit();
-        const symbol_name = "???";
+        const symbol_name = getSymbolNameDwarf(debug_info, address - 1) orelse "???"[0..2];
         try printLineInfo(
             out_stream,
             line_info,
@@ -969,6 +969,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec
 pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
     di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
     di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
+    di.func_list = ArrayList(Func).init(allocator);
+    try scanAllFunctions(di);
     try scanAllCompileUnits(di);
 }
 
@@ -992,6 +994,7 @@ pub fn openElfDebugInfo(
         .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")),
         .abbrev_table_list = undefined,
         .compile_unit_list = undefined,
+        .func_list = undefined,
     };
     try openDwarfDebugInfo(&di, allocator);
     return di;
@@ -1162,6 +1165,7 @@ pub const DwarfInfo = struct {
     debug_ranges: ?Section,
     abbrev_table_list: ArrayList(AbbrevTableHeader),
     compile_unit_list: ArrayList(CompileUnit),
+    func_list: ArrayList(Func),
 
     pub const Section = struct {
         offset: usize,
@@ -1301,6 +1305,14 @@ const Die = struct {
         };
     }
 
+    fn getAttrRef(self: *const Die, id: u64) !u64 {
+        const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+        return switch (form_value.*) {
+            FormValue.Ref => |value| value,
+            else => error.InvalidDebugInfo,
+        };
+    }
+
     fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
         return switch (form_value.*) {
@@ -1572,6 +1584,26 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
     return null;
 }
 
+fn parseDie1(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
+    const abbrev_code = try readULeb128(di.dwarf_in_stream);
+    if (abbrev_code == 0) return null;
+    const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
+
+    var result = Die{
+        .tag_id = table_entry.tag_id,
+        .has_children = table_entry.has_children,
+        .attrs = ArrayList(Die.Attr).init(di.allocator()),
+    };
+    try result.attrs.resize(table_entry.attrs.len);
+    for (table_entry.attrs.toSliceConst()) |attr, i| {
+        result.attrs.items[i] = Die.Attr{
+            .id = attr.attr_id,
+            .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64),
+        };
+    }
+    return result;
+}
+
 fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
     const abbrev_code = try readULeb128(di.dwarf_in_stream);
     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
     return error.MissingDebugInfo;
 }
 
+const Func = struct {
+    pc_range: ?PcRange,
+    name: ?[]u8,
+};
+
+fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]u8 {
+    for (di.func_list.toSliceConst()) |*func| {
+        if (func.pc_range) |range| {
+            if (address >= range.start and address < range.end) {
+                return func.name;
+            }
+        }
+    }
+
+    return null;
+}
+
+fn scanAllFunctions(di: *DwarfInfo) !void {
+    const debug_info_end = di.debug_info.offset + di.debug_info.size;
+    var this_unit_offset = di.debug_info.offset;
+
+    while (this_unit_offset < debug_info_end) {
+        try di.dwarf_seekable_stream.seekTo(this_unit_offset);
+
+        var is_64: bool = undefined;
+        const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
+        if (unit_length == 0) return;
+        const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
+
+        const version = try di.dwarf_in_stream.readInt(u16, di.endian);
+        if (version < 2 or version > 5) return error.InvalidDebugInfo;
+
+        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);
+
+        const address_size = try di.dwarf_in_stream.readByte();
+        if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
+
+        const compile_unit_pos = try di.dwarf_seekable_stream.getPos();
+        const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset);
+
+        try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
+
+        const next_unit_pos = this_unit_offset + next_offset;
+
+        while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) {
+            const die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse continue;
+            const after_die_offset = try di.dwarf_seekable_stream.getPos();
+
+            switch (die_obj.tag_id) {
+                DW.TAG_subprogram,
+                DW.TAG_inlined_subroutine,
+                DW.TAG_subroutine,
+                DW.TAG_entry_point => {
+                    const fn_name = x: {
+                        var depth: i32 = 3;
+                        var this_die_obj = die_obj;
+                        // Prenvent endless loops
+                        while (depth > 0) : (depth -= 1) {
+                            if (this_die_obj.getAttr(DW.AT_name)) |_| {
+                                const name = try this_die_obj.getAttrString(di, DW.AT_name);
+                                break :x name;
+                            }
+                            else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| {
+                                // Follow the DIE it points to and repeat
+                                const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
+                                if (ref_offset > next_offset) return error.InvalidDebugInfo;
+                                try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
+                                this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+                            }
+                            else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
+                                // Follow the DIE it points to and repeat
+                                const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
+                                if (ref_offset > next_offset) return error.InvalidDebugInfo;
+                                try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
+                                this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+                            }
+                            else {
+                                break :x null;
+                            }
+                        }
+
+                        break :x null;
+                    };
+
+                    const pc_range = x: {
+                        if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| {
+                            if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| {
+                                const pc_end = switch (high_pc_value.*) {
+                                    FormValue.Address => |value| value,
+                                    FormValue.Const => |value| b: {
+                                        const offset = try value.asUnsignedLe();
+                                        break :b (low_pc + offset);
+                                    },
+                                    else => return error.InvalidDebugInfo,
+                                };
+                                break :x PcRange{
+                                    .start = low_pc,
+                                    .end = pc_end,
+                                };
+                            } else {
+                                break :x null;
+                            }
+                        } else |err| {
+                            if (err != error.MissingDebugInfo) return err;
+                            break :x null;
+                        }
+                    };
+
+                    try di.func_list.append(Func{
+                        .name = fn_name,
+                        .pc_range = pc_range,
+                    });
+                },
+                else => {
+                    continue;
+                }
+            }
+
+            try di.dwarf_seekable_stream.seekTo(after_die_offset);
+        }
+
+        this_unit_offset += next_offset;
+    }
+}
+
 fn scanAllCompileUnits(di: *DwarfInfo) !void {
     const debug_info_end = di.debug_info.offset + di.debug_info.size;
     var this_unit_offset = di.debug_info.offset;
std/dwarf.zig
@@ -13,6 +13,7 @@ pub const TAG_reference_type = 0x10;
 pub const TAG_compile_unit = 0x11;
 pub const TAG_string_type = 0x12;
 pub const TAG_structure_type = 0x13;
+pub const TAG_subroutine = 0x14;
 pub const TAG_subroutine_type = 0x15;
 pub const TAG_typedef = 0x16;
 pub const TAG_union_type = 0x17;