Commit 3a1612a0f5

Andrew Kelley <andrew@ziglang.org>
2018-12-03 05:46:45
std.debug: fix some issues with freestanding debug info
1 parent 5c3b8cb
Changed files (1)
std
debug
std/debug/index.zig
@@ -200,12 +200,10 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,
 
 pub const StackIterator = struct {
     first_addr: ?usize,
-    debug_info: *DebugInfo,
     fp: usize,
 
-    pub fn init(debug_info: *DebugInfo, first_addr: ?usize) StackIterator {
+    pub fn init(first_addr: ?usize) StackIterator {
         return StackIterator{
-            .debug_info = debug_info,
             .first_addr = first_addr,
             .fp = @ptrToInt(@frameAddress()),
         };
@@ -236,7 +234,7 @@ pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color
         builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr),
         else => {},
     }
-    var it = StackIterator.init(debug_info, start_addr);
+    var it = StackIterator.init(start_addr);
     while (it.next()) |return_address| {
         try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
     }
@@ -596,7 +594,6 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt
     if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| {
         defer line_info.deinit();
         try printLineInfo(
-            di,
             out_stream,
             line_info,
             address,
@@ -620,7 +617,7 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt
 /// This function works in freestanding mode.
 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void
 pub fn printSourceAtAddressDwarf(
-    debug_info: *DebugInfo,
+    debug_info: *DwarfInfo,
     out_stream: var,
     address: usize,
     tty_color: bool,
@@ -639,7 +636,6 @@ pub fn printSourceAtAddressDwarf(
         defer line_info.deinit();
         const symbol_name = "???";
         try printLineInfo(
-            debug_info,
             out_stream,
             line_info,
             address,
@@ -665,7 +661,6 @@ pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, addres
 }
 
 fn printLineInfo(
-    debug_info: *DebugInfo,
     out_stream: var,
     line_info: LineInfo,
     address: usize,
@@ -900,36 +895,50 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
     return list.toOwnedSlice();
 }
 
-pub fn openDwarfDebugInfo(
+fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section {
+    const elf_header = (try elf_file.findSection(name)) orelse return null;
+    return DwarfInfo.Section{
+        .offset = elf_header.offset,
+        .size = elf_header.size,
+    };
+}
+
+/// Initialize DWARF info. The caller has the responsibility to initialize most
+/// the DwarfInfo fields before calling. These fields can be left undefined:
+/// * abbrev_table_list
+/// * compile_unit_list
+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);
+    try scanAllCompileUnits(di);
+}
+
+pub fn openElfDebugInfo(
     allocator: *mem.Allocator,
-    dwarf_seekable_stream: *DwarfSeekableStream,
-    dwarf_in_stream: *DwarfInStream,
-) !DebugInfo {
-    var di = DebugInfo{
-        .dwarf_seekable_stream = dwarf_seekable_stream,
-        .dwarf_in_stream = dwarf_in_stream,
-        .elf = undefined,
-        .debug_info = undefined,
-        .debug_abbrev = undefined,
-        .debug_str = undefined,
-        .debug_line = undefined,
-        .debug_ranges = null,
-        .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
-        .compile_unit_list = ArrayList(CompileUnit).init(allocator),
+    elf_seekable_stream: *DwarfSeekableStream,
+    elf_in_stream: *DwarfInStream,
+) !DwarfInfo {
+    var efile: elf.Elf = undefined;
+    try efile.openStream(allocator, elf_seekable_stream, elf_in_stream);
+    errdefer efile.close();
+
+    var di = DwarfInfo{
+        .dwarf_seekable_stream = elf_seekable_stream,
+        .dwarf_in_stream = elf_in_stream,
+        .endian = efile.endian,
+        .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo,
+        .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo,
+        .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo,
+        .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo,
+        .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")),
+        .abbrev_table_list = undefined,
+        .compile_unit_list = undefined,
     };
-    try di.elf.openStream(allocator, dwarf_seekable_stream, dwarf_in_stream);
-    errdefer di.elf.close();
-
-    di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
-    di.debug_abbrev = (try di.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
-    di.debug_str = (try di.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
-    di.debug_line = (try di.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
-    di.debug_ranges = (try di.elf.findSection(".debug_ranges"));
-    try scanAllCompileUnits(&di);
+    try openDwarfDebugInfo(&di, allocator);
     return di;
 }
 
-fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
+fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo {
     const S = struct {
         var self_exe_file: os.File = undefined;
         var self_exe_seekable_stream: os.File.SeekableStream = undefined;
@@ -941,7 +950,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
     S.self_exe_seekable_stream = S.self_exe_file.seekableStream();
     S.self_exe_in_stream = S.self_exe_file.inStream();
 
-    return openDwarfDebugInfo(
+    return openElfDebugInfo(
         allocator,
         // TODO https://github.com/ziglang/zig/issues/764
         @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream),
@@ -1086,26 +1095,27 @@ pub const DwarfInStream = io.InStream(anyerror);
 pub const DwarfInfo = struct {
     dwarf_seekable_stream: *DwarfSeekableStream,
     dwarf_in_stream: *DwarfInStream,
-    elf: elf.Elf,
-    debug_info: *elf.SectionHeader,
-    debug_abbrev: *elf.SectionHeader,
-    debug_str: *elf.SectionHeader,
-    debug_line: *elf.SectionHeader,
-    debug_ranges: ?*elf.SectionHeader,
+    endian: builtin.Endian,
+    debug_info: Section,
+    debug_abbrev: Section,
+    debug_str: Section,
+    debug_line: Section,
+    debug_ranges: ?Section,
     abbrev_table_list: ArrayList(AbbrevTableHeader),
     compile_unit_list: ArrayList(CompileUnit),
 
-    pub fn allocator(self: DebugInfo) *mem.Allocator {
+    pub const Section = struct {
+        offset: usize,
+        size: usize,
+    };
+
+    pub fn allocator(self: DwarfInfo) *mem.Allocator {
         return self.abbrev_table_list.allocator;
     }
 
-    pub fn readString(self: *DebugInfo) ![]u8 {
+    pub fn readString(self: *DwarfInfo) ![]u8 {
         return readStringRaw(self.allocator(), self.dwarf_in_stream);
     }
-
-    pub fn close(self: *DebugInfo) void {
-        self.elf.close();
-    }
 };
 
 pub const DebugInfo = switch (builtin.os) {
@@ -1236,7 +1246,7 @@ const Die = struct {
         };
     }
 
-    fn getAttrString(self: *const Die, di: *DebugInfo, id: u64) ![]u8 {
+    fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
         return switch (form_value.*) {
             FormValue.String => |value| value,
@@ -1253,7 +1263,7 @@ const FileEntry = struct {
     len_bytes: usize,
 };
 
-const LineInfo = struct {
+pub const LineInfo = struct {
     line: usize,
     column: usize,
     file_name: []const u8,
@@ -1352,7 +1362,7 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
     return buf.toSlice();
 }
 
-fn getString(di: *DebugInfo, offset: u64) ![]u8 {
+fn getString(di: *DwarfInfo, offset: u64) ![]u8 {
     const pos = di.debug_str.offset + offset;
     try di.dwarf_seekable_stream.seekTo(pos);
     return di.readString();
@@ -1452,7 +1462,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
     };
 }
 
-fn parseAbbrevTable(di: *DebugInfo) !AbbrevTable {
+fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable {
     var result = AbbrevTable.init(di.allocator());
     while (true) {
         const abbrev_code = try readULeb128(di.dwarf_in_stream);
@@ -1479,7 +1489,7 @@ fn parseAbbrevTable(di: *DebugInfo) !AbbrevTable {
 
 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
 /// seeks in the stream and parses it.
-fn getAbbrevTable(di: *DebugInfo, abbrev_offset: u64) !*const AbbrevTable {
+fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
     for (di.abbrev_table_list.toSlice()) |*header| {
         if (header.offset == abbrev_offset) {
             return &header.table;
@@ -1500,7 +1510,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
     return null;
 }
 
-fn parseDie(di: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
+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;
 
@@ -1717,7 +1727,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
     return error.MissingDebugInfo;
 }
 
-fn getLineNumberInfoDwarf(di: *DebugInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
+fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
     const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
 
     const debug_line_end = di.debug_line.offset + di.debug_line.size;
@@ -1737,11 +1747,11 @@ fn getLineNumberInfoDwarf(di: *DebugInfo, compile_unit: CompileUnit, target_addr
             continue;
         }
 
-        const version = try di.dwarf_in_stream.readInt(di.elf.endian, u16);
+        const version = try di.dwarf_in_stream.readInt(di.endian, u16);
         // TODO support 3 and 5
         if (version != 2 and version != 4) return error.InvalidDebugInfo;
 
-        const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(di.elf.endian, u64) else try di.dwarf_in_stream.readInt(di.elf.endian, u32);
+        const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32);
         const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
 
         const minimum_instruction_length = try di.dwarf_in_stream.readByte();
@@ -1810,7 +1820,7 @@ fn getLineNumberInfoDwarf(di: *DebugInfo, compile_unit: CompileUnit, target_addr
                         return error.MissingDebugInfo;
                     },
                     DW.LNE_set_address => {
-                        const addr = try di.dwarf_in_stream.readInt(di.elf.endian, usize);
+                        const addr = try di.dwarf_in_stream.readInt(di.endian, usize);
                         prog.address = addr;
                     },
                     DW.LNE_define_file => {
@@ -1872,7 +1882,7 @@ fn getLineNumberInfoDwarf(di: *DebugInfo, compile_unit: CompileUnit, target_addr
                         prog.address += inc_addr;
                     },
                     DW.LNS_fixed_advance_pc => {
-                        const arg = try di.dwarf_in_stream.readInt(di.elf.endian, u16);
+                        const arg = try di.dwarf_in_stream.readInt(di.endian, u16);
                         prog.address += arg;
                     },
                     DW.LNS_set_prologue_end => {},
@@ -1891,7 +1901,7 @@ fn getLineNumberInfoDwarf(di: *DebugInfo, compile_unit: CompileUnit, target_addr
     return error.MissingDebugInfo;
 }
 
-fn scanAllCompileUnits(di: *DebugInfo) !void {
+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;
     var cu_index: usize = 0;
@@ -1904,10 +1914,10 @@ fn scanAllCompileUnits(di: *DebugInfo) !void {
         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(di.elf.endian, u16);
+        const version = try di.dwarf_in_stream.readInt(di.endian, u16);
         if (version < 2 or version > 5) return error.InvalidDebugInfo;
 
-        const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(di.elf.endian, u64) else try di.dwarf_in_stream.readInt(di.elf.endian, u32);
+        const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32);
 
         const address_size = try di.dwarf_in_stream.readByte();
         if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
@@ -1958,7 +1968,7 @@ fn scanAllCompileUnits(di: *DebugInfo) !void {
     }
 }
 
-fn findCompileUnit(di: *DebugInfo, target_address: u64) !*const CompileUnit {
+fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
     for (di.compile_unit_list.toSlice()) |*compile_unit| {
         if (compile_unit.pc_range) |range| {
             if (target_address >= range.start and target_address < range.end) return compile_unit;