authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-02 18:13:19-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
logf04f9705cca5cccdfaff2eab0e57e9bd253e8441
tree5858ae583a35f98dd0e670cf7566297b712d8676
parent395ab474eba31f5bbe95e96cab06ab066384c4af

dwarf: add support for DWARF5 DW_AT_ranges in subprograms, add DebugRangeIterator

Some DWARF5 subprograms have non-contiguous instruction ranges. An example of such a function is `puts` in Ubuntu's libc. This change fixes name lookups for functions that use DW_AT_range in their DIE.

2 files changed, 135 insertions(+), 81 deletions(-)

lib/std/dwarf.zig+133-80
...@@ -717,7 +717,6 @@ pub const DwarfInfo = struct {...@@ -717,7 +717,6 @@ pub const DwarfInfo = struct {
717 }717 }
718718
719 pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {719 pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
720 // TODO: Can this be binary searched?
721 for (di.func_list.items) |*func| {720 for (di.func_list.items) |*func| {
722 if (func.pc_range) |range| {721 if (func.pc_range) |range| {
723 if (address >= range.start and address < range.end) {722 if (address >= range.start and address < range.end) {
...@@ -835,37 +834,56 @@ pub const DwarfInfo = struct {...@@ -835,37 +834,56 @@ pub const DwarfInfo = struct {
835 break :x null;834 break :x null;
836 };835 };
837836
838 const pc_range = x: {837 var found_range = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
839 if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| {838 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
840 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {839 const pc_end = switch (high_pc_value.*) {
841 const pc_end = switch (high_pc_value.*) {840 FormValue.Address => |value| value,
842 FormValue.Address => |value| value,841 FormValue.Const => |value| b: {
843 FormValue.Const => |value| b: {842 const offset = try value.asUnsignedLe();
844 const offset = try value.asUnsignedLe();843 break :b (low_pc + offset);
845 break :b (low_pc + offset);844 },
846 },845 else => return badDwarf(),
847 else => return badDwarf(),846 };
848 };847
849 break :x PcRange{848 try di.func_list.append(allocator, Func{
849 .name = fn_name,
850 .pc_range = .{
850 .start = low_pc,851 .start = low_pc,
851 .end = pc_end,852 .end = pc_end,
852 };853 },
853 } else {854 });
854 break :x null;
855 }
856 } else |err| {
857 if (err != error.MissingDebugInfo) return err;
858 break :x null;
859 }855 }
856
857 break :blk true;
858 } else |err| blk: {
859 if (err != error.MissingDebugInfo) return err;
860 break :blk false;
860 };861 };
861862
862 // TODO: Debug issue where `puts` in Ubuntu's libc was not found863 if (die_obj.getAttr(AT.ranges)) |ranges_value| blk: {
863 //if (fn_name != null and pc_range != null) debug.print("func_list: {s} -> 0x{x}-0x{x}\n", .{fn_name.?, pc_range.?.start, pc_range.?.end});864 var iter = DebugRangeIterator.init(ranges_value, di, &compile_unit) catch |err| {
865 if (err != error.MissingDebugInfo) return err;
866 break :blk;
867 };
868
869 while (try iter.next()) |range| {
870 found_range = true;
871 try di.func_list.append(allocator, Func{
872 .name = fn_name,
873 .pc_range = .{
874 .start = range.start_addr,
875 .end = range.end_addr,
876 },
877 });
878 }
879 }
864880
865 try di.func_list.append(allocator, Func{881 if (!found_range) {
866 .name = fn_name,882 try di.func_list.append(allocator, Func{
867 .pc_range = pc_range,883 .name = fn_name,
868 });884 .pc_range = null,
885 });
886 }
869 },887 },
870 else => {},888 else => {},
871 }889 }
...@@ -966,17 +984,18 @@ pub const DwarfInfo = struct {...@@ -966,17 +984,18 @@ pub const DwarfInfo = struct {
966 }984 }
967 }985 }
968986
969 pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {987 const DebugRangeIterator = struct {
970 for (di.compile_unit_list.items) |*compile_unit| {988 base_address: u64,
971 if (compile_unit.pc_range) |range| {989 section_type: DwarfSection,
972 if (target_address >= range.start and target_address < range.end) return compile_unit;990 di: *DwarfInfo,
973 }991 compile_unit: *const CompileUnit,
992 stream: io.FixedBufferStream([]const u8),
974993
975 const opt_debug_ranges = if (compile_unit.version >= 5) di.section(.debug_rnglists) else di.section(.debug_ranges);994 pub fn init(ranges_value: *const FormValue, di: *DwarfInfo, compile_unit: *const CompileUnit) !@This() {
976 const debug_ranges = opt_debug_ranges orelse continue;995 const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges;
996 const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
977997
978 const ranges_val = compile_unit.die.getAttr(AT.ranges) orelse continue;998 const ranges_offset = switch (ranges_value.*) {
979 const ranges_offset = switch (ranges_val.*) {
980 .SecOffset => |off| off,999 .SecOffset => |off| off,
981 .Const => |c| try c.asUnsignedLe(),1000 .Const => |c| try c.asUnsignedLe(),
982 .RangeListOffset => |idx| off: {1001 .RangeListOffset => |idx| off: {
...@@ -996,8 +1015,7 @@ pub const DwarfInfo = struct {...@@ -996,8 +1015,7 @@ pub const DwarfInfo = struct {
996 };1015 };
9971016
998 var stream = io.fixedBufferStream(debug_ranges);1017 var stream = io.fixedBufferStream(debug_ranges);
999 const in = &stream.reader();1018 try stream.seekTo(ranges_offset);
1000 const seekable = &stream.seekableStream();
10011019
1002 // All the addresses in the list are relative to the value1020 // All the addresses in the list are relative to the value
1003 // specified by DW_AT.low_pc or to some other value encoded1021 // specified by DW_AT.low_pc or to some other value encoded
...@@ -1008,86 +1026,122 @@ pub const DwarfInfo = struct {...@@ -1008,86 +1026,122 @@ pub const DwarfInfo = struct {
1008 else => return err,1026 else => return err,
1009 };1027 };
10101028
1011 try seekable.seekTo(ranges_offset);1029 return .{
1030 .base_address = base_address,
1031 .section_type = section_type,
1032 .di = di,
1033 .compile_unit = compile_unit,
1034 .stream = stream,
1035 };
1036 }
10121037
1013 if (compile_unit.version >= 5) {1038 // Returns the next range in the list, or null if the end was reached.
1014 while (true) {1039 pub fn next(self: *@This()) !?struct{ start_addr: u64, end_addr: u64 } {
1040 const in = self.stream.reader();
1041 switch (self.section_type) {
1042 .debug_rnglists => {
1015 const kind = try in.readByte();1043 const kind = try in.readByte();
1016 switch (kind) {1044 switch (kind) {
1017 RLE.end_of_list => break,1045 RLE.end_of_list => return null,
1018 RLE.base_addressx => {1046 RLE.base_addressx => {
1019 const index = try leb.readULEB128(usize, in);1047 const index = try leb.readULEB128(usize, in);
1020 base_address = try di.readDebugAddr(compile_unit.*, index);1048 self.base_address = try self.di.readDebugAddr(self.compile_unit.*, index);
1049 return try self.next();
1021 },1050 },
1022 RLE.startx_endx => {1051 RLE.startx_endx => {
1023 const start_index = try leb.readULEB128(usize, in);1052 const start_index = try leb.readULEB128(usize, in);
1024 const start_addr = try di.readDebugAddr(compile_unit.*, start_index);1053 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
10251054
1026 const end_index = try leb.readULEB128(usize, in);1055 const end_index = try leb.readULEB128(usize, in);
1027 const end_addr = try di.readDebugAddr(compile_unit.*, end_index);1056 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);
10281057
1029 if (target_address >= start_addr and target_address < end_addr) {1058 return .{
1030 return compile_unit;1059 .start_addr = start_addr,
1031 }1060 .end_addr = end_addr,
1061 };
1032 },1062 },
1033 RLE.startx_length => {1063 RLE.startx_length => {
1034 const start_index = try leb.readULEB128(usize, in);1064 const start_index = try leb.readULEB128(usize, in);
1035 const start_addr = try di.readDebugAddr(compile_unit.*, start_index);1065 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
10361066
1037 const len = try leb.readULEB128(usize, in);1067 const len = try leb.readULEB128(usize, in);
1038 const end_addr = start_addr + len;1068 const end_addr = start_addr + len;
10391069
1040 if (target_address >= start_addr and target_address < end_addr) {1070 return .{
1041 return compile_unit;1071 .start_addr = start_addr,
1042 }1072 .end_addr = end_addr,
1073 };
1043 },1074 },
1044 RLE.offset_pair => {1075 RLE.offset_pair => {
1045 const start_addr = try leb.readULEB128(usize, in);1076 const start_addr = try leb.readULEB128(usize, in);
1046 const end_addr = try leb.readULEB128(usize, in);1077 const end_addr = try leb.readULEB128(usize, in);
1078
1047 // This is the only kind that uses the base address1079 // This is the only kind that uses the base address
1048 if (target_address >= base_address + start_addr and target_address < base_address + end_addr) {1080 return .{
1049 return compile_unit;1081 .start_addr = self.base_address + start_addr,
1050 }1082 .end_addr = self.base_address + end_addr,
1083 };
1051 },1084 },
1052 RLE.base_address => {1085 RLE.base_address => {
1053 base_address = try in.readInt(usize, di.endian);1086 self.base_address = try in.readInt(usize, self.di.endian);
1087 return try self.next();
1054 },1088 },
1055 RLE.start_end => {1089 RLE.start_end => {
1056 const start_addr = try in.readInt(usize, di.endian);1090 const start_addr = try in.readInt(usize, self.di.endian);
1057 const end_addr = try in.readInt(usize, di.endian);1091 const end_addr = try in.readInt(usize, self.di.endian);
1058 if (target_address >= start_addr and target_address < end_addr) {1092
1059 return compile_unit;1093 return .{
1060 }1094 .start_addr = start_addr,
1095 .end_addr = end_addr,
1096 };
1061 },1097 },
1062 RLE.start_length => {1098 RLE.start_length => {
1063 const start_addr = try in.readInt(usize, di.endian);1099 const start_addr = try in.readInt(usize, self.di.endian);
1064 const len = try leb.readULEB128(usize, in);1100 const len = try leb.readULEB128(usize, in);
1065 const end_addr = start_addr + len;1101 const end_addr = start_addr + len;
1066 if (target_address >= start_addr and target_address < end_addr) {1102
1067 return compile_unit;1103 return .{
1068 }1104 .start_addr = start_addr,
1105 .end_addr = end_addr,
1106 };
1069 },1107 },
1070 else => return badDwarf(),1108 else => return badDwarf(),
1071 }1109 }
1072 }1110 },
1073 } else {1111 .debug_ranges => {
1074 while (true) {1112 const start_addr = try in.readInt(usize, self.di.endian);
1075 const begin_addr = try in.readInt(usize, di.endian);1113 const end_addr = try in.readInt(usize, self.di.endian);
1076 const end_addr = try in.readInt(usize, di.endian);1114 if (start_addr == 0 and end_addr == 0) return null;
1077 if (begin_addr == 0 and end_addr == 0) {1115
1078 break;
1079 }
1080 // This entry selects a new value for the base address1116 // This entry selects a new value for the base address
1081 if (begin_addr == math.maxInt(usize)) {1117 if (start_addr == math.maxInt(usize)) {
1082 base_address = end_addr;1118 self.base_address = end_addr;
1083 continue;1119 return try self.next();
1084 }
1085 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
1086 return compile_unit;
1087 }1120 }
1088 }1121
1122 return .{
1123 .start_addr = self.base_address + start_addr,
1124 .end_addr = self.base_address + end_addr,
1125 };
1126 },
1127 else => unreachable,
1089 }1128 }
1090 }1129 }
1130 };
1131
1132 pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
1133 for (di.compile_unit_list.items) |*compile_unit| {
1134 if (compile_unit.pc_range) |range| {
1135 if (target_address >= range.start and target_address < range.end) return compile_unit;
1136 }
1137
1138 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;
1139 var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue;
1140 while (try iter.next()) |range| {
1141 if (target_address >= range.start_addr and target_address < range.end_addr) return compile_unit;
1142 }
1143 }
1144
1091 return missingDwarf();1145 return missingDwarf();
1092 }1146 }
10931147
...@@ -1566,7 +1620,6 @@ pub const DwarfInfo = struct {...@@ -1566,7 +1620,6 @@ pub const DwarfInfo = struct {
1566 }1620 }
1567 }1621 }
15681622
1569 // TODO: Avoiding sorting if has_eh_frame_hdr exists
1570 std.mem.sort(FrameDescriptionEntry, di.fde_list.items, {}, struct {1623 std.mem.sort(FrameDescriptionEntry, di.fde_list.items, {}, struct {
1571 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {1624 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
1572 _ = ctx;1625 _ = ctx;
lib/std/dwarf/abi.zig+2-1
...@@ -245,7 +245,8 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext...@@ -245,7 +245,8 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
245}245}
246246
247/// Returns the ABI-defined default value this register has in the unwinding table247/// Returns the ABI-defined default value this register has in the unwinding table
248/// before running any of the CIE instructions.248/// before running any of the CIE instructions. The DWARF spec defines these values
249// to be undefined, but allows ABI authors to override that default.
249pub fn getRegDefaultValue(reg_number: u8, out: []u8) void {250pub fn getRegDefaultValue(reg_number: u8, out: []u8) void {
250 // TODO: Implement any ABI-specific rules for the default value for registers251 // TODO: Implement any ABI-specific rules for the default value for registers
251 _ = reg_number;252 _ = reg_number;