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 {
717717 }
718718
719719 pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
720 // TODO: Can this be binary searched?
721720 for (di.func_list.items) |*func| {
722721 if (func.pc_range) |range| {
723722 if (address >= range.start and address < range.end) {
......@@ -835,37 +834,56 @@ pub const DwarfInfo = struct {
835834 break :x null;
836835 };
837836
838 const pc_range = x: {
839 if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| {
840 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
841 const pc_end = switch (high_pc_value.*) {
842 FormValue.Address => |value| value,
843 FormValue.Const => |value| b: {
844 const offset = try value.asUnsignedLe();
845 break :b (low_pc + offset);
846 },
847 else => return badDwarf(),
848 };
849 break :x PcRange{
837 var found_range = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
838 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
839 const pc_end = switch (high_pc_value.*) {
840 FormValue.Address => |value| value,
841 FormValue.Const => |value| b: {
842 const offset = try value.asUnsignedLe();
843 break :b (low_pc + offset);
844 },
845 else => return badDwarf(),
846 };
847
848 try di.func_list.append(allocator, Func{
849 .name = fn_name,
850 .pc_range = .{
850851 .start = low_pc,
851852 .end = pc_end,
852 };
853 } else {
854 break :x null;
855 }
856 } else |err| {
857 if (err != error.MissingDebugInfo) return err;
858 break :x null;
853 },
854 });
859855 }
856
857 break :blk true;
858 } else |err| blk: {
859 if (err != error.MissingDebugInfo) return err;
860 break :blk false;
860861 };
861862
862 // TODO: Debug issue where `puts` in Ubuntu's libc was not found
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});
863 if (die_obj.getAttr(AT.ranges)) |ranges_value| blk: {
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{
866 .name = fn_name,
867 .pc_range = pc_range,
868 });
881 if (!found_range) {
882 try di.func_list.append(allocator, Func{
883 .name = fn_name,
884 .pc_range = null,
885 });
886 }
869887 },
870888 else => {},
871889 }
......@@ -966,17 +984,18 @@ pub const DwarfInfo = struct {
966984 }
967985 }
968986
969 pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
970 for (di.compile_unit_list.items) |*compile_unit| {
971 if (compile_unit.pc_range) |range| {
972 if (target_address >= range.start and target_address < range.end) return compile_unit;
973 }
987 const DebugRangeIterator = struct {
988 base_address: u64,
989 section_type: DwarfSection,
990 di: *DwarfInfo,
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);
976 const debug_ranges = opt_debug_ranges orelse continue;
994 pub fn init(ranges_value: *const FormValue, di: *DwarfInfo, compile_unit: *const CompileUnit) !@This() {
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;
979 const ranges_offset = switch (ranges_val.*) {
998 const ranges_offset = switch (ranges_value.*) {
980999 .SecOffset => |off| off,
9811000 .Const => |c| try c.asUnsignedLe(),
9821001 .RangeListOffset => |idx| off: {
......@@ -996,8 +1015,7 @@ pub const DwarfInfo = struct {
9961015 };
9971016
9981017 var stream = io.fixedBufferStream(debug_ranges);
999 const in = &stream.reader();
1000 const seekable = &stream.seekableStream();
1018 try stream.seekTo(ranges_offset);
10011019
10021020 // All the addresses in the list are relative to the value
10031021 // specified by DW_AT.low_pc or to some other value encoded
......@@ -1008,86 +1026,122 @@ pub const DwarfInfo = struct {
10081026 else => return err,
10091027 };
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) {
1014 while (true) {
1038 // Returns the next range in the list, or null if the end was reached.
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 => {
10151043 const kind = try in.readByte();
10161044 switch (kind) {
1017 RLE.end_of_list => break,
1045 RLE.end_of_list => return null,
10181046 RLE.base_addressx => {
10191047 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();
10211050 },
10221051 RLE.startx_endx => {
10231052 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
10261055 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) {
1030 return compile_unit;
1031 }
1058 return .{
1059 .start_addr = start_addr,
1060 .end_addr = end_addr,
1061 };
10321062 },
10331063 RLE.startx_length => {
10341064 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
10371067 const len = try leb.readULEB128(usize, in);
10381068 const end_addr = start_addr + len;
10391069
1040 if (target_address >= start_addr and target_address < end_addr) {
1041 return compile_unit;
1042 }
1070 return .{
1071 .start_addr = start_addr,
1072 .end_addr = end_addr,
1073 };
10431074 },
10441075 RLE.offset_pair => {
10451076 const start_addr = try leb.readULEB128(usize, in);
10461077 const end_addr = try leb.readULEB128(usize, in);
1078
10471079 // 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) {
1049 return compile_unit;
1050 }
1080 return .{
1081 .start_addr = self.base_address + start_addr,
1082 .end_addr = self.base_address + end_addr,
1083 };
10511084 },
10521085 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();
10541088 },
10551089 RLE.start_end => {
1056 const start_addr = try in.readInt(usize, di.endian);
1057 const end_addr = try in.readInt(usize, di.endian);
1058 if (target_address >= start_addr and target_address < end_addr) {
1059 return compile_unit;
1060 }
1090 const start_addr = try in.readInt(usize, self.di.endian);
1091 const end_addr = try in.readInt(usize, self.di.endian);
1092
1093 return .{
1094 .start_addr = start_addr,
1095 .end_addr = end_addr,
1096 };
10611097 },
10621098 RLE.start_length => {
1063 const start_addr = try in.readInt(usize, di.endian);
1099 const start_addr = try in.readInt(usize, self.di.endian);
10641100 const len = try leb.readULEB128(usize, in);
10651101 const end_addr = start_addr + len;
1066 if (target_address >= start_addr and target_address < end_addr) {
1067 return compile_unit;
1068 }
1102
1103 return .{
1104 .start_addr = start_addr,
1105 .end_addr = end_addr,
1106 };
10691107 },
10701108 else => return badDwarf(),
10711109 }
1072 }
1073 } else {
1074 while (true) {
1075 const begin_addr = try in.readInt(usize, di.endian);
1076 const end_addr = try in.readInt(usize, di.endian);
1077 if (begin_addr == 0 and end_addr == 0) {
1078 break;
1079 }
1110 },
1111 .debug_ranges => {
1112 const start_addr = try in.readInt(usize, self.di.endian);
1113 const end_addr = try in.readInt(usize, self.di.endian);
1114 if (start_addr == 0 and end_addr == 0) return null;
1115
10801116 // This entry selects a new value for the base address
1081 if (begin_addr == math.maxInt(usize)) {
1082 base_address = end_addr;
1083 continue;
1084 }
1085 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
1086 return compile_unit;
1117 if (start_addr == math.maxInt(usize)) {
1118 self.base_address = end_addr;
1119 return try self.next();
10871120 }
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,
10891128 }
10901129 }
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
10911145 return missingDwarf();
10921146 }
10931147
......@@ -1566,7 +1620,6 @@ pub const DwarfInfo = struct {
15661620 }
15671621 }
15681622
1569 // TODO: Avoiding sorting if has_eh_frame_hdr exists
15701623 std.mem.sort(FrameDescriptionEntry, di.fde_list.items, {}, struct {
15711624 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
15721625 _ = ctx;
lib/std/dwarf/abi.zig+2-1
......@@ -245,7 +245,8 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
245245}
246246
247247/// 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.
249250pub fn getRegDefaultValue(reg_number: u8, out: []u8) void {
250251 // TODO: Implement any ABI-specific rules for the default value for registers
251252 _ = reg_number;