diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6324c88d8641..e74e48f794fb 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -976,26 +976,6 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { return list.toOwnedSlice(); } -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); - di.func_list = ArrayList(Func).init(allocator); - try di.scanAllFunctions(); - try di.scanAllCompileUnits(); -} - pub fn openElfDebugInfo( allocator: *mem.Allocator, elf_seekable_stream: *DwarfSeekableStream, @@ -1004,21 +984,65 @@ pub fn openElfDebugInfo( var efile = try elf.Elf.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, - .func_list = undefined, - }; - try openDwarfDebugInfo(&di, allocator); - return di; + var debug_info: ?DwarfInfo.Section = null; + var debug_abbrev: ?DwarfInfo.Section = null; + var debug_str: ?DwarfInfo.Section = null; + var debug_line: ?DwarfInfo.Section = null; + var debug_ranges: ?DwarfInfo.Section = null; + var n: usize = 0; // count number of sections found to exit loop early + + var it = efile.sectionIterator(); + while (it.next()) |elf_section| { + if (elf_section.sh_type == elf.SHT_NULL) continue; + + if (debug_info == null and try elf_section.nameIs(&efile, ".debug_info")) { + debug_info = DwarfInfo.Section{ + .offset = elf_section.offset, + .size = elf_section.size, + }; + } else if (debug_abbrev == null and try elf_section.nameIs(&efile, ".debug_abbrev")) { + debug_abbrev = DwarfInfo.Section{ + .offset = elf_section.offset, + .size = elf_section.size, + }; + } else if (debug_str == null and try elf_section.nameIs(&efile, ".debug_str")) { + debug_str = DwarfInfo.Section{ + .offset = elf_section.offset, + .size = elf_section.size, + }; + } else if (debug_line == null and try elf_section.nameIs(&efile, ".debug_line")) { + debug_line = DwarfInfo.Section{ + .offset = elf_section.offset, + .size = elf_section.size, + }; + } else if (debug_ranges == null and try elf_section.nameIs(&efile, ".debug_ranges")) { + debug_ranges = DwarfInfo.Section{ + .offset = elf_section.offset, + .size = elf_section.size, + }; + } else { + continue; + } + + n += 1; + if (n == 5) break; + } + + if (n >= 4 and debug_info != null and debug_abbrev != null and debug_str != null and debug_line != null) { + return DwarfInfo{ + .allocator = allocator, + .dwarf_seekable_stream = elf_seekable_stream, + .dwarf_in_stream = elf_in_stream, + .endian = efile.endian, + .debug_info = debug_info.?, + .debug_abbrev = debug_abbrev.?, + .debug_str = debug_str.?, + .debug_line = debug_line.?, + .debug_ranges = debug_ranges, + }; + } + + return error.MissingDebugInfo; } fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo { @@ -1059,16 +1083,18 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { const hdr_base = @ptrCast([*]u8, hdr); var ptr = hdr_base + @sizeOf(macho.mach_header_64); - var ncmd: u32 = hdr.ncmds; - const symtab = while (ncmd != 0) : (ncmd -= 1) { - const lc = @ptrCast(*std.macho.load_command, ptr); - switch (lc.cmd) { - std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), - else => {}, + const symtab = blk: { + var ncmd = hdr.ncmds; + while (ncmd != 0) : (ncmd -= 1) { + const lc = @ptrCast(*std.macho.load_command, ptr); + switch (lc.cmd) { + std.macho.LC_SYMTAB => break :blk @ptrCast(*std.macho.symtab_command, ptr), + else => {}, + } + ptr += lc.cmdsize; + } else { + return error.MissingDebugInfo; } - ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 - } else { - return error.MissingDebugInfo; }; const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; @@ -1187,6 +1213,7 @@ pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); pub const DwarfInStream = io.InStream(anyerror); pub const DwarfInfo = struct { + allocator: *mem.Allocator, dwarf_seekable_stream: *DwarfSeekableStream, dwarf_in_stream: *DwarfInStream, endian: builtin.Endian, @@ -1195,21 +1222,14 @@ pub const DwarfInfo = struct { debug_str: Section, debug_line: Section, debug_ranges: ?Section, - abbrev_table_list: ArrayList(AbbrevTableHeader), - compile_unit_list: ArrayList(CompileUnit), - func_list: ArrayList(Func), pub const Section = struct { offset: u64, size: u64, }; - pub fn allocator(self: DwarfInfo) *mem.Allocator { - return self.abbrev_table_list.allocator; - } - pub fn readString(self: *DwarfInfo) ![]u8 { - return readStringRaw(self.allocator(), self.dwarf_in_stream); + return readStringRaw(self.allocator, self.dwarf_in_stream); } /// This function works in freestanding mode. @@ -1221,7 +1241,7 @@ pub const DwarfInfo = struct { tty_color: bool, comptime printLineFromFile: var, ) !void { - const compile_unit = self.findCompileUnit(address) catch { + const compile_unit_die = self.findDie(address) catch { if (tty_color) { try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); } else { @@ -1229,15 +1249,23 @@ pub const DwarfInfo = struct { } return; }; - const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); - if (self.getLineNumberInfo(compile_unit.*, address)) |line_info| { + defer compile_unit_die.deinit(); + const allocator = getDebugInfoAllocator(); + const compile_unit_name = try compile_unit_die.getAttrString(self, allocator, DW.AT_name); + defer allocator.free(compile_unit_name); + if (self.getLineNumberInfo(compile_unit_die, address)) |line_info| { defer line_info.deinit(); - const symbol_name = self.getSymbolName(address) orelse "???"; + const maybe_symbol_name = self.getSymbolName(allocator, address) catch null; + defer { + if (maybe_symbol_name) |symbol_name| { + allocator.free(symbol_name); + } + } try printLineInfo( out_stream, line_info, address, - symbol_name, + maybe_symbol_name orelse "???", compile_unit_name, tty_color, printLineFromFile, @@ -1254,28 +1282,17 @@ pub const DwarfInfo = struct { } } - fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const 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 { + fn getSymbolName(di: *DwarfInfo, allocator: *mem.Allocator, address: u64) !?[]const u8 { const debug_info_end = di.debug_info.offset + di.debug_info.size; var this_unit_offset = di.debug_info.offset; + var next_unit_pos: usize = undefined; - while (this_unit_offset < debug_info_end) { + while (this_unit_offset < debug_info_end) : (this_unit_offset = next_unit_pos) { 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; + if (unit_length == 0) break; const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); const version = try di.dwarf_in_stream.readInt(u16, di.endian); @@ -1286,75 +1303,50 @@ pub const DwarfInfo = struct { 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 di.getAbbrevTable(debug_abbrev_offset); - - try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - - const next_unit_pos = this_unit_offset + next_offset; + next_unit_pos = this_unit_offset + next_offset; while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { - const die_obj = (try di.parseDie(abbrev_table, is_64)) orelse continue; + var die_obj = (try di.parseDie(debug_abbrev_offset, is_64)) orelse continue; + defer die_obj.deinit(); 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 pc_range = try die_obj.getPcRange(); + const fn_name = x: { var depth: i32 = 3; - var this_die_obj = die_obj; - // Prenvent endless loops + // Prevent 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 di.parseDie(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_specification); - if (ref_offset > next_offset) return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + var ref_offset: usize = undefined; + if (die_obj.getAttr(DW.AT_name)) |_| { + break :x try die_obj.getAttrString(di, allocator, DW.AT_name); + } else if (die_obj.getAttr(DW.AT_abstract_origin)) |_| { + ref_offset = try die_obj.getAttrRef(DW.AT_abstract_origin); + } else if (die_obj.getAttr(DW.AT_specification)) |_| { + ref_offset = try die_obj.getAttrRef(DW.AT_specification); } else { break :x null; } + // Follow the DIE it points to and repeat + if (ref_offset > next_offset) return error.InvalidDebugInfo; + try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); + if (try di.parseDie(debug_abbrev_offset, is_64)) |new_die| { + die_obj.deinit(); + die_obj = new_die; + } else { + return error.InvalidDebugInfo; + } } 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; + if (pc_range) |range| { + if (address >= range.start and address < range.end) { + return fn_name; } - }; - - try di.func_list.append(Func{ - .name = fn_name, - .pc_range = pc_range, - }); + } }, else => { continue; @@ -1363,22 +1355,56 @@ pub const DwarfInfo = struct { try di.dwarf_seekable_stream.seekTo(after_die_offset); } + } - this_unit_offset += next_offset; + return null; + } + + 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; + } + if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { + var base_address: usize = 0; + if (di.debug_ranges) |debug_ranges| { + try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); + while (true) { + const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); + const end_addr = try di.dwarf_in_stream.readIntLittle(usize); + if (begin_addr == 0 and end_addr == 0) { + break; + } + if (begin_addr == maxInt(usize)) { + base_address = begin_addr; + continue; + } + if (target_address >= begin_addr and target_address < end_addr) { + return compile_unit; + } + } + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + continue; + } } + return error.MissingDebugInfo; } - fn scanAllCompileUnits(di: *DwarfInfo) !void { + fn findDie(di: *DwarfInfo, target_address: u64) !Die { const debug_info_end = di.debug_info.offset + di.debug_info.size; var this_unit_offset = di.debug_info.offset; + var next_unit_pos: usize = undefined; - while (this_unit_offset < debug_info_end) { + while (this_unit_offset < debug_info_end) : (this_unit_offset = next_unit_pos) { 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; + if (unit_length == 0) break; const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + next_unit_pos = this_unit_offset + next_offset; const version = try di.dwarf_in_stream.readInt(u16, di.endian); if (version < 2 or version > 5) return error.InvalidDebugInfo; @@ -1388,57 +1414,18 @@ pub const DwarfInfo = struct { 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 di.getAbbrevTable(debug_abbrev_offset); - - try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - - const compile_unit_die = try di.allocator().create(Die); - compile_unit_die.* = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + const compile_unit_die = (try di.parseDie(debug_abbrev_offset, is_64)) orelse return error.InvalidDebugInfo; + errdefer compile_unit_die.deinit(); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; - const pc_range = x: { - if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { - if (compile_unit_die.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; + const pc_range = try compile_unit_die.getPcRange(); + if (pc_range) |range| { + if (target_address >= range.start and target_address < range.end) { + return compile_unit_die; } - }; - - try di.compile_unit_list.append(CompileUnit{ - .version = version, - .is_64 = is_64, - .pc_range = pc_range, - .die = compile_unit_die, - }); - - this_unit_offset += next_offset; - } - } - - 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; } - if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { + if (compile_unit_die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { var base_address: usize = 0; if (di.debug_ranges) |debug_ranges| { try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); @@ -1453,82 +1440,75 @@ pub const DwarfInfo = struct { continue; } if (target_address >= begin_addr and target_address < end_addr) { - return compile_unit; + return compile_unit_die; } } } } else |err| { if (err != error.MissingDebugInfo) return err; - continue; } + + compile_unit_die.deinit(); } return error.MissingDebugInfo; } - /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, - /// seeks in the stream and parses it. - fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { - for (di.abbrev_table_list.toSlice()) |*header| { - if (header.offset == abbrev_offset) { - return &header.table; - } - } - try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); - try di.abbrev_table_list.append(AbbrevTableHeader{ - .offset = abbrev_offset, - .table = try di.parseAbbrevTable(), - }); - return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; - } + fn parseDie(di: *DwarfInfo, abbrev_table_offset: usize, is_64: bool) !?Die { + const want_abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); + if (want_abbrev_code == 0) return null; + + // seek to the relevant abbrev table + var die_seek_offset = try di.dwarf_seekable_stream.getPos(); + try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_table_offset); - fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { - var result = AbbrevTable.init(di.allocator()); while (true) { const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); - if (abbrev_code == 0) return result; - try result.append(AbbrevTableEntry{ - .abbrev_code = abbrev_code, - .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), - .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, - .attrs = ArrayList(AbbrevAttr).init(di.allocator()), - }); - const attrs = &result.items[result.len - 1].attrs; - - while (true) { - const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); - const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); - if (attr_id == 0 and form_id == 0) break; - try attrs.append(AbbrevAttr{ - .attr_id = attr_id, - .form_id = form_id, - }); + if (abbrev_code == 0) break; + + if (abbrev_code == want_abbrev_code) { + const tag_id = try leb.readULEB128(u64, di.dwarf_in_stream); + const has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes; + var attrs = ArrayList(Die.Attr).init(di.allocator); + + while (true) { + const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); + const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); + if (attr_id == 0 and form_id == 0) break; + + // juggle file offsets while we call parseFormValue + const abbrev_seek_offset = try di.dwarf_seekable_stream.getPos(); + try di.dwarf_seekable_stream.seekTo(die_seek_offset); + const value = try parseFormValue(di.allocator, di.dwarf_in_stream, form_id, is_64); + die_seek_offset = try di.dwarf_seekable_stream.getPos(); + try di.dwarf_seekable_stream.seekTo(abbrev_seek_offset); + try attrs.append(Die.Attr{ + .id = attr_id, + .value = value, + }); + } + try di.dwarf_seekable_stream.seekTo(die_seek_offset); + return Die{ + .tag_id = tag_id, + .has_children = has_children, + .attrs = attrs, + }; + } else { // skip to next item + _ = try leb.readULEB128(u64, di.dwarf_in_stream); + _ = try di.dwarf_in_stream.readByte(); + while (true) { + const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); + const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); + if (attr_id == 0 and form_id == 0) break; + } } } + return error.InvalidDebugInfo; } - fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { - const abbrev_code = try leb.readULEB128(u64, 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 getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { - const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); - const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); + fn getLineNumberInfo(di: *DwarfInfo, compile_unit_die: Die, target_address: usize) !LineInfo { + const compile_unit_cwd = try compile_unit_die.getAttrString(di, di.allocator, DW.AT_comp_dir); + defer di.allocator.free(compile_unit_cwd); + const line_info_offset = try compile_unit_die.getAttrSecOffset(DW.AT_stmt_list); assert(line_info_offset < di.debug_line.size); @@ -1564,33 +1544,27 @@ pub const DwarfInfo = struct { const opcode_base = try di.dwarf_in_stream.readByte(); - const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); + var standard_opcode_lengths_storage: [256]u8 = undefined; + const standard_opcode_lengths = standard_opcode_lengths_storage[0..opcode_base]; + try di.dwarf_in_stream.readNoEof(standard_opcode_lengths); - { - var i: usize = 0; - while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); - } - } + var prog = LineNumberProgram.init(di.allocator, default_is_stmt, target_address); + defer prog.deinit(); - var include_directories = ArrayList([]u8).init(di.allocator()); - try include_directories.append(compile_unit_cwd); + try prog.include_dirs.append(compile_unit_cwd); while (true) { const dir = try di.readString(); if (dir.len == 0) break; - try include_directories.append(dir); + try prog.include_dirs.append(dir); } - var file_entries = ArrayList(FileEntry).init(di.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); - while (true) { const file_name = try di.readString(); if (file_name.len == 0) break; const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); - try file_entries.append(FileEntry{ + try prog.file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -1622,7 +1596,7 @@ pub const DwarfInfo = struct { const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); - try file_entries.append(FileEntry{ + try prog.file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -1692,10 +1666,10 @@ pub const DwarfInfo = struct { return error.MissingDebugInfo; } - fn getString(di: *DwarfInfo, offset: u64) ![]u8 { + fn getString(di: *DwarfInfo, allocator: *mem.Allocator, offset: u64) ![]u8 { const pos = di.debug_str.offset + offset; try di.dwarf_seekable_stream.seekTo(pos); - return di.readString(); + return readStringRaw(allocator, di.dwarf_in_stream); } }; @@ -1731,33 +1705,6 @@ const PcRange = struct { end: u64, }; -const CompileUnit = struct { - version: u16, - is_64: bool, - die: *Die, - pc_range: ?PcRange, -}; - -const AbbrevTable = ArrayList(AbbrevTableEntry); - -const AbbrevTableHeader = struct { - // offset from .debug_abbrev - offset: u64, - table: AbbrevTable, -}; - -const AbbrevTableEntry = struct { - has_children: bool, - abbrev_code: u64, - tag_id: u64, - attrs: ArrayList(AbbrevAttr), -}; - -const AbbrevAttr = struct { - attr_id: u64, - form_id: u64, -}; - const FormValue = union(enum) { Address: u64, Block: []u8, @@ -1791,6 +1738,10 @@ const Die = struct { value: FormValue, }; + fn deinit(self: *const Die) void { + self.attrs.deinit(); + } + fn getAttr(self: *const Die, id: u64) ?*const FormValue { for (self.attrs.toSliceConst()) |*attr| { if (attr.id == id) return &attr.value; @@ -1831,14 +1782,38 @@ const Die = struct { }; } - fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { + fn getAttrString(self: *const Die, di: *DwarfInfo, allocator: *mem.Allocator, id: u64) ![]u8 { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, - FormValue.StrPtr => |offset| di.getString(offset), + FormValue.StrPtr => |offset| di.getString(allocator, offset), else => error.InvalidDebugInfo, }; } + + fn getPcRange(self: Die) !?PcRange { + if (self.getAttrAddr(DW.AT_low_pc)) |low_pc| { + if (self.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, + }; + return PcRange{ + .start = low_pc, + .end = pc_end, + }; + } else { + return null; + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + return null; + } + } }; const FileEntry = struct { @@ -1870,8 +1845,8 @@ const LineNumberProgram = struct { end_sequence: bool, target_address: usize, - include_dirs: []const []const u8, - file_entries: *ArrayList(FileEntry), + include_dirs: ArrayList([]const u8), + file_entries: ArrayList(FileEntry), prev_address: usize, prev_file: usize, @@ -1881,7 +1856,7 @@ const LineNumberProgram = struct { prev_basic_block: bool, prev_end_sequence: bool, - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { + pub fn init(allocator: *mem.Allocator, is_stmt: bool, target_address: usize) LineNumberProgram { return LineNumberProgram{ .address = 0, .file = 1, @@ -1890,8 +1865,8 @@ const LineNumberProgram = struct { .is_stmt = is_stmt, .basic_block = false, .end_sequence = false, - .include_dirs = include_dirs, - .file_entries = file_entries, + .include_dirs = ArrayList([]const u8).init(allocator), + .file_entries = ArrayList(FileEntry).init(allocator), .target_address = target_address, .prev_address = 0, .prev_file = undefined, @@ -1903,6 +1878,11 @@ const LineNumberProgram = struct { }; } + pub fn deinit(self: *LineNumberProgram) void { + self.include_dirs.deinit(); + self.file_entries.deinit(); + } + pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { if (self.target_address >= self.prev_address and self.target_address < self.address) { const file_entry = if (self.prev_file == 0) { @@ -1915,7 +1895,7 @@ const LineNumberProgram = struct { const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { return error.InvalidDebugInfo; } else - self.include_dirs[file_entry.dir_index]; + self.include_dirs.at(file_entry.dir_index); const file_name = try fs.path.join(self.file_entries.allocator, [_][]const u8{ dir_name, file_entry.file_name }); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ @@ -1940,6 +1920,7 @@ const LineNumberProgram = struct { // TODO the noasyncs here are workarounds fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { var buf = ArrayList(u8).init(allocator); + errdefer buf.deinit(); while (true) { const byte = try noasync in_stream.readByte(); if (byte == 0) break; @@ -2074,13 +2055,6 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 }; } -fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { - for (abbrev_table.toSliceConst()) |*table_entry| { - if (table_entry.abbrev_code == abbrev_code) return table_entry; - } - return null; -} - fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { const ofile = symbol.ofile orelse return error.MissingDebugInfo; const gop = try di.ofiles.getOrPut(ofile); @@ -2103,16 +2077,18 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u const hdr_base = @ptrCast([*]const u8, hdr); var ptr = hdr_base + @sizeOf(macho.mach_header_64); - var ncmd: u32 = hdr.ncmds; - const segcmd = while (ncmd != 0) : (ncmd -= 1) { - const lc = @ptrCast(*const std.macho.load_command, ptr); - switch (lc.cmd) { - std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), - else => {}, + const segcmd = blk: { + var ncmd = hdr.ncmds; + while (ncmd != 0) : (ncmd -= 1) { + const lc = @ptrCast(*const std.macho.load_command, ptr); + switch (lc.cmd) { + std.macho.LC_SEGMENT_64 => break :blk @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), + else => {}, + } + ptr += lc.cmdsize; + } else { + return error.MissingDebugInfo; } - ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 - } else { - return error.MissingDebugInfo; }; const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; for (sections) |*sect| { @@ -2167,24 +2143,23 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; ptr += opcode_base - 1; - var include_directories = ArrayList([]const u8).init(di.allocator()); - try include_directories.append(""); + var prog = LineNumberProgram.init(di.allocator(), default_is_stmt, target_address); + defer prog.deinit(); + + try prog.include_dirs.append(""); while (true) { const dir = readStringMem(&ptr); if (dir.len == 0) break; - try include_directories.append(dir); + try prog.include_dirs.append(dir); } - var file_entries = ArrayList(FileEntry).init(di.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); - while (true) { const file_name = readStringMem(&ptr); if (file_name.len == 0) break; const dir_index = try leb.readULEB128Mem(usize, &ptr); const mtime = try leb.readULEB128Mem(usize, &ptr); const len_bytes = try leb.readULEB128Mem(usize, &ptr); - try file_entries.append(FileEntry{ + try prog.file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -2215,7 +2190,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u const dir_index = try leb.readULEB128Mem(usize, &ptr); const mtime = try leb.readULEB128Mem(usize, &ptr); const len_bytes = try leb.readULEB128Mem(usize, &ptr); - try file_entries.append(FileEntry{ + try prog.file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -2284,11 +2259,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u return error.MissingDebugInfo; } -const Func = struct { - pc_range: ?PcRange, - name: ?[]u8, -}; - fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { // TODO https://github.com/ziglang/zig/issues/863 const size = (T.bit_count + 7) / 8; diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 3bb4054bfe41..df9ddad83e76 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -352,6 +352,21 @@ pub const SectionHeader = struct { info: u32, addr_align: u64, ent_size: u64, + + pub fn nameIs(elf_section: SectionHeader, elf: *Elf, name: []const u8) !bool { + const name_offset = elf.string_section.offset + elf_section.name; + try elf.seekable_stream.seekTo(name_offset); + + for (name) |expected_c| { + const target_c = try elf.in_stream.readByte(); + if (target_c == 0 or expected_c != target_c) return false; + } + + const null_byte = try elf.in_stream.readByte(); + if (null_byte == 0) return true; + + return false; + } }; pub const Elf = struct { @@ -530,21 +545,37 @@ pub const Elf = struct { elf.allocator.free(elf.section_headers); } - pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { - section_loop: for (elf.section_headers) |*elf_section| { - if (elf_section.sh_type == SHT_NULL) continue; - const name_offset = elf.string_section.offset + elf_section.name; - try elf.seekable_stream.seekTo(name_offset); + pub const SectionIterator = struct { + section_headers: *const []SectionHeader, + count: usize, - for (name) |expected_c| { - const target_c = try elf.in_stream.readByte(); - if (target_c == 0 or expected_c != target_c) continue :section_loop; - } + pub fn next(it: *SectionIterator) ?SectionHeader { + if (it.count >= it.section_headers.len) return null; + const val = it.section_headers.*[it.count]; + it.count += 1; + return val; + } + + pub fn reset(it: *SectionIterator) void { + it.count = 0; + } + }; + + pub fn sectionIterator(elf: *const Elf) SectionIterator { + return SectionIterator{ + .section_headers = &elf.section_headers, + .count = 0, + }; + } + + pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { + var it = elf.sectionIterator(); + while (it.next()) |elf_section| { + if (elf_section.sh_type == SHT_NULL) continue; - { - const null_byte = try elf.in_stream.readByte(); - if (null_byte == 0) return elf_section; + if (try elf_section.nameIs(elf, name)) { + return elf_section; } }