authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-05-26 07:14:54+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-29 11:28:39-07:00
log46e724ab28ba5934471d8b2dec60acdd7885d64b
treeaa4469c776d972e512c8bc046fc8c68dd4864abe
parent1697d44809151e4759f6b5f9447a908c30ac1e84

std.dwarf: handle DWARF 5 compile unit DW_AT_ranges correctly

This data changed quite significantly between DWARF 4 and 5. Some systems are shipping DWARF 5 libraries (Void Linux on musl libc seems to use it for crt1 etc), which meant when printing stack traces, a random compile unit might be incorrectly identified as containing an address, resulting in incorrect location information. I was consistently experiencing this issue with compiler stack traces, and this change fixed it.

1 files changed, 137 insertions(+), 56 deletions(-)

lib/std/dwarf.zig+137-56
......@@ -299,30 +299,7 @@ const Die = struct {
299299 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
300300 return switch (form_value.*) {
301301 FormValue.Address => |value| value,
302 FormValue.AddrOffset => |index| {
303 const debug_addr = di.debug_addr orelse return badDwarf();
304 // addr_base points to the first item after the header, however we
305 // need to read the header to know the size of each item. Empirically,
306 // it may disagree with is_64 on the compile unit.
307 // The header is 8 or 12 bytes depending on is_64.
308 if (compile_unit.addr_base < 8) return badDwarf();
309
310 const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);
311 if (version != 5) return badDwarf();
312
313 const addr_size = debug_addr[compile_unit.addr_base - 2];
314 const seg_size = debug_addr[compile_unit.addr_base - 1];
315
316 const byte_offset = compile_unit.addr_base + (addr_size + seg_size) * index;
317 if (byte_offset + addr_size > debug_addr.len) return badDwarf();
318 switch (addr_size) {
319 1 => return debug_addr[byte_offset],
320 2 => return mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
321 4 => return mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
322 8 => return mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
323 else => return badDwarf(),
324 }
325 },
302 FormValue.AddrOffset => |index| di.readDebugAddr(compile_unit, index),
326303 else => error.InvalidDebugInfo,
327304 };
328305 }
......@@ -952,41 +929,119 @@ pub const DwarfInfo = struct {
952929 if (compile_unit.pc_range) |range| {
953930 if (target_address >= range.start and target_address < range.end) return compile_unit;
954931 }
955 if (di.debug_ranges) |debug_ranges| {
956 if (compile_unit.die.getAttrSecOffset(AT.ranges)) |ranges_offset| {
957 var stream = io.fixedBufferStream(debug_ranges);
958 const in = &stream.reader();
959 const seekable = &stream.seekableStream();
960
961 // All the addresses in the list are relative to the value
962 // specified by DW_AT.low_pc or to some other value encoded
963 // in the list itself.
964 // If no starting value is specified use zero.
965 var base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) {
966 error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135
967 else => return err,
968 };
969932
970 try seekable.seekTo(ranges_offset);
933 const opt_debug_ranges = if (compile_unit.version >= 5) di.debug_rnglists else di.debug_ranges;
934 const debug_ranges = opt_debug_ranges orelse continue;
935
936 const ranges_val = compile_unit.die.getAttr(AT.ranges) orelse continue;
937 const ranges_offset = switch (ranges_val.*) {
938 .SecOffset => |off| off,
939 .RangeListOffset => |idx| off: {
940 if (compile_unit.is_64) {
941 const offset_loc = @intCast(usize, compile_unit.rnglists_base + 8 * idx);
942 if (offset_loc + 8 > debug_ranges.len) return badDwarf();
943 const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);
944 break :off compile_unit.rnglists_base + offset;
945 } else {
946 const offset_loc = @intCast(usize, compile_unit.rnglists_base + 4 * idx);
947 if (offset_loc + 4 > debug_ranges.len) return badDwarf();
948 const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);
949 break :off compile_unit.rnglists_base + offset;
950 }
951 },
952 else => return badDwarf(),
953 };
971954
972 while (true) {
973 const begin_addr = try in.readInt(usize, di.endian);
974 const end_addr = try in.readInt(usize, di.endian);
975 if (begin_addr == 0 and end_addr == 0) {
976 break;
977 }
978 // This entry selects a new value for the base address
979 if (begin_addr == math.maxInt(usize)) {
980 base_address = end_addr;
981 continue;
982 }
983 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
984 return compile_unit;
985 }
955 var stream = io.fixedBufferStream(debug_ranges);
956 const in = &stream.reader();
957 const seekable = &stream.seekableStream();
958
959 // All the addresses in the list are relative to the value
960 // specified by DW_AT.low_pc or to some other value encoded
961 // in the list itself.
962 // If no starting value is specified use zero.
963 var base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) {
964 error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135
965 else => return err,
966 };
967
968 try seekable.seekTo(ranges_offset);
969
970 if (compile_unit.version >= 5) {
971 while (true) {
972 const kind = try in.readByte();
973 switch (kind) {
974 RLE.end_of_list => break,
975 RLE.base_addressx => {
976 const index = try leb.readULEB128(usize, in);
977 base_address = try di.readDebugAddr(compile_unit.*, index);
978 },
979 RLE.startx_endx => {
980 const start_index = try leb.readULEB128(usize, in);
981 const start_addr = try di.readDebugAddr(compile_unit.*, start_index);
982
983 const end_index = try leb.readULEB128(usize, in);
984 const end_addr = try di.readDebugAddr(compile_unit.*, end_index);
985
986 if (target_address >= start_addr and target_address < end_addr) {
987 return compile_unit;
988 }
989 },
990 RLE.startx_length => {
991 const start_index = try leb.readULEB128(usize, in);
992 const start_addr = try di.readDebugAddr(compile_unit.*, start_index);
993
994 const len = try leb.readULEB128(usize, in);
995 const end_addr = start_addr + len;
996
997 if (target_address >= start_addr and target_address < end_addr) {
998 return compile_unit;
999 }
1000 },
1001 RLE.offset_pair => {
1002 const start_addr = try leb.readULEB128(usize, in);
1003 const end_addr = try leb.readULEB128(usize, in);
1004 // This is the only kind that uses the base address
1005 if (target_address >= base_address + start_addr and target_address < base_address + end_addr) {
1006 return compile_unit;
1007 }
1008 },
1009 RLE.base_address => {
1010 base_address = try in.readInt(usize, di.endian);
1011 },
1012 RLE.start_end => {
1013 const start_addr = try in.readInt(usize, di.endian);
1014 const end_addr = try in.readInt(usize, di.endian);
1015 if (target_address >= start_addr and target_address < end_addr) {
1016 return compile_unit;
1017 }
1018 },
1019 RLE.start_length => {
1020 const start_addr = try in.readInt(usize, di.endian);
1021 const len = try leb.readULEB128(usize, in);
1022 const end_addr = start_addr + len;
1023 if (target_address >= start_addr and target_address < end_addr) {
1024 return compile_unit;
1025 }
1026 },
1027 else => return badDwarf(),
1028 }
1029 }
1030 } else {
1031 while (true) {
1032 const begin_addr = try in.readInt(usize, di.endian);
1033 const end_addr = try in.readInt(usize, di.endian);
1034 if (begin_addr == 0 and end_addr == 0) {
1035 break;
1036 }
1037 // This entry selects a new value for the base address
1038 if (begin_addr == math.maxInt(usize)) {
1039 base_address = end_addr;
1040 continue;
1041 }
1042 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
1043 return compile_unit;
9861044 }
987 } else |err| {
988 if (err != error.MissingDebugInfo) return err;
989 continue;
9901045 }
9911046 }
9921047 }
......@@ -1366,6 +1421,32 @@ pub const DwarfInfo = struct {
13661421 fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 {
13671422 return getStringGeneric(di.debug_line_str, offset);
13681423 }
1424
1425 fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 {
1426 const debug_addr = di.debug_addr orelse return badDwarf();
1427
1428 // addr_base points to the first item after the header, however we
1429 // need to read the header to know the size of each item. Empirically,
1430 // it may disagree with is_64 on the compile unit.
1431 // The header is 8 or 12 bytes depending on is_64.
1432 if (compile_unit.addr_base < 8) return badDwarf();
1433
1434 const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);
1435 if (version != 5) return badDwarf();
1436
1437 const addr_size = debug_addr[compile_unit.addr_base - 2];
1438 const seg_size = debug_addr[compile_unit.addr_base - 1];
1439
1440 const byte_offset = @intCast(usize, compile_unit.addr_base + (addr_size + seg_size) * index);
1441 if (byte_offset + addr_size > debug_addr.len) return badDwarf();
1442 return switch (addr_size) {
1443 1 => debug_addr[byte_offset],
1444 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
1445 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
1446 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
1447 else => badDwarf(),
1448 };
1449 }
13691450};
13701451
13711452/// Initialize DWARF info. The caller has the responsibility to initialize most