authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-23 00:28:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-23 00:28:45-07:00
log1ce71c86bff351a4ade4321e188a13c9d3cff8b4
tree59cfb709ae37a1f737d30b074f1360a1f8f3b6c4
parentc84e5ee87852eafff0cbf986bf02c5221cbcec35

std.debug: implement support for DWARFv5


5 files changed, 415 insertions(+), 160 deletions(-)

lib/std/debug.zig+42-2
...@@ -923,6 +923,11 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn...@@ -923,6 +923,11 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn
923 var opt_debug_line: ?[]const u8 = null;923 var opt_debug_line: ?[]const u8 = null;
924 var opt_debug_line_str: ?[]const u8 = null;924 var opt_debug_line_str: ?[]const u8 = null;
925 var opt_debug_ranges: ?[]const u8 = null;925 var opt_debug_ranges: ?[]const u8 = null;
926 var opt_debug_loclists: ?[]const u8 = null;
927 var opt_debug_rnglists: ?[]const u8 = null;
928 var opt_debug_addr: ?[]const u8 = null;
929 var opt_debug_names: ?[]const u8 = null;
930 var opt_debug_frame: ?[]const u8 = null;
926931
927 for (shdrs) |*shdr| {932 for (shdrs) |*shdr| {
928 if (shdr.sh_type == elf.SHT_NULL) continue;933 if (shdr.sh_type == elf.SHT_NULL) continue;
...@@ -942,6 +947,16 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn...@@ -942,6 +947,16 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn
942 opt_debug_line_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);947 opt_debug_line_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
943 } else if (mem.eql(u8, name, ".debug_ranges")) {948 } else if (mem.eql(u8, name, ".debug_ranges")) {
944 opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);949 opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
950 } else if (mem.eql(u8, name, ".debug_loclists")) {
951 opt_debug_loclists = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
952 } else if (mem.eql(u8, name, ".debug_rnglists")) {
953 opt_debug_rnglists = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
954 } else if (mem.eql(u8, name, ".debug_addr")) {
955 opt_debug_addr = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
956 } else if (mem.eql(u8, name, ".debug_names")) {
957 opt_debug_names = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
958 } else if (mem.eql(u8, name, ".debug_frame")) {
959 opt_debug_frame = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
945 }960 }
946 }961 }
947962
...@@ -954,6 +969,11 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn...@@ -954,6 +969,11 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn
954 .debug_line = opt_debug_line orelse return error.MissingDebugInfo,969 .debug_line = opt_debug_line orelse return error.MissingDebugInfo,
955 .debug_line_str = opt_debug_line_str,970 .debug_line_str = opt_debug_line_str,
956 .debug_ranges = opt_debug_ranges,971 .debug_ranges = opt_debug_ranges,
972 .debug_loclists = opt_debug_loclists,
973 .debug_rnglists = opt_debug_rnglists,
974 .debug_addr = opt_debug_addr,
975 .debug_names = opt_debug_names,
976 .debug_frame = opt_debug_frame,
957 };977 };
958978
959 try DW.openDwarfDebugInfo(&di, allocator);979 try DW.openDwarfDebugInfo(&di, allocator);
...@@ -1494,6 +1514,11 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1494,6 +1514,11 @@ pub const ModuleDebugInfo = switch (native_os) {
1494 var opt_debug_str: ?macho.section_64 = null;1514 var opt_debug_str: ?macho.section_64 = null;
1495 var opt_debug_line_str: ?macho.section_64 = null;1515 var opt_debug_line_str: ?macho.section_64 = null;
1496 var opt_debug_ranges: ?macho.section_64 = null;1516 var opt_debug_ranges: ?macho.section_64 = null;
1517 var opt_debug_loclists: ?macho.section_64 = null;
1518 var opt_debug_rnglists: ?macho.section_64 = null;
1519 var opt_debug_addr: ?macho.section_64 = null;
1520 var opt_debug_names: ?macho.section_64 = null;
1521 var opt_debug_frame: ?macho.section_64 = null;
14971522
1498 for (segcmd.?.getSections()) |sect| {1523 for (segcmd.?.getSections()) |sect| {
1499 const name = sect.sectName();1524 const name = sect.sectName();
...@@ -1509,6 +1534,16 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1509,6 +1534,16 @@ pub const ModuleDebugInfo = switch (native_os) {
1509 opt_debug_line_str = sect;1534 opt_debug_line_str = sect;
1510 } else if (mem.eql(u8, name, "__debug_ranges")) {1535 } else if (mem.eql(u8, name, "__debug_ranges")) {
1511 opt_debug_ranges = sect;1536 opt_debug_ranges = sect;
1537 } else if (mem.eql(u8, name, "__debug_loclists")) {
1538 opt_debug_loclists = sect;
1539 } else if (mem.eql(u8, name, "__debug_rnglists")) {
1540 opt_debug_rnglists = sect;
1541 } else if (mem.eql(u8, name, "__debug_addr")) {
1542 opt_debug_addr = sect;
1543 } else if (mem.eql(u8, name, "__debug_names")) {
1544 opt_debug_names = sect;
1545 } else if (mem.eql(u8, name, "__debug_frame")) {
1546 opt_debug_frame = sect;
1512 }1547 }
1513 }1548 }
15141549
...@@ -1536,6 +1571,11 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1536,6 +1571,11 @@ pub const ModuleDebugInfo = switch (native_os) {
1536 try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)1571 try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)
1537 else1572 else
1538 null,1573 null,
1574 .debug_loclists = opt_debug_loclists,
1575 .debug_rnglists = opt_debug_rnglists,
1576 .debug_addr = opt_debug_addr,
1577 .debug_names = opt_debug_names,
1578 .debug_frame = opt_debug_frame,
1539 };1579 };
15401580
1541 try DW.openDwarfDebugInfo(&di, allocator);1581 try DW.openDwarfDebugInfo(&di, allocator);
...@@ -1590,7 +1630,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1590,7 +1630,7 @@ pub const ModuleDebugInfo = switch (native_os) {
1590 .compile_unit_name = compile_unit.die.getAttrString(1630 .compile_unit_name = compile_unit.die.getAttrString(
1591 o_file_di,1631 o_file_di,
1592 DW.AT.name,1632 DW.AT.name,
1593 compile_unit.is_64,1633 self.di.debug_str,
1594 ) catch |err| switch (err) {1634 ) catch |err| switch (err) {
1595 error.MissingDebugInfo, error.InvalidDebugInfo => "???",1635 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1596 },1636 },
...@@ -1712,7 +1752,7 @@ fn getSymbolFromDwarf(allocator: mem.Allocator, address: u64, di: *DW.DwarfInfo)...@@ -1712,7 +1752,7 @@ fn getSymbolFromDwarf(allocator: mem.Allocator, address: u64, di: *DW.DwarfInfo)
1712 if (nosuspend di.findCompileUnit(address)) |compile_unit| {1752 if (nosuspend di.findCompileUnit(address)) |compile_unit| {
1713 return SymbolInfo{1753 return SymbolInfo{
1714 .symbol_name = nosuspend di.getSymbolName(address) orelse "???",1754 .symbol_name = nosuspend di.getSymbolName(address) orelse "???",
1715 .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT.name, compile_unit.is_64) catch |err| switch (err) {1755 .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT.name, di.debug_str) catch |err| switch (err) {
1716 error.MissingDebugInfo, error.InvalidDebugInfo => "???",1756 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1717 },1757 },
1718 .line_info = nosuspend di.getLineNumberInfo(allocator, compile_unit.*, address) catch |err| switch (err) {1758 .line_info = nosuspend di.getLineNumberInfo(allocator, compile_unit.*, address) catch |err| switch (err) {
lib/std/dwarf.zig+351-153
...@@ -205,6 +205,7 @@ const AbbrevAttr = struct {...@@ -205,6 +205,7 @@ const AbbrevAttr = struct {
205205
206const FormValue = union(enum) {206const FormValue = union(enum) {
207 Address: u64,207 Address: u64,
208 AddrOffset: u64,
208 Block: []u8,209 Block: []u8,
209 Const: Constant,210 Const: Constant,
210 ExprLoc: []u8,211 ExprLoc: []u8,
...@@ -216,14 +217,35 @@ const FormValue = union(enum) {...@@ -216,14 +217,35 @@ const FormValue = union(enum) {
216 StrPtr: u64,217 StrPtr: u64,
217 StrOffset: u64,218 StrOffset: u64,
218 LineStrPtr: u64,219 LineStrPtr: u64,
220 LocListOffset: u64,
221 RangeListOffset: u64,
222
223 fn getString(fv: FormValue, di: DwarfInfo) ![]const u8 {
224 switch (fv) {
225 .String => |s| return s,
226 .StrPtr => |off| return di.getString(off),
227 .LineStrPtr => |off| return di.getLineString(off),
228 else => return badDwarf(),
229 }
230 }
231
232 fn getUInt(fv: FormValue, comptime U: type) !U {
233 switch (fv) {
234 .Const => |c| {
235 const int = try c.asUnsignedLe();
236 return math.cast(U, int) orelse return badDwarf();
237 },
238 else => return badDwarf(),
239 }
240 }
219};241};
220242
221const Constant = struct {243const Constant = struct {
222 payload: u64,244 payload: u64,
223 signed: bool,245 signed: bool,
224246
225 fn asUnsignedLe(self: *const Constant) !u64 {247 fn asUnsignedLe(self: Constant) !u64 {
226 if (self.signed) return error.InvalidDebugInfo;248 if (self.signed) return badDwarf();
227 return self.payload;249 return self.payload;
228 }250 }
229};251};
...@@ -252,16 +274,57 @@ const Die = struct {...@@ -252,16 +274,57 @@ const Die = struct {
252 return null;274 return null;
253 }275 }
254276
255 fn getAttrAddr(self: *const Die, id: u64) !u64 {277 fn getAttrAddr(self: *const Die, di: *DwarfInfo, id: u64) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
256 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;278 const form_value = self.getAttr(id) orelse return missingDwarf();
257 return switch (form_value.*) {279 return switch (form_value.*) {
258 FormValue.Address => |value| value,280 FormValue.Address => |value| value,
281 FormValue.AddrOffset => |index| {
282 const debug_addr = di.debug_addr orelse return badDwarf();
283 if (debug_addr.len < 8) return badDwarf();
284 const first_32_bits = mem.readInt(u32, debug_addr[0..4], di.endian);
285 const is_64 = first_32_bits == 0xffffffff;
286 var off: usize = undefined;
287 const length: u64 = l: {
288 if (is_64) {
289 if (debug_addr.len < 16) return badDwarf();
290 off = 12;
291 break :l mem.readInt(u64, debug_addr[4..12], di.endian);
292 } else {
293 if (debug_addr.len < 8) return badDwarf();
294 if (first_32_bits >= 0xfffffff0) return badDwarf();
295 off = 4;
296 break :l first_32_bits;
297 }
298 };
299 if (index > length) return badDwarf();
300
301 const version = mem.readInt(u16, debug_addr[off..][0..2], di.endian);
302 off += 2;
303 if (version < 5) return badDwarf();
304
305 const addr_size = debug_addr[off];
306 off += 1;
307
308 const seg_size = debug_addr[off];
309 off += 1;
310
311 const base_offset = self.getAttrSecOffset(AT.addr_base) catch off;
312 const byte_offset = base_offset + (addr_size + seg_size) * index;
313 if (byte_offset + addr_size > debug_addr.len) return badDwarf();
314 switch (addr_size) {
315 1 => return debug_addr[byte_offset],
316 2 => return mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
317 4 => return mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
318 8 => return mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
319 else => return badDwarf(),
320 }
321 },
259 else => error.InvalidDebugInfo,322 else => error.InvalidDebugInfo,
260 };323 };
261 }324 }
262325
263 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {326 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
264 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;327 const form_value = self.getAttr(id) orelse return missingDwarf();
265 return switch (form_value.*) {328 return switch (form_value.*) {
266 FormValue.Const => |value| value.asUnsignedLe(),329 FormValue.Const => |value| value.asUnsignedLe(),
267 FormValue.SecOffset => |value| value,330 FormValue.SecOffset => |value| value,
...@@ -270,7 +333,7 @@ const Die = struct {...@@ -270,7 +333,7 @@ const Die = struct {
270 }333 }
271334
272 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {335 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
273 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;336 const form_value = self.getAttr(id) orelse return missingDwarf();
274 return switch (form_value.*) {337 return switch (form_value.*) {
275 FormValue.Const => |value| value.asUnsignedLe(),338 FormValue.Const => |value| value.asUnsignedLe(),
276 else => error.InvalidDebugInfo,339 else => error.InvalidDebugInfo,
...@@ -278,33 +341,67 @@ const Die = struct {...@@ -278,33 +341,67 @@ const Die = struct {
278 }341 }
279342
280 fn getAttrRef(self: *const Die, id: u64) !u64 {343 fn getAttrRef(self: *const Die, id: u64) !u64 {
281 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;344 const form_value = self.getAttr(id) orelse return missingDwarf();
282 return switch (form_value.*) {345 return switch (form_value.*) {
283 FormValue.Ref => |value| value,346 FormValue.Ref => |value| value,
284 else => error.InvalidDebugInfo,347 else => error.InvalidDebugInfo,
285 };348 };
286 }349 }
287350
288 pub fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64, is_64: bool) ![]const u8 {351 pub fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64, opt_str: ?[]const u8) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {
289 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;352 const form_value = self.getAttr(id) orelse return missingDwarf();
290 return switch (form_value.*) {353 switch (form_value.*) {
291 FormValue.String => |value| value,354 FormValue.String => |value| return value,
292 FormValue.StrPtr => |offset| di.getString(offset),355 FormValue.StrPtr => |offset| return di.getString(offset),
293 FormValue.StrOffset => |index| blk: {356 FormValue.StrOffset => |index| {
294 const base_offset = self.getAttrSecOffset(AT.str_offsets_base) catch 0;357 const debug_str_offsets = di.debug_str_offsets orelse return badDwarf();
295 break :blk di.getLineString(try di.getStringOffset(base_offset + index, is_64));358 if (debug_str_offsets.len < 8) return badDwarf();
359 const first_32_bits = mem.readInt(u32, debug_str_offsets[0..4], di.endian);
360 const is_64 = first_32_bits == 0xffffffff;
361 var off: usize = undefined;
362 const length: u64 = l: {
363 if (is_64) {
364 if (debug_str_offsets.len < 16) return badDwarf();
365 off = 12;
366 break :l mem.readInt(u64, debug_str_offsets[4..12], di.endian);
367 } else {
368 if (debug_str_offsets.len < 8) return badDwarf();
369 if (first_32_bits >= 0xfffffff0) return badDwarf();
370 off = 4;
371 break :l first_32_bits;
372 }
373 };
374 if (index > length) return badDwarf();
375
376 const version = mem.readInt(u16, debug_str_offsets[off..][0..2], di.endian);
377 off += 2;
378 if (version < 5) return badDwarf();
379
380 off += 2; // reserved
381
382 const base_offset = self.getAttrSecOffset(AT.str_offsets_base) catch off;
383 if (is_64) {
384 const byte_offset = base_offset + 8 * index;
385 const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);
386 return getStringGeneric(opt_str, offset);
387 } else {
388 const byte_offset = base_offset + 4 * index;
389 const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);
390 return getStringGeneric(opt_str, offset);
391 }
296 },392 },
297 FormValue.LineStrPtr => |offset| di.getLineString(offset),393 FormValue.LineStrPtr => |offset| return di.getLineString(offset),
298 else => error.InvalidDebugInfo,394 else => return badDwarf(),
299 };395 }
300 }396 }
301};397};
302398
303const FileEntry = struct {399const FileEntry = struct {
304 file_name: []const u8,400 path: []const u8,
305 dir_index: usize,401 dir_index: u32 = 0,
306 mtime: usize,402 mtime: u64 = 0,
307 len_bytes: usize,403 size: u64 = 0,
404 md5: u128 = 0,
308};405};
309406
310const LineNumberProgram = struct {407const LineNumberProgram = struct {
...@@ -312,13 +409,14 @@ const LineNumberProgram = struct {...@@ -312,13 +409,14 @@ const LineNumberProgram = struct {
312 file: usize,409 file: usize,
313 line: i64,410 line: i64,
314 column: u64,411 column: u64,
412 version: u16,
315 is_stmt: bool,413 is_stmt: bool,
316 basic_block: bool,414 basic_block: bool,
317 end_sequence: bool,415 end_sequence: bool,
318416
319 default_is_stmt: bool,417 default_is_stmt: bool,
320 target_address: u64,418 target_address: u64,
321 include_dirs: []const []const u8,419 include_dirs: []const FileEntry,
322420
323 prev_valid: bool,421 prev_valid: bool,
324 prev_address: u64,422 prev_address: u64,
...@@ -349,12 +447,18 @@ const LineNumberProgram = struct {...@@ -349,12 +447,18 @@ const LineNumberProgram = struct {
349 self.prev_end_sequence = undefined;447 self.prev_end_sequence = undefined;
350 }448 }
351449
352 pub fn init(is_stmt: bool, include_dirs: []const []const u8, target_address: u64) LineNumberProgram {450 pub fn init(
451 is_stmt: bool,
452 include_dirs: []const FileEntry,
453 target_address: u64,
454 version: u16,
455 ) LineNumberProgram {
353 return LineNumberProgram{456 return LineNumberProgram{
354 .address = 0,457 .address = 0,
355 .file = 1,458 .file = 1,
356 .line = 1,459 .line = 1,
357 .column = 0,460 .column = 0,
461 .version = version,
358 .is_stmt = is_stmt,462 .is_stmt = is_stmt,
359 .basic_block = false,463 .basic_block = false,
360 .end_sequence = false,464 .end_sequence = false,
...@@ -377,18 +481,24 @@ const LineNumberProgram = struct {...@@ -377,18 +481,24 @@ const LineNumberProgram = struct {
377 allocator: mem.Allocator,481 allocator: mem.Allocator,
378 file_entries: []const FileEntry,482 file_entries: []const FileEntry,
379 ) !?debug.LineInfo {483 ) !?debug.LineInfo {
380 if (self.prev_valid and self.target_address >= self.prev_address and self.target_address < self.address) {484 if (self.prev_valid and
381 const file_entry = if (self.prev_file == 0) {485 self.target_address >= self.prev_address and
382 return error.MissingDebugInfo;486 self.target_address < self.address)
383 } else if (self.prev_file - 1 >= file_entries.len) {487 {
384 return error.InvalidDebugInfo;488 const file_index = if (self.version >= 5) self.prev_file else i: {
385 } else &file_entries[self.prev_file - 1];489 if (self.prev_file == 0) return missingDwarf();
490 break :i self.prev_file - 1;
491 };
386492
387 const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {493 if (file_index >= file_entries.len) return badDwarf();
388 return error.InvalidDebugInfo;494 const file_entry = &file_entries[file_index];
389 } else self.include_dirs[file_entry.dir_index];
390495
391 const file_name = try fs.path.join(allocator, &[_][]const u8{ dir_name, file_entry.file_name });496 if (file_entry.dir_index >= self.include_dirs.len) return badDwarf();
497 const dir_name = self.include_dirs[file_entry.dir_index].path;
498
499 const file_name = try fs.path.join(allocator, &[_][]const u8{
500 dir_name, file_entry.path,
501 });
392502
393 return debug.LineInfo{503 return debug.LineInfo{
394 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,504 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
...@@ -415,7 +525,7 @@ fn readUnitLength(in_stream: anytype, endian: std.builtin.Endian, is_64: *bool)...@@ -415,7 +525,7 @@ fn readUnitLength(in_stream: anytype, endian: std.builtin.Endian, is_64: *bool)
415 if (is_64.*) {525 if (is_64.*) {
416 return in_stream.readInt(u64, endian);526 return in_stream.readInt(u64, endian);
417 } else {527 } else {
418 if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;528 if (first_32_bits >= 0xfffffff0) return badDwarf();
419 // TODO this cast should not be needed529 // TODO this cast should not be needed
420 return @as(u64, first_32_bits);530 return @as(u64, first_32_bits);
421 }531 }
...@@ -492,6 +602,12 @@ fn parseFormValueRef(in_stream: anytype, endian: std.builtin.Endian, size: i32)...@@ -492,6 +602,12 @@ fn parseFormValueRef(in_stream: anytype, endian: std.builtin.Endian, size: i32)
492fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, endian: std.builtin.Endian, is_64: bool) anyerror!FormValue {602fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, endian: std.builtin.Endian, is_64: bool) anyerror!FormValue {
493 return switch (form_id) {603 return switch (form_id) {
494 FORM.addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },604 FORM.addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },
605 FORM.addrx1 => return FormValue{ .AddrOffset = try in_stream.readInt(u8, endian) },
606 FORM.addrx2 => return FormValue{ .AddrOffset = try in_stream.readInt(u16, endian) },
607 FORM.addrx3 => return FormValue{ .AddrOffset = try in_stream.readInt(u24, endian) },
608 FORM.addrx4 => return FormValue{ .AddrOffset = try in_stream.readInt(u32, endian) },
609 FORM.addrx => return FormValue{ .AddrOffset = try nosuspend leb.readULEB128(u64, in_stream) },
610
495 FORM.block1 => parseFormValueBlock(allocator, in_stream, endian, 1),611 FORM.block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
496 FORM.block2 => parseFormValueBlock(allocator, in_stream, endian, 2),612 FORM.block2 => parseFormValueBlock(allocator, in_stream, endian, 2),
497 FORM.block4 => parseFormValueBlock(allocator, in_stream, endian, 4),613 FORM.block4 => parseFormValueBlock(allocator, in_stream, endian, 4),
...@@ -527,10 +643,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en...@@ -527,10 +643,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en
527643
528 FORM.string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },644 FORM.string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
529 FORM.strp => FormValue{ .StrPtr = try readAddress(in_stream, endian, is_64) },645 FORM.strp => FormValue{ .StrPtr = try readAddress(in_stream, endian, is_64) },
530 FORM.strx1 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u8, endian)) },646 FORM.strx1 => return FormValue{ .StrOffset = try in_stream.readInt(u8, endian) },
531 FORM.strx2 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u16, endian)) },647 FORM.strx2 => return FormValue{ .StrOffset = try in_stream.readInt(u16, endian) },
532 FORM.strx3 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u24, endian)) },648 FORM.strx3 => return FormValue{ .StrOffset = try in_stream.readInt(u24, endian) },
533 FORM.strx4 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u32, endian)) },649 FORM.strx4 => return FormValue{ .StrOffset = try in_stream.readInt(u32, endian) },
650 FORM.strx => return FormValue{ .StrOffset = try nosuspend leb.readULEB128(u64, in_stream) },
534 FORM.line_strp => FormValue{ .LineStrPtr = try readAddress(in_stream, endian, is_64) },651 FORM.line_strp => FormValue{ .LineStrPtr = try readAddress(in_stream, endian, is_64) },
535 FORM.indirect => {652 FORM.indirect => {
536 const child_form_id = try nosuspend leb.readULEB128(u64, in_stream);653 const child_form_id = try nosuspend leb.readULEB128(u64, in_stream);
...@@ -543,9 +660,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en...@@ -543,9 +660,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en
543 return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });660 return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
544 },661 },
545 FORM.implicit_const => FormValue{ .Const = Constant{ .signed = true, .payload = undefined } },662 FORM.implicit_const => FormValue{ .Const = Constant{ .signed = true, .payload = undefined } },
546663 FORM.loclistx => return FormValue{ .LocListOffset = try nosuspend leb.readULEB128(u64, in_stream) },
664 FORM.rnglistx => return FormValue{ .RangeListOffset = try nosuspend leb.readULEB128(u64, in_stream) },
547 else => {665 else => {
548 return error.InvalidDebugInfo;666 std.debug.print("unrecognized form id: {x}\n", .{form_id});
667 return badDwarf();
549 },668 },
550 };669 };
551}670}
...@@ -567,6 +686,11 @@ pub const DwarfInfo = struct {...@@ -567,6 +686,11 @@ pub const DwarfInfo = struct {
567 debug_line: []const u8,686 debug_line: []const u8,
568 debug_line_str: ?[]const u8,687 debug_line_str: ?[]const u8,
569 debug_ranges: ?[]const u8,688 debug_ranges: ?[]const u8,
689 debug_loclists: ?[]const u8,
690 debug_rnglists: ?[]const u8,
691 debug_addr: ?[]const u8,
692 debug_names: ?[]const u8,
693 debug_frame: ?[]const u8,
570 // Filled later by the initializer694 // Filled later by the initializer
571 abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},695 abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},
572 compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},696 compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
...@@ -602,7 +726,7 @@ pub const DwarfInfo = struct {...@@ -602,7 +726,7 @@ pub const DwarfInfo = struct {
602726
603 fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void {727 fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void {
604 var stream = io.fixedBufferStream(di.debug_info);728 var stream = io.fixedBufferStream(di.debug_info);
605 const in = &stream.reader();729 const in = stream.reader();
606 const seekable = &stream.seekableStream();730 const seekable = &stream.seekableStream();
607 var this_unit_offset: u64 = 0;731 var this_unit_offset: u64 = 0;
608732
...@@ -619,29 +743,26 @@ pub const DwarfInfo = struct {...@@ -619,29 +743,26 @@ pub const DwarfInfo = struct {
619 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));743 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
620744
621 const version = try in.readInt(u16, di.endian);745 const version = try in.readInt(u16, di.endian);
622 if (version < 2 or version > 5) return error.InvalidDebugInfo;746 if (version < 2 or version > 5) return badDwarf();
623747
624 var address_size: u8 = undefined;748 var address_size: u8 = undefined;
625 var debug_abbrev_offset: u64 = undefined;749 var debug_abbrev_offset: u64 = undefined;
626 switch (version) {750 if (version >= 5) {
627 5 => {751 const unit_type = try in.readInt(u8, di.endian);
628 const unit_type = try in.readInt(u8, di.endian);752 if (unit_type != UT.compile) return badDwarf();
629 if (unit_type != UT.compile) return error.InvalidDebugInfo;753 address_size = try in.readByte();
630 address_size = try in.readByte();754 debug_abbrev_offset = if (is_64)
631 debug_abbrev_offset = if (is_64)755 try in.readInt(u64, di.endian)
632 try in.readInt(u64, di.endian)756 else
633 else757 try in.readInt(u32, di.endian);
634 try in.readInt(u32, di.endian);758 } else {
635 },759 debug_abbrev_offset = if (is_64)
636 else => {760 try in.readInt(u64, di.endian)
637 debug_abbrev_offset = if (is_64)761 else
638 try in.readInt(u64, di.endian)762 try in.readInt(u32, di.endian);
639 else763 address_size = try in.readByte();
640 try in.readInt(u32, di.endian);
641 address_size = try in.readByte();
642 },
643 }764 }
644 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;765 if (address_size != @sizeOf(usize)) return badDwarf();
645766
646 const compile_unit_pos = try seekable.getPos();767 const compile_unit_pos = try seekable.getPos();
647 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);768 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
...@@ -662,30 +783,30 @@ pub const DwarfInfo = struct {...@@ -662,30 +783,30 @@ pub const DwarfInfo = struct {
662 // Prevent endless loops783 // Prevent endless loops
663 while (depth > 0) : (depth -= 1) {784 while (depth > 0) : (depth -= 1) {
664 if (this_die_obj.getAttr(AT.name)) |_| {785 if (this_die_obj.getAttr(AT.name)) |_| {
665 const name = try this_die_obj.getAttrString(di, AT.name, is_64);786 const name = try this_die_obj.getAttrString(di, AT.name, di.debug_str);
666 break :x try allocator.dupe(u8, name);787 break :x try allocator.dupe(u8, name);
667 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {788 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
668 // Follow the DIE it points to and repeat789 // Follow the DIE it points to and repeat
669 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);790 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
670 if (ref_offset > next_offset) return error.InvalidDebugInfo;791 if (ref_offset > next_offset) return badDwarf();
671 try seekable.seekTo(this_unit_offset + ref_offset);792 try seekable.seekTo(this_unit_offset + ref_offset);
672 this_die_obj = (try di.parseDie(793 this_die_obj = (try di.parseDie(
673 arena,794 arena,
674 in,795 in,
675 abbrev_table,796 abbrev_table,
676 is_64,797 is_64,
677 )) orelse return error.InvalidDebugInfo;798 )) orelse return badDwarf();
678 } else if (this_die_obj.getAttr(AT.specification)) |_| {799 } else if (this_die_obj.getAttr(AT.specification)) |_| {
679 // Follow the DIE it points to and repeat800 // Follow the DIE it points to and repeat
680 const ref_offset = try this_die_obj.getAttrRef(AT.specification);801 const ref_offset = try this_die_obj.getAttrRef(AT.specification);
681 if (ref_offset > next_offset) return error.InvalidDebugInfo;802 if (ref_offset > next_offset) return badDwarf();
682 try seekable.seekTo(this_unit_offset + ref_offset);803 try seekable.seekTo(this_unit_offset + ref_offset);
683 this_die_obj = (try di.parseDie(804 this_die_obj = (try di.parseDie(
684 arena,805 arena,
685 in,806 in,
686 abbrev_table,807 abbrev_table,
687 is_64,808 is_64,
688 )) orelse return error.InvalidDebugInfo;809 )) orelse return badDwarf();
689 } else {810 } else {
690 break :x null;811 break :x null;
691 }812 }
...@@ -695,7 +816,7 @@ pub const DwarfInfo = struct {...@@ -695,7 +816,7 @@ pub const DwarfInfo = struct {
695 };816 };
696817
697 const pc_range = x: {818 const pc_range = x: {
698 if (die_obj.getAttrAddr(AT.low_pc)) |low_pc| {819 if (die_obj.getAttrAddr(di, AT.low_pc)) |low_pc| {
699 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {820 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
700 const pc_end = switch (high_pc_value.*) {821 const pc_end = switch (high_pc_value.*) {
701 FormValue.Address => |value| value,822 FormValue.Address => |value| value,
...@@ -703,7 +824,7 @@ pub const DwarfInfo = struct {...@@ -703,7 +824,7 @@ pub const DwarfInfo = struct {
703 const offset = try value.asUnsignedLe();824 const offset = try value.asUnsignedLe();
704 break :b (low_pc + offset);825 break :b (low_pc + offset);
705 },826 },
706 else => return error.InvalidDebugInfo,827 else => return badDwarf(),
707 };828 };
708 break :x PcRange{829 break :x PcRange{
709 .start = low_pc,830 .start = low_pc,
...@@ -748,14 +869,14 @@ pub const DwarfInfo = struct {...@@ -748,14 +869,14 @@ pub const DwarfInfo = struct {
748 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));869 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
749870
750 const version = try in.readInt(u16, di.endian);871 const version = try in.readInt(u16, di.endian);
751 if (version < 2 or version > 5) return error.InvalidDebugInfo;872 if (version < 2 or version > 5) return badDwarf();
752873
753 var address_size: u8 = undefined;874 var address_size: u8 = undefined;
754 var debug_abbrev_offset: u64 = undefined;875 var debug_abbrev_offset: u64 = undefined;
755 switch (version) {876 switch (version) {
756 5 => {877 5 => {
757 const unit_type = try in.readInt(u8, di.endian);878 const unit_type = try in.readInt(u8, di.endian);
758 if (unit_type != UT.compile) return error.InvalidDebugInfo;879 if (unit_type != UT.compile) return badDwarf();
759 address_size = try in.readByte();880 address_size = try in.readByte();
760 debug_abbrev_offset = if (is_64)881 debug_abbrev_offset = if (is_64)
761 try in.readInt(u64, di.endian)882 try in.readInt(u64, di.endian)
...@@ -770,7 +891,7 @@ pub const DwarfInfo = struct {...@@ -770,7 +891,7 @@ pub const DwarfInfo = struct {
770 address_size = try in.readByte();891 address_size = try in.readByte();
771 },892 },
772 }893 }
773 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;894 if (address_size != @sizeOf(usize)) return badDwarf();
774895
775 const compile_unit_pos = try seekable.getPos();896 const compile_unit_pos = try seekable.getPos();
776 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);897 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
...@@ -780,12 +901,12 @@ pub const DwarfInfo = struct {...@@ -780,12 +901,12 @@ pub const DwarfInfo = struct {
780 const compile_unit_die = try allocator.create(Die);901 const compile_unit_die = try allocator.create(Die);
781 errdefer allocator.destroy(compile_unit_die);902 errdefer allocator.destroy(compile_unit_die);
782 compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse903 compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse
783 return error.InvalidDebugInfo;904 return badDwarf();
784905
785 if (compile_unit_die.tag_id != TAG.compile_unit) return error.InvalidDebugInfo;906 if (compile_unit_die.tag_id != TAG.compile_unit) return badDwarf();
786907
787 const pc_range = x: {908 const pc_range = x: {
788 if (compile_unit_die.getAttrAddr(AT.low_pc)) |low_pc| {909 if (compile_unit_die.getAttrAddr(di, AT.low_pc)) |low_pc| {
789 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {910 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
790 const pc_end = switch (high_pc_value.*) {911 const pc_end = switch (high_pc_value.*) {
791 FormValue.Address => |value| value,912 FormValue.Address => |value| value,
...@@ -793,7 +914,7 @@ pub const DwarfInfo = struct {...@@ -793,7 +914,7 @@ pub const DwarfInfo = struct {
793 const offset = try value.asUnsignedLe();914 const offset = try value.asUnsignedLe();
794 break :b (low_pc + offset);915 break :b (low_pc + offset);
795 },916 },
796 else => return error.InvalidDebugInfo,917 else => return badDwarf(),
797 };918 };
798 break :x PcRange{919 break :x PcRange{
799 .start = low_pc,920 .start = low_pc,
...@@ -834,7 +955,7 @@ pub const DwarfInfo = struct {...@@ -834,7 +955,7 @@ pub const DwarfInfo = struct {
834 // specified by DW_AT.low_pc or to some other value encoded955 // specified by DW_AT.low_pc or to some other value encoded
835 // in the list itself.956 // in the list itself.
836 // If no starting value is specified use zero.957 // If no starting value is specified use zero.
837 var base_address = compile_unit.die.getAttrAddr(AT.low_pc) catch |err| switch (err) {958 var base_address = compile_unit.die.getAttrAddr(di, AT.low_pc) catch |err| switch (err) {
838 error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135959 error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135
839 else => return err,960 else => return err,
840 };961 };
...@@ -862,7 +983,7 @@ pub const DwarfInfo = struct {...@@ -862,7 +983,7 @@ pub const DwarfInfo = struct {
862 }983 }
863 }984 }
864 }985 }
865 return error.MissingDebugInfo;986 return missingDwarf();
866 }987 }
867988
868 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,989 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
...@@ -929,7 +1050,7 @@ pub const DwarfInfo = struct {...@@ -929,7 +1050,7 @@ pub const DwarfInfo = struct {
929 ) !?Die {1050 ) !?Die {
930 const abbrev_code = try leb.readULEB128(u64, in_stream);1051 const abbrev_code = try leb.readULEB128(u64, in_stream);
931 if (abbrev_code == 0) return null;1052 if (abbrev_code == 0) return null;
932 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;1053 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return badDwarf();
9331054
934 var result = Die{1055 var result = Die{
935 // Lives as long as the Die.1056 // Lives as long as the Die.
...@@ -966,7 +1087,7 @@ pub const DwarfInfo = struct {...@@ -966,7 +1087,7 @@ pub const DwarfInfo = struct {
966 const in = &stream.reader();1087 const in = &stream.reader();
967 const seekable = &stream.seekableStream();1088 const seekable = &stream.seekableStream();
9681089
969 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, compile_unit.is_64);1090 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.debug_line_str);
970 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);1091 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
9711092
972 try seekable.seekTo(line_info_offset);1093 try seekable.seekTo(line_info_offset);
...@@ -974,18 +1095,25 @@ pub const DwarfInfo = struct {...@@ -974,18 +1095,25 @@ pub const DwarfInfo = struct {
974 var is_64: bool = undefined;1095 var is_64: bool = undefined;
975 const unit_length = try readUnitLength(in, di.endian, &is_64);1096 const unit_length = try readUnitLength(in, di.endian, &is_64);
976 if (unit_length == 0) {1097 if (unit_length == 0) {
977 return error.MissingDebugInfo;1098 return missingDwarf();
978 }1099 }
979 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));1100 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
9801101
981 const version = try in.readInt(u16, di.endian);1102 const version = try in.readInt(u16, di.endian);
982 if (version < 2 or version > 4) return error.InvalidDebugInfo;1103 if (version < 2) return badDwarf();
1104
1105 var addr_size: u8 = if (is_64) 8 else 4;
1106 var seg_size: u8 = 0;
1107 if (version >= 5) {
1108 addr_size = try in.readByte();
1109 seg_size = try in.readByte();
1110 }
9831111
984 const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);1112 const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
985 const prog_start_offset = (try seekable.getPos()) + prologue_length;1113 const prog_start_offset = (try seekable.getPos()) + prologue_length;
9861114
987 const minimum_instruction_length = try in.readByte();1115 const minimum_instruction_length = try in.readByte();
988 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;1116 if (minimum_instruction_length == 0) return badDwarf();
9891117
990 if (version >= 4) {1118 if (version >= 4) {
991 // maximum_operations_per_instruction1119 // maximum_operations_per_instruction
...@@ -996,7 +1124,7 @@ pub const DwarfInfo = struct {...@@ -996,7 +1124,7 @@ pub const DwarfInfo = struct {
996 const line_base = try in.readByteSigned();1124 const line_base = try in.readByteSigned();
9971125
998 const line_range = try in.readByte();1126 const line_range = try in.readByte();
999 if (line_range == 0) return error.InvalidDebugInfo;1127 if (line_range == 0) return badDwarf();
10001128
1001 const opcode_base = try in.readByte();1129 const opcode_base = try in.readByte();
10021130
...@@ -1014,36 +1142,120 @@ pub const DwarfInfo = struct {...@@ -1014,36 +1142,120 @@ pub const DwarfInfo = struct {
1014 defer tmp_arena.deinit();1142 defer tmp_arena.deinit();
1015 const arena = tmp_arena.allocator();1143 const arena = tmp_arena.allocator();
10161144
1017 var include_directories = std.ArrayList([]const u8).init(arena);1145 var include_directories = std.ArrayList(FileEntry).init(arena);
1018 try include_directories.append(compile_unit_cwd);1146 var file_entries = std.ArrayList(FileEntry).init(arena);
10191147
1020 while (true) {1148 if (version < 5) {
1021 const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));1149 try include_directories.append(.{ .path = compile_unit_cwd });
1022 if (dir.len == 0) break;1150
1023 try include_directories.append(dir);1151 while (true) {
1152 const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
1153 if (dir.len == 0) break;
1154 try include_directories.append(.{ .path = dir });
1155 }
1156
1157 while (true) {
1158 const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
1159 if (file_name.len == 0) break;
1160 const dir_index = try leb.readULEB128(u32, in);
1161 const mtime = try leb.readULEB128(u64, in);
1162 const size = try leb.readULEB128(u64, in);
1163 try file_entries.append(FileEntry{
1164 .path = file_name,
1165 .dir_index = dir_index,
1166 .mtime = mtime,
1167 .size = size,
1168 });
1169 }
1170 } else {
1171 const FileEntFmt = struct {
1172 content_type_code: u8,
1173 form_code: u16,
1174 };
1175 {
1176 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
1177 const directory_entry_format_count = try in.readByte();
1178 if (directory_entry_format_count > dir_ent_fmt_buf.len) return badDwarf();
1179 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
1180 ent_fmt.* = .{
1181 .content_type_code = try leb.readULEB128(u8, in),
1182 .form_code = try leb.readULEB128(u16, in),
1183 };
1184 }
1185
1186 const directories_count = try leb.readULEB128(usize, in);
1187 try include_directories.ensureUnusedCapacity(directories_count);
1188 {
1189 var i: usize = 0;
1190 while (i < directories_count) : (i += 1) {
1191 var e: FileEntry = .{ .path = &.{} };
1192 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
1193 const form_value = try parseFormValue(
1194 arena,
1195 in,
1196 ent_fmt.form_code,
1197 di.endian,
1198 is_64,
1199 );
1200 switch (ent_fmt.content_type_code) {
1201 LNCT.path => e.path = try form_value.getString(di.*),
1202 LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
1203 LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
1204 LNCT.size => e.size = try form_value.getUInt(u64),
1205 LNCT.MD5 => e.md5 = try form_value.getUInt(u128),
1206 else => continue,
1207 }
1208 }
1209 include_directories.appendAssumeCapacity(e);
1210 }
1211 }
1212 }
1213
1214 var file_ent_fmt_buf: [10]FileEntFmt = undefined;
1215 const file_name_entry_format_count = try in.readByte();
1216 if (file_name_entry_format_count > file_ent_fmt_buf.len) return badDwarf();
1217 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
1218 ent_fmt.* = .{
1219 .content_type_code = try leb.readULEB128(u8, in),
1220 .form_code = try leb.readULEB128(u16, in),
1221 };
1222 }
1223
1224 const file_names_count = try leb.readULEB128(usize, in);
1225 try file_entries.ensureUnusedCapacity(file_names_count);
1226 {
1227 var i: usize = 0;
1228 while (i < file_names_count) : (i += 1) {
1229 var e: FileEntry = .{ .path = &.{} };
1230 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
1231 const form_value = try parseFormValue(
1232 arena,
1233 in,
1234 ent_fmt.form_code,
1235 di.endian,
1236 is_64,
1237 );
1238 switch (ent_fmt.content_type_code) {
1239 LNCT.path => e.path = try form_value.getString(di.*),
1240 LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
1241 LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
1242 LNCT.size => e.size = try form_value.getUInt(u64),
1243 LNCT.MD5 => e.md5 = try form_value.getUInt(u128),
1244 else => continue,
1245 }
1246 }
1247 file_entries.appendAssumeCapacity(e);
1248 }
1249 }
1024 }1250 }
10251251
1026 var file_entries = std.ArrayList(FileEntry).init(arena);
1027 var prog = LineNumberProgram.init(1252 var prog = LineNumberProgram.init(
1028 default_is_stmt,1253 default_is_stmt,
1029 include_directories.items,1254 include_directories.items,
1030 target_address,1255 target_address,
1256 version,
1031 );1257 );
10321258
1033 while (true) {
1034 const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
1035 if (file_name.len == 0) break;
1036 const dir_index = try leb.readULEB128(usize, in);
1037 const mtime = try leb.readULEB128(usize, in);
1038 const len_bytes = try leb.readULEB128(usize, in);
1039 try file_entries.append(FileEntry{
1040 .file_name = file_name,
1041 .dir_index = dir_index,
1042 .mtime = mtime,
1043 .len_bytes = len_bytes,
1044 });
1045 }
1046
1047 try seekable.seekTo(prog_start_offset);1259 try seekable.seekTo(prog_start_offset);
10481260
1049 const next_unit_pos = line_info_offset + next_offset;1261 const next_unit_pos = line_info_offset + next_offset;
...@@ -1053,7 +1265,7 @@ pub const DwarfInfo = struct {...@@ -1053,7 +1265,7 @@ pub const DwarfInfo = struct {
10531265
1054 if (opcode == LNS.extended_op) {1266 if (opcode == LNS.extended_op) {
1055 const op_size = try leb.readULEB128(u64, in);1267 const op_size = try leb.readULEB128(u64, in);
1056 if (op_size < 1) return error.InvalidDebugInfo;1268 if (op_size < 1) return badDwarf();
1057 var sub_op = try in.readByte();1269 var sub_op = try in.readByte();
1058 switch (sub_op) {1270 switch (sub_op) {
1059 LNE.end_sequence => {1271 LNE.end_sequence => {
...@@ -1066,19 +1278,19 @@ pub const DwarfInfo = struct {...@@ -1066,19 +1278,19 @@ pub const DwarfInfo = struct {
1066 prog.address = addr;1278 prog.address = addr;
1067 },1279 },
1068 LNE.define_file => {1280 LNE.define_file => {
1069 const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));1281 const path = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
1070 const dir_index = try leb.readULEB128(usize, in);1282 const dir_index = try leb.readULEB128(u32, in);
1071 const mtime = try leb.readULEB128(usize, in);1283 const mtime = try leb.readULEB128(u64, in);
1072 const len_bytes = try leb.readULEB128(usize, in);1284 const size = try leb.readULEB128(u64, in);
1073 try file_entries.append(FileEntry{1285 try file_entries.append(FileEntry{
1074 .file_name = file_name,1286 .path = path,
1075 .dir_index = dir_index,1287 .dir_index = dir_index,
1076 .mtime = mtime,1288 .mtime = mtime,
1077 .len_bytes = len_bytes,1289 .size = size,
1078 });1290 });
1079 },1291 },
1080 else => {1292 else => {
1081 const fwd_amt = math.cast(isize, op_size - 1) orelse return error.InvalidDebugInfo;1293 const fwd_amt = math.cast(isize, op_size - 1) orelse return badDwarf();
1082 try seekable.seekBy(fwd_amt);1294 try seekable.seekBy(fwd_amt);
1083 },1295 },
1084 }1296 }
...@@ -1129,7 +1341,7 @@ pub const DwarfInfo = struct {...@@ -1129,7 +1341,7 @@ pub const DwarfInfo = struct {
1129 },1341 },
1130 LNS.set_prologue_end => {},1342 LNS.set_prologue_end => {},
1131 else => {1343 else => {
1132 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;1344 if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf();
1133 const len_bytes = standard_opcode_lengths[opcode - 1];1345 const len_bytes = standard_opcode_lengths[opcode - 1];
1134 try seekable.seekBy(len_bytes);1346 try seekable.seekBy(len_bytes);
1135 },1347 },
...@@ -1137,50 +1349,15 @@ pub const DwarfInfo = struct {...@@ -1137,50 +1349,15 @@ pub const DwarfInfo = struct {
1137 }1349 }
1138 }1350 }
11391351
1140 return error.MissingDebugInfo;1352 return missingDwarf();
1141 }1353 }
11421354
1143 fn getString(di: *DwarfInfo, offset: u64) ![]const u8 {1355 fn getString(di: DwarfInfo, offset: u64) ![]const u8 {
1144 if (offset > di.debug_str.len)1356 return getStringGeneric(di.debug_str, offset);
1145 return error.InvalidDebugInfo;
1146 const casted_offset = math.cast(usize, offset) orelse
1147 return error.InvalidDebugInfo;
1148
1149 // Valid strings always have a terminating zero byte
1150 if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
1151 return di.debug_str[casted_offset..last];
1152 }
1153
1154 return error.InvalidDebugInfo;
1155 }1357 }
11561358
1157 fn getStringOffset(di: *DwarfInfo, offset: u64, is_64: bool) !u64 {1359 fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 {
1158 if (di.debug_str_offsets) |debug_str_offsets| {1360 return getStringGeneric(di.debug_line_str, offset);
1159 if (offset >= debug_str_offsets.len) {
1160 return error.InvalidDebugInfo;
1161 }
1162 const offset_casted = @intCast(usize, offset);
1163 if (is_64) {
1164 return std.mem.readIntSlice(u64, debug_str_offsets[offset_casted .. offset_casted + 8], di.endian);
1165 }
1166 return @intCast(u64, std.mem.readIntSlice(u32, debug_str_offsets[offset_casted .. offset_casted + 4], di.endian));
1167 }
1168 return error.InvalidDebugInfo;
1169 }
1170
1171 fn getLineString(di: *DwarfInfo, offset: u64) ![]const u8 {
1172 const debug_line_str = di.debug_line_str orelse return error.InvalidDebugInfo;
1173 if (offset > debug_line_str.len)
1174 return error.InvalidDebugInfo;
1175 const casted_offset = math.cast(usize, offset) orelse
1176 return error.InvalidDebugInfo;
1177
1178 // Valid strings always have a terminating zero byte
1179 if (mem.indexOfScalarPos(u8, debug_line_str, casted_offset, 0)) |last| {
1180 return debug_line_str[casted_offset..last];
1181 }
1182
1183 return error.InvalidDebugInfo;
1184 }1361 }
1185};1362};
11861363
...@@ -1190,3 +1367,24 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {...@@ -1190,3 +1367,24 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
1190 try di.scanAllFunctions(allocator);1367 try di.scanAllFunctions(allocator);
1191 try di.scanAllCompileUnits(allocator);1368 try di.scanAllCompileUnits(allocator);
1192}1369}
1370
1371/// This function is to make it handy to comment out the return and make it
1372/// into a crash when working on this file.
1373inline fn badDwarf() error{InvalidDebugInfo} {
1374 //std.os.abort(); // can be handy to uncomment when working on this file
1375 return error.InvalidDebugInfo;
1376}
1377
1378inline fn missingDwarf() error{MissingDebugInfo} {
1379 //std.os.abort(); // can be handy to uncomment when working on this file
1380 return error.MissingDebugInfo;
1381}
1382
1383fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
1384 const str = opt_str orelse return badDwarf();
1385 if (offset > str.len) return badDwarf();
1386 const casted_offset = math.cast(usize, offset) orelse return badDwarf();
1387 // Valid strings always have a terminating zero byte
1388 const last = mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf();
1389 return str[casted_offset..last :0];
1390}
src/link/MachO.zig+2-2
...@@ -5862,8 +5862,8 @@ pub fn generateSymbolStabs(...@@ -5862,8 +5862,8 @@ pub fn generateSymbolStabs(
5862 else => |e| return e,5862 else => |e| return e,
5863 };5863 };
58645864
5865 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, compile_unit.is_64);5865 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str);
5866 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, compile_unit.is_64);5866 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str);
58675867
5868 // Open scope5868 // Open scope
5869 try locals.ensureUnusedCapacity(3);5869 try locals.ensureUnusedCapacity(3);
src/link/MachO/Object.zig+15
...@@ -584,6 +584,11 @@ pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {...@@ -584,6 +584,11 @@ pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
584 .debug_line = &[0]u8{},584 .debug_line = &[0]u8{},
585 .debug_line_str = &[0]u8{},585 .debug_line_str = &[0]u8{},
586 .debug_ranges = &[0]u8{},586 .debug_ranges = &[0]u8{},
587 .debug_loclists = &[0]u8{},
588 .debug_rnglists = &[0]u8{},
589 .debug_addr = &[0]u8{},
590 .debug_names = &[0]u8{},
591 .debug_frame = &[0]u8{},
587 };592 };
588 for (self.sections.items) |sect| {593 for (self.sections.items) |sect| {
589 const segname = sect.segName();594 const segname = sect.segName();
...@@ -601,6 +606,16 @@ pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {...@@ -601,6 +606,16 @@ pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
601 di.debug_line_str = try self.getSectionContents(sect);606 di.debug_line_str = try self.getSectionContents(sect);
602 } else if (mem.eql(u8, sectname, "__debug_ranges")) {607 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
603 di.debug_ranges = try self.getSectionContents(sect);608 di.debug_ranges = try self.getSectionContents(sect);
609 } else if (mem.eql(u8, sectname, "__debug_loclists")) {
610 di.debug_loclists = try self.getSectionContents(sect);
611 } else if (mem.eql(u8, sectname, "__debug_rnglists")) {
612 di.debug_rnglists = try self.getSectionContents(sect);
613 } else if (mem.eql(u8, sectname, "__debug_addr")) {
614 di.debug_addr = try self.getSectionContents(sect);
615 } else if (mem.eql(u8, sectname, "__debug_names")) {
616 di.debug_names = try self.getSectionContents(sect);
617 } else if (mem.eql(u8, sectname, "__debug_frame")) {
618 di.debug_frame = try self.getSectionContents(sect);
604 }619 }
605 }620 }
606 }621 }
test/stack_traces.zig+5-3
...@@ -21,7 +21,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -21,7 +21,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
21 },21 },
22 .ReleaseSafe = .{22 .ReleaseSafe = .{
23 .exclude_os = .{23 .exclude_os = .{
24 .windows, // segfault24 .windows, // TODO
25 .linux, // defeated by aggressive inlining25 .linux, // defeated by aggressive inlining
26 },26 },
27 .expect = 27 .expect =
...@@ -71,7 +71,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -71,7 +71,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
71 },71 },
72 .ReleaseSafe = .{72 .ReleaseSafe = .{
73 .exclude_os = .{73 .exclude_os = .{
74 .windows, // segfault74 .windows, // TODO
75 .linux, // TODO
75 },76 },
76 .expect = 77 .expect =
77 \\error: TheSkyIsFalling78 \\error: TheSkyIsFalling
...@@ -137,7 +138,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -137,7 +138,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
137 },138 },
138 .ReleaseSafe = .{139 .ReleaseSafe = .{
139 .exclude_os = .{140 .exclude_os = .{
140 .windows, // segfault141 .windows, // TODO
142 .linux, // TODO
141 },143 },
142 .expect = 144 .expect =
143 \\error: TheSkyIsFalling145 \\error: TheSkyIsFalling