authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-01 13:40:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-01 13:56:12-07:00
loge5b46eab3b16a6bf7924ef837fcd6552f430bd58
tree8a90dd4323ba9d89305f33013db9d98e13f3e32d
parent377274ee9ab270886cce1ceaf5ffeddaefd9c239

std: dwarf namespace reorg

std.debug.Dwarf is the parsing/decoding logic. std.dwarf remains the unopinionated types and bits alone. If you look at this diff you can see a lot less redundancy in namespaces.

10 files changed, 5489 insertions(+), 5480 deletions(-)

lib/std/debug.zig+39-37
......@@ -18,6 +18,8 @@ const native_arch = builtin.cpu.arch;
1818const native_os = builtin.os.tag;
1919const native_endian = native_arch.endian();
2020
21pub const Dwarf = @import("debug/Dwarf.zig");
22
2123pub const runtime_safety = switch (builtin.mode) {
2224 .Debug, .ReleaseSafe => true,
2325 .ReleaseFast, .ReleaseSmall => false,
......@@ -67,7 +69,7 @@ pub const SymbolInfo = struct {
6769};
6870const PdbOrDwarf = union(enum) {
6971 pdb: pdb.Pdb,
70 dwarf: DW.DwarfInfo,
72 dwarf: Dwarf,
7173
7274 fn deinit(self: *PdbOrDwarf, allocator: mem.Allocator) void {
7375 switch (self.*) {
......@@ -566,7 +568,7 @@ pub const StackIterator = struct {
566568 // using DWARF and MachO unwind info.
567569 unwind_state: if (have_ucontext) ?struct {
568570 debug_info: *Info,
569 dwarf_context: DW.UnwindContext,
571 dwarf_context: Dwarf.UnwindContext,
570572 last_error: ?UnwindError = null,
571573 failed: bool = false,
572574 } else void = if (have_ucontext) null else {},
......@@ -599,7 +601,7 @@ pub const StackIterator = struct {
599601 var iterator = init(first_address, null);
600602 iterator.unwind_state = .{
601603 .debug_info = debug_info,
602 .dwarf_context = try DW.UnwindContext.init(debug_info.allocator, context),
604 .dwarf_context = try Dwarf.UnwindContext.init(debug_info.allocator, context),
603605 };
604606
605607 return iterator;
......@@ -783,7 +785,7 @@ pub const StackIterator = struct {
783785 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
784786 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
785787 if (module.unwind_info) |unwind_info| {
786 if (DW.unwindFrameMachO(&unwind_state.dwarf_context, &it.ma, unwind_info, module.eh_frame, module.base_address)) |return_address| {
788 if (Dwarf.unwindFrameMachO(&unwind_state.dwarf_context, &it.ma, unwind_info, module.eh_frame, module.base_address)) |return_address| {
787789 return return_address;
788790 } else |err| {
789791 if (err != error.RequiresDWARFUnwind) return err;
......@@ -1140,10 +1142,10 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
11401142
11411143 if (coff_obj.getSectionByName(".debug_info")) |_| {
11421144 // This coff file has embedded DWARF debug info
1143 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
1145 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
11441146 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
11451147
1146 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
1148 inline for (@typeInfo(Dwarf.Section.Id).Enum.fields, 0..) |section, i| {
11471149 sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: {
11481150 break :blk .{
11491151 .data = try coff_obj.getSectionDataAlloc(section_header, allocator),
......@@ -1153,13 +1155,13 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
11531155 } else null;
11541156 }
11551157
1156 var dwarf = DW.DwarfInfo{
1158 var dwarf = Dwarf{
11571159 .endian = native_endian,
11581160 .sections = sections,
11591161 .is_macho = false,
11601162 };
11611163
1162 try DW.openDwarfDebugInfo(&dwarf, allocator);
1164 try Dwarf.open(&dwarf, allocator);
11631165 di.dwarf = dwarf;
11641166 }
11651167
......@@ -1211,7 +1213,7 @@ pub fn readElfDebugInfo(
12111213 elf_filename: ?[]const u8,
12121214 build_id: ?[]const u8,
12131215 expected_crc: ?u32,
1214 parent_sections: *DW.DwarfInfo.SectionArray,
1216 parent_sections: *Dwarf.SectionArray,
12151217 parent_mapped_mem: ?[]align(mem.page_size) const u8,
12161218) !ModuleDebugInfo {
12171219 nosuspend {
......@@ -1245,7 +1247,7 @@ pub fn readElfDebugInfo(
12451247 @ptrCast(@alignCast(&mapped_mem[shoff])),
12461248 )[0..hdr.e_shnum];
12471249
1248 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
1250 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
12491251
12501252 // Combine section list. This takes ownership over any owned sections from the parent scope.
12511253 for (parent_sections, &sections) |*parent, *section| {
......@@ -1274,7 +1276,7 @@ pub fn readElfDebugInfo(
12741276 }
12751277
12761278 var section_index: ?usize = null;
1277 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
1279 inline for (@typeInfo(Dwarf.Section.Id).Enum.fields, 0..) |section, i| {
12781280 if (mem.eql(u8, "." ++ section.name, name)) section_index = i;
12791281 }
12801282 if (section_index == null) continue;
......@@ -1308,10 +1310,10 @@ pub fn readElfDebugInfo(
13081310 }
13091311
13101312 const missing_debug_info =
1311 sections[@intFromEnum(DW.DwarfSection.debug_info)] == null or
1312 sections[@intFromEnum(DW.DwarfSection.debug_abbrev)] == null or
1313 sections[@intFromEnum(DW.DwarfSection.debug_str)] == null or
1314 sections[@intFromEnum(DW.DwarfSection.debug_line)] == null;
1313 sections[@intFromEnum(Dwarf.Section.Id.debug_info)] == null or
1314 sections[@intFromEnum(Dwarf.Section.Id.debug_abbrev)] == null or
1315 sections[@intFromEnum(Dwarf.Section.Id.debug_str)] == null or
1316 sections[@intFromEnum(Dwarf.Section.Id.debug_line)] == null;
13151317
13161318 // Attempt to load debug info from an external file
13171319 // See: https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
......@@ -1379,13 +1381,13 @@ pub fn readElfDebugInfo(
13791381 return error.MissingDebugInfo;
13801382 }
13811383
1382 var di = DW.DwarfInfo{
1384 var di = Dwarf{
13831385 .endian = endian,
13841386 .sections = sections,
13851387 .is_macho = false,
13861388 };
13871389
1388 try DW.openDwarfDebugInfo(&di, allocator);
1390 try Dwarf.open(&di, allocator);
13891391
13901392 return ModuleDebugInfo{
13911393 .base_address = undefined,
......@@ -2168,13 +2170,13 @@ pub const Info = struct {
21682170 const obj_di = try self.allocator.create(ModuleDebugInfo);
21692171 errdefer self.allocator.destroy(obj_di);
21702172
2171 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
2173 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
21722174 if (ctx.gnu_eh_frame) |eh_frame_hdr| {
21732175 // This is a special case - pointer offsets inside .eh_frame_hdr
21742176 // are encoded relative to its base address, so we must use the
21752177 // version that is already memory mapped, and not the one that
21762178 // will be mapped separately from the ELF file.
2177 sections[@intFromEnum(DW.DwarfSection.eh_frame_hdr)] = .{
2179 sections[@intFromEnum(Dwarf.Section.Id.eh_frame_hdr)] = .{
21782180 .data = eh_frame_hdr,
21792181 .owned = false,
21802182 };
......@@ -2219,7 +2221,7 @@ pub const ModuleDebugInfo = switch (native_os) {
22192221
22202222 const OFileTable = std.StringHashMap(OFileInfo);
22212223 const OFileInfo = struct {
2222 di: DW.DwarfInfo,
2224 di: Dwarf,
22232225 addr_table: std.StringHashMap(u64),
22242226 };
22252227
......@@ -2278,8 +2280,8 @@ pub const ModuleDebugInfo = switch (native_os) {
22782280 addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value);
22792281 }
22802282
2281 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
2282 if (self.eh_frame) |eh_frame| sections[@intFromEnum(DW.DwarfSection.eh_frame)] = .{
2283 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
2284 if (self.eh_frame) |eh_frame| sections[@intFromEnum(Dwarf.Section.Id.eh_frame)] = .{
22832285 .data = eh_frame,
22842286 .owned = false,
22852287 };
......@@ -2288,7 +2290,7 @@ pub const ModuleDebugInfo = switch (native_os) {
22882290 if (!std.mem.eql(u8, "__DWARF", sect.segName())) continue;
22892291
22902292 var section_index: ?usize = null;
2291 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
2293 inline for (@typeInfo(Dwarf.Section.Id).Enum.fields, 0..) |section, i| {
22922294 if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;
22932295 }
22942296 if (section_index == null) continue;
......@@ -2302,19 +2304,19 @@ pub const ModuleDebugInfo = switch (native_os) {
23022304 }
23032305
23042306 const missing_debug_info =
2305 sections[@intFromEnum(DW.DwarfSection.debug_info)] == null or
2306 sections[@intFromEnum(DW.DwarfSection.debug_abbrev)] == null or
2307 sections[@intFromEnum(DW.DwarfSection.debug_str)] == null or
2308 sections[@intFromEnum(DW.DwarfSection.debug_line)] == null;
2307 sections[@intFromEnum(Dwarf.Section.Id.debug_info)] == null or
2308 sections[@intFromEnum(Dwarf.Section.Id.debug_abbrev)] == null or
2309 sections[@intFromEnum(Dwarf.Section.Id.debug_str)] == null or
2310 sections[@intFromEnum(Dwarf.Section.Id.debug_line)] == null;
23092311 if (missing_debug_info) return error.MissingDebugInfo;
23102312
2311 var di = DW.DwarfInfo{
2313 var di = Dwarf{
23122314 .endian = .little,
23132315 .sections = sections,
23142316 .is_macho = true,
23152317 };
23162318
2317 try DW.openDwarfDebugInfo(&di, allocator);
2319 try Dwarf.open(&di, allocator);
23182320 const info = OFileInfo{
23192321 .di = di,
23202322 .addr_table = addr_table,
......@@ -2411,14 +2413,14 @@ pub const ModuleDebugInfo = switch (native_os) {
24112413 }
24122414 }
24132415
2414 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {
2416 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const Dwarf {
24152417 return if ((try self.getOFileInfoForAddress(allocator, address)).o_file_info) |o_file_info| &o_file_info.di else null;
24162418 }
24172419 },
24182420 .uefi, .windows => struct {
24192421 base_address: usize,
24202422 pdb: ?pdb.Pdb = null,
2421 dwarf: ?DW.DwarfInfo = null,
2423 dwarf: ?Dwarf = null,
24222424 coff_image_base: u64,
24232425
24242426 /// Only used if pdb is non-null
......@@ -2488,7 +2490,7 @@ pub const ModuleDebugInfo = switch (native_os) {
24882490 return SymbolInfo{};
24892491 }
24902492
2491 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {
2493 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const Dwarf {
24922494 _ = allocator;
24932495 _ = address;
24942496
......@@ -2500,7 +2502,7 @@ pub const ModuleDebugInfo = switch (native_os) {
25002502 },
25012503 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => struct {
25022504 base_address: usize,
2503 dwarf: DW.DwarfInfo,
2505 dwarf: Dwarf,
25042506 mapped_memory: []align(mem.page_size) const u8,
25052507 external_mapped_memory: ?[]align(mem.page_size) const u8,
25062508
......@@ -2516,7 +2518,7 @@ pub const ModuleDebugInfo = switch (native_os) {
25162518 return getSymbolFromDwarf(allocator, relocated_address, &self.dwarf);
25172519 }
25182520
2519 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {
2521 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const Dwarf {
25202522 _ = allocator;
25212523 _ = address;
25222524 return &self.dwarf;
......@@ -2535,17 +2537,17 @@ pub const ModuleDebugInfo = switch (native_os) {
25352537 return SymbolInfo{};
25362538 }
25372539
2538 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {
2540 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const Dwarf {
25392541 _ = self;
25402542 _ = allocator;
25412543 _ = address;
25422544 return null;
25432545 }
25442546 },
2545 else => DW.DwarfInfo,
2547 else => Dwarf,
25462548};
25472549
2548fn getSymbolFromDwarf(allocator: mem.Allocator, address: u64, di: *DW.DwarfInfo) !SymbolInfo {
2550fn getSymbolFromDwarf(allocator: mem.Allocator, address: u64, di: *Dwarf) !SymbolInfo {
25492551 if (nosuspend di.findCompileUnit(address)) |compile_unit| {
25502552 return SymbolInfo{
25512553 .symbol_name = nosuspend di.getSymbolName(address) orelse "???",
lib/std/debug/Dwarf.zig created+2709
......@@ -0,0 +1,2709 @@
1//! Implements parsing, decoding, and caching of DWARF information.
2//!
3//! For unopinionated types and bits, see `std.dwarf`.
4
5const builtin = @import("builtin");
6const std = @import("../std.zig");
7const AT = DW.AT;
8const Allocator = std.mem.Allocator;
9const DW = std.dwarf;
10const EH = DW.EH;
11const FORM = DW.FORM;
12const Format = DW.Format;
13const RLE = DW.RLE;
14const StackIterator = std.debug.StackIterator;
15const UT = DW.UT;
16const assert = std.debug.assert;
17const cast = std.math.cast;
18const maxInt = std.math.maxInt;
19const native_endian = builtin.cpu.arch.endian();
20const readInt = std.mem.readInt;
21
22const Dwarf = @This();
23
24pub const expression = @import("Dwarf/expression.zig");
25pub const abi = @import("Dwarf/abi.zig");
26pub const call_frame = @import("Dwarf/call_frame.zig");
27
28endian: std.builtin.Endian,
29sections: SectionArray = null_section_array,
30is_macho: bool,
31
32// Filled later by the initializer
33abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{},
34compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
35func_list: std.ArrayListUnmanaged(Func) = .{},
36
37eh_frame_hdr: ?ExceptionFrameHeader = null,
38// These lookup tables are only used if `eh_frame_hdr` is null
39cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{},
40// Sorted by start_pc
41fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},
42
43pub const Section = struct {
44 data: []const u8,
45 // Module-relative virtual address.
46 // Only set if the section data was loaded from disk.
47 virtual_address: ?usize = null,
48 // If `data` is owned by this Dwarf.
49 owned: bool,
50
51 pub const Id = enum {
52 debug_info,
53 debug_abbrev,
54 debug_str,
55 debug_str_offsets,
56 debug_line,
57 debug_line_str,
58 debug_ranges,
59 debug_loclists,
60 debug_rnglists,
61 debug_addr,
62 debug_names,
63 debug_frame,
64 eh_frame,
65 eh_frame_hdr,
66 };
67
68 // For sections that are not memory mapped by the loader, this is an offset
69 // from `data.ptr` to where the section would have been mapped. Otherwise,
70 // `data` is directly backed by the section and the offset is zero.
71 pub fn virtualOffset(self: Section, base_address: usize) i64 {
72 return if (self.virtual_address) |va|
73 @as(i64, @intCast(base_address + va)) -
74 @as(i64, @intCast(@intFromPtr(self.data.ptr)))
75 else
76 0;
77 }
78};
79
80pub const Abbrev = struct {
81 code: u64,
82 tag_id: u64,
83 has_children: bool,
84 attrs: []Attr,
85
86 fn deinit(abbrev: *Abbrev, allocator: Allocator) void {
87 allocator.free(abbrev.attrs);
88 abbrev.* = undefined;
89 }
90
91 const Attr = struct {
92 id: u64,
93 form_id: u64,
94 /// Only valid if form_id is .implicit_const
95 payload: i64,
96 };
97
98 const Table = struct {
99 // offset from .debug_abbrev
100 offset: u64,
101 abbrevs: []Abbrev,
102
103 fn deinit(table: *Table, allocator: Allocator) void {
104 for (table.abbrevs) |*abbrev| {
105 abbrev.deinit(allocator);
106 }
107 allocator.free(table.abbrevs);
108 table.* = undefined;
109 }
110
111 fn get(table: *const Table, abbrev_code: u64) ?*const Abbrev {
112 return for (table.abbrevs) |*abbrev| {
113 if (abbrev.code == abbrev_code) break abbrev;
114 } else null;
115 }
116 };
117};
118
119pub const CompileUnit = struct {
120 version: u16,
121 format: Format,
122 die: Die,
123 pc_range: ?PcRange,
124
125 str_offsets_base: usize,
126 addr_base: usize,
127 rnglists_base: usize,
128 loclists_base: usize,
129 frame_base: ?*const FormValue,
130};
131
132pub const FormValue = union(enum) {
133 addr: u64,
134 addrx: usize,
135 block: []const u8,
136 udata: u64,
137 data16: *const [16]u8,
138 sdata: i64,
139 exprloc: []const u8,
140 flag: bool,
141 sec_offset: u64,
142 ref: u64,
143 ref_addr: u64,
144 string: [:0]const u8,
145 strp: u64,
146 strx: usize,
147 line_strp: u64,
148 loclistx: u64,
149 rnglistx: u64,
150
151 fn getString(fv: FormValue, di: Dwarf) ![:0]const u8 {
152 switch (fv) {
153 .string => |s| return s,
154 .strp => |off| return di.getString(off),
155 .line_strp => |off| return di.getLineString(off),
156 else => return badDwarf(),
157 }
158 }
159
160 fn getUInt(fv: FormValue, comptime U: type) !U {
161 return switch (fv) {
162 inline .udata,
163 .sdata,
164 .sec_offset,
165 => |c| cast(U, c) orelse badDwarf(),
166 else => badDwarf(),
167 };
168 }
169};
170
171pub const Die = struct {
172 tag_id: u64,
173 has_children: bool,
174 attrs: []Attr,
175
176 const Attr = struct {
177 id: u64,
178 value: FormValue,
179 };
180
181 fn deinit(self: *Die, allocator: Allocator) void {
182 allocator.free(self.attrs);
183 self.* = undefined;
184 }
185
186 fn getAttr(self: *const Die, id: u64) ?*const FormValue {
187 for (self.attrs) |*attr| {
188 if (attr.id == id) return &attr.value;
189 }
190 return null;
191 }
192
193 fn getAttrAddr(
194 self: *const Die,
195 di: *const Dwarf,
196 id: u64,
197 compile_unit: CompileUnit,
198 ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
199 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
200 return switch (form_value.*) {
201 .addr => |value| value,
202 .addrx => |index| di.readDebugAddr(compile_unit, index),
203 else => error.InvalidDebugInfo,
204 };
205 }
206
207 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
208 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
209 return form_value.getUInt(u64);
210 }
211
212 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
213 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
214 return switch (form_value.*) {
215 .Const => |value| value.asUnsignedLe(),
216 else => error.InvalidDebugInfo,
217 };
218 }
219
220 fn getAttrRef(self: *const Die, id: u64) !u64 {
221 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
222 return switch (form_value.*) {
223 .ref => |value| value,
224 else => error.InvalidDebugInfo,
225 };
226 }
227
228 pub fn getAttrString(
229 self: *const Die,
230 di: *Dwarf,
231 id: u64,
232 opt_str: ?[]const u8,
233 compile_unit: CompileUnit,
234 ) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {
235 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
236 switch (form_value.*) {
237 .string => |value| return value,
238 .strp => |offset| return di.getString(offset),
239 .strx => |index| {
240 const debug_str_offsets = di.section(.debug_str_offsets) orelse return badDwarf();
241 if (compile_unit.str_offsets_base == 0) return badDwarf();
242 switch (compile_unit.format) {
243 .@"32" => {
244 const byte_offset = compile_unit.str_offsets_base + 4 * index;
245 if (byte_offset + 4 > debug_str_offsets.len) return badDwarf();
246 const offset = readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);
247 return getStringGeneric(opt_str, offset);
248 },
249 .@"64" => {
250 const byte_offset = compile_unit.str_offsets_base + 8 * index;
251 if (byte_offset + 8 > debug_str_offsets.len) return badDwarf();
252 const offset = readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);
253 return getStringGeneric(opt_str, offset);
254 },
255 }
256 },
257 .line_strp => |offset| return di.getLineString(offset),
258 else => return badDwarf(),
259 }
260 }
261};
262
263/// This represents the decoded .eh_frame_hdr header
264pub const ExceptionFrameHeader = struct {
265 eh_frame_ptr: usize,
266 table_enc: u8,
267 fde_count: usize,
268 entries: []const u8,
269
270 pub fn entrySize(table_enc: u8) !u8 {
271 return switch (table_enc & EH.PE.type_mask) {
272 EH.PE.udata2,
273 EH.PE.sdata2,
274 => 4,
275 EH.PE.udata4,
276 EH.PE.sdata4,
277 => 8,
278 EH.PE.udata8,
279 EH.PE.sdata8,
280 => 16,
281 // This is a binary search table, so all entries must be the same length
282 else => return badDwarf(),
283 };
284 }
285
286 fn isValidPtr(
287 self: ExceptionFrameHeader,
288 comptime T: type,
289 ptr: usize,
290 ma: *StackIterator.MemoryAccessor,
291 eh_frame_len: ?usize,
292 ) bool {
293 if (eh_frame_len) |len| {
294 return ptr >= self.eh_frame_ptr and ptr <= self.eh_frame_ptr + len - @sizeOf(T);
295 } else {
296 return ma.load(T, ptr) != null;
297 }
298 }
299
300 /// Find an entry by binary searching the eh_frame_hdr section.
301 ///
302 /// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller,
303 /// MemoryAccessor will be used to verify readability of the header entries.
304 /// If `eh_frame_len` is provided, then these checks can be skipped.
305 pub fn findEntry(
306 self: ExceptionFrameHeader,
307 ma: *StackIterator.MemoryAccessor,
308 eh_frame_len: ?usize,
309 eh_frame_hdr_ptr: usize,
310 pc: usize,
311 cie: *CommonInformationEntry,
312 fde: *FrameDescriptionEntry,
313 ) !void {
314 const entry_size = try entrySize(self.table_enc);
315
316 var left: usize = 0;
317 var len: usize = self.fde_count;
318
319 var fbr: FixedBufferReader = .{ .buf = self.entries, .endian = native_endian };
320
321 while (len > 1) {
322 const mid = left + len / 2;
323
324 fbr.pos = mid * entry_size;
325 const pc_begin = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
326 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
327 .follow_indirect = true,
328 .data_rel_base = eh_frame_hdr_ptr,
329 }) orelse return badDwarf();
330
331 if (pc < pc_begin) {
332 len /= 2;
333 } else {
334 left = mid;
335 if (pc == pc_begin) break;
336 len -= len / 2;
337 }
338 }
339
340 if (len == 0) return badDwarf();
341 fbr.pos = left * entry_size;
342
343 // Read past the pc_begin field of the entry
344 _ = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
345 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
346 .follow_indirect = true,
347 .data_rel_base = eh_frame_hdr_ptr,
348 }) orelse return badDwarf();
349
350 const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
351 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
352 .follow_indirect = true,
353 .data_rel_base = eh_frame_hdr_ptr,
354 }) orelse return badDwarf()) orelse return badDwarf();
355
356 if (fde_ptr < self.eh_frame_ptr) return badDwarf();
357
358 // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor
359 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)];
360
361 const fde_offset = fde_ptr - self.eh_frame_ptr;
362 var eh_frame_fbr: FixedBufferReader = .{
363 .buf = eh_frame,
364 .pos = fde_offset,
365 .endian = native_endian,
366 };
367
368 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
369 if (!self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf();
370 if (fde_entry_header.type != .fde) return badDwarf();
371
372 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
373 const cie_offset = fde_entry_header.type.fde;
374 try eh_frame_fbr.seekTo(cie_offset);
375 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
376 if (!self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf();
377 if (cie_entry_header.type != .cie) return badDwarf();
378
379 cie.* = try CommonInformationEntry.parse(
380 cie_entry_header.entry_bytes,
381 0,
382 true,
383 cie_entry_header.format,
384 .eh_frame,
385 cie_entry_header.length_offset,
386 @sizeOf(usize),
387 native_endian,
388 );
389
390 fde.* = try FrameDescriptionEntry.parse(
391 fde_entry_header.entry_bytes,
392 0,
393 true,
394 cie.*,
395 @sizeOf(usize),
396 native_endian,
397 );
398 }
399};
400
401pub const EntryHeader = struct {
402 /// Offset of the length field in the backing buffer
403 length_offset: usize,
404 format: Format,
405 type: union(enum) {
406 cie,
407 /// Value is the offset of the corresponding CIE
408 fde: u64,
409 terminator,
410 },
411 /// The entry's contents, not including the ID field
412 entry_bytes: []const u8,
413
414 /// The length of the entry including the ID field, but not the length field itself
415 pub fn entryLength(self: EntryHeader) usize {
416 return self.entry_bytes.len + @as(u8, if (self.format == .@"64") 8 else 4);
417 }
418
419 /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure.
420 /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections.
421 pub fn read(
422 fbr: *FixedBufferReader,
423 opt_ma: ?*StackIterator.MemoryAccessor,
424 dwarf_section: Section.Id,
425 ) !EntryHeader {
426 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
427
428 const length_offset = fbr.pos;
429 const unit_header = try readUnitHeader(fbr, opt_ma);
430 const unit_length = cast(usize, unit_header.unit_length) orelse return badDwarf();
431 if (unit_length == 0) return .{
432 .length_offset = length_offset,
433 .format = unit_header.format,
434 .type = .terminator,
435 .entry_bytes = &.{},
436 };
437 const start_offset = fbr.pos;
438 const end_offset = start_offset + unit_length;
439 defer fbr.pos = end_offset;
440
441 const id = try if (opt_ma) |ma|
442 fbr.readAddressChecked(unit_header.format, ma)
443 else
444 fbr.readAddress(unit_header.format);
445 const entry_bytes = fbr.buf[fbr.pos..end_offset];
446 const cie_id: u64 = switch (dwarf_section) {
447 .eh_frame => CommonInformationEntry.eh_id,
448 .debug_frame => switch (unit_header.format) {
449 .@"32" => CommonInformationEntry.dwarf32_id,
450 .@"64" => CommonInformationEntry.dwarf64_id,
451 },
452 else => unreachable,
453 };
454
455 return .{
456 .length_offset = length_offset,
457 .format = unit_header.format,
458 .type = if (id == cie_id) .cie else .{ .fde = switch (dwarf_section) {
459 .eh_frame => try std.math.sub(u64, start_offset, id),
460 .debug_frame => id,
461 else => unreachable,
462 } },
463 .entry_bytes = entry_bytes,
464 };
465 }
466};
467
468pub const CommonInformationEntry = struct {
469 // Used in .eh_frame
470 pub const eh_id = 0;
471
472 // Used in .debug_frame (DWARF32)
473 pub const dwarf32_id = maxInt(u32);
474
475 // Used in .debug_frame (DWARF64)
476 pub const dwarf64_id = maxInt(u64);
477
478 // Offset of the length field of this entry in the eh_frame section.
479 // This is the key that FDEs use to reference CIEs.
480 length_offset: u64,
481 version: u8,
482 address_size: u8,
483 format: Format,
484
485 // Only present in version 4
486 segment_selector_size: ?u8,
487
488 code_alignment_factor: u32,
489 data_alignment_factor: i32,
490 return_address_register: u8,
491
492 aug_str: []const u8,
493 aug_data: []const u8,
494 lsda_pointer_enc: u8,
495 personality_enc: ?u8,
496 personality_routine_pointer: ?u64,
497 fde_pointer_enc: u8,
498 initial_instructions: []const u8,
499
500 pub fn isSignalFrame(self: CommonInformationEntry) bool {
501 for (self.aug_str) |c| if (c == 'S') return true;
502 return false;
503 }
504
505 pub fn addressesSignedWithBKey(self: CommonInformationEntry) bool {
506 for (self.aug_str) |c| if (c == 'B') return true;
507 return false;
508 }
509
510 pub fn mteTaggedFrame(self: CommonInformationEntry) bool {
511 for (self.aug_str) |c| if (c == 'G') return true;
512 return false;
513 }
514
515 /// This function expects to read the CIE starting with the version field.
516 /// The returned struct references memory backed by cie_bytes.
517 ///
518 /// See the FrameDescriptionEntry.parse documentation for the description
519 /// of `pc_rel_offset` and `is_runtime`.
520 ///
521 /// `length_offset` specifies the offset of this CIE's length field in the
522 /// .eh_frame / .debug_frame section.
523 pub fn parse(
524 cie_bytes: []const u8,
525 pc_rel_offset: i64,
526 is_runtime: bool,
527 format: Format,
528 dwarf_section: Section.Id,
529 length_offset: u64,
530 addr_size_bytes: u8,
531 endian: std.builtin.Endian,
532 ) !CommonInformationEntry {
533 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;
534
535 var fbr: FixedBufferReader = .{ .buf = cie_bytes, .endian = endian };
536
537 const version = try fbr.readByte();
538 switch (dwarf_section) {
539 .eh_frame => if (version != 1 and version != 3) return error.UnsupportedDwarfVersion,
540 .debug_frame => if (version != 4) return error.UnsupportedDwarfVersion,
541 else => return error.UnsupportedDwarfSection,
542 }
543
544 var has_eh_data = false;
545 var has_aug_data = false;
546
547 var aug_str_len: usize = 0;
548 const aug_str_start = fbr.pos;
549 var aug_byte = try fbr.readByte();
550 while (aug_byte != 0) : (aug_byte = try fbr.readByte()) {
551 switch (aug_byte) {
552 'z' => {
553 if (aug_str_len != 0) return badDwarf();
554 has_aug_data = true;
555 },
556 'e' => {
557 if (has_aug_data or aug_str_len != 0) return badDwarf();
558 if (try fbr.readByte() != 'h') return badDwarf();
559 has_eh_data = true;
560 },
561 else => if (has_eh_data) return badDwarf(),
562 }
563
564 aug_str_len += 1;
565 }
566
567 if (has_eh_data) {
568 // legacy data created by older versions of gcc - unsupported here
569 for (0..addr_size_bytes) |_| _ = try fbr.readByte();
570 }
571
572 const address_size = if (version == 4) try fbr.readByte() else addr_size_bytes;
573 const segment_selector_size = if (version == 4) try fbr.readByte() else null;
574
575 const code_alignment_factor = try fbr.readUleb128(u32);
576 const data_alignment_factor = try fbr.readIleb128(i32);
577 const return_address_register = if (version == 1) try fbr.readByte() else try fbr.readUleb128(u8);
578
579 var lsda_pointer_enc: u8 = EH.PE.omit;
580 var personality_enc: ?u8 = null;
581 var personality_routine_pointer: ?u64 = null;
582 var fde_pointer_enc: u8 = EH.PE.absptr;
583
584 var aug_data: []const u8 = &[_]u8{};
585 const aug_str = if (has_aug_data) blk: {
586 const aug_data_len = try fbr.readUleb128(usize);
587 const aug_data_start = fbr.pos;
588 aug_data = cie_bytes[aug_data_start..][0..aug_data_len];
589
590 const aug_str = cie_bytes[aug_str_start..][0..aug_str_len];
591 for (aug_str[1..]) |byte| {
592 switch (byte) {
593 'L' => {
594 lsda_pointer_enc = try fbr.readByte();
595 },
596 'P' => {
597 personality_enc = try fbr.readByte();
598 personality_routine_pointer = try readEhPointer(&fbr, personality_enc.?, addr_size_bytes, .{
599 .pc_rel_base = try pcRelBase(@intFromPtr(&cie_bytes[fbr.pos]), pc_rel_offset),
600 .follow_indirect = is_runtime,
601 });
602 },
603 'R' => {
604 fde_pointer_enc = try fbr.readByte();
605 },
606 'S', 'B', 'G' => {},
607 else => return badDwarf(),
608 }
609 }
610
611 // aug_data_len can include padding so the CIE ends on an address boundary
612 fbr.pos = aug_data_start + aug_data_len;
613 break :blk aug_str;
614 } else &[_]u8{};
615
616 const initial_instructions = cie_bytes[fbr.pos..];
617 return .{
618 .length_offset = length_offset,
619 .version = version,
620 .address_size = address_size,
621 .format = format,
622 .segment_selector_size = segment_selector_size,
623 .code_alignment_factor = code_alignment_factor,
624 .data_alignment_factor = data_alignment_factor,
625 .return_address_register = return_address_register,
626 .aug_str = aug_str,
627 .aug_data = aug_data,
628 .lsda_pointer_enc = lsda_pointer_enc,
629 .personality_enc = personality_enc,
630 .personality_routine_pointer = personality_routine_pointer,
631 .fde_pointer_enc = fde_pointer_enc,
632 .initial_instructions = initial_instructions,
633 };
634 }
635};
636
637pub const FrameDescriptionEntry = struct {
638 // Offset into eh_frame where the CIE for this FDE is stored
639 cie_length_offset: u64,
640
641 pc_begin: u64,
642 pc_range: u64,
643 lsda_pointer: ?u64,
644 aug_data: []const u8,
645 instructions: []const u8,
646
647 /// This function expects to read the FDE starting at the PC Begin field.
648 /// The returned struct references memory backed by `fde_bytes`.
649 ///
650 /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values
651 /// used when decoding pointers. This should be set to zero if fde_bytes is
652 /// backed by the memory of a .eh_frame / .debug_frame section in the running executable.
653 /// Otherwise, it should be the relative offset to translate addresses from
654 /// where the section is currently stored in memory, to where it *would* be
655 /// stored at runtime: section base addr - backing data base ptr.
656 ///
657 /// Similarly, `is_runtime` specifies this function is being called on a runtime
658 /// section, and so indirect pointers can be followed.
659 pub fn parse(
660 fde_bytes: []const u8,
661 pc_rel_offset: i64,
662 is_runtime: bool,
663 cie: CommonInformationEntry,
664 addr_size_bytes: u8,
665 endian: std.builtin.Endian,
666 ) !FrameDescriptionEntry {
667 if (addr_size_bytes > 8) return error.InvalidAddrSize;
668
669 var fbr: FixedBufferReader = .{ .buf = fde_bytes, .endian = endian };
670
671 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
672 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),
673 .follow_indirect = is_runtime,
674 }) orelse return badDwarf();
675
676 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
677 .pc_rel_base = 0,
678 .follow_indirect = false,
679 }) orelse return badDwarf();
680
681 var aug_data: []const u8 = &[_]u8{};
682 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
683 const aug_data_len = try fbr.readUleb128(usize);
684 const aug_data_start = fbr.pos;
685 aug_data = fde_bytes[aug_data_start..][0..aug_data_len];
686
687 const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit)
688 try readEhPointer(&fbr, cie.lsda_pointer_enc, addr_size_bytes, .{
689 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),
690 .follow_indirect = is_runtime,
691 })
692 else
693 null;
694
695 fbr.pos = aug_data_start + aug_data_len;
696 break :blk lsda_pointer;
697 } else null;
698
699 const instructions = fde_bytes[fbr.pos..];
700 return .{
701 .cie_length_offset = cie.length_offset,
702 .pc_begin = pc_begin,
703 .pc_range = pc_range,
704 .lsda_pointer = lsda_pointer,
705 .aug_data = aug_data,
706 .instructions = instructions,
707 };
708 }
709};
710
711pub const UnwindContext = struct {
712 allocator: Allocator,
713 cfa: ?usize,
714 pc: usize,
715 thread_context: *std.debug.ThreadContext,
716 reg_context: abi.RegisterContext,
717 vm: call_frame.VirtualMachine,
718 stack_machine: expression.StackMachine(.{ .call_frame_context = true }),
719
720 pub fn init(
721 allocator: Allocator,
722 thread_context: *const std.debug.ThreadContext,
723 ) !UnwindContext {
724 const pc = abi.stripInstructionPtrAuthCode(
725 (try abi.regValueNative(
726 usize,
727 thread_context,
728 abi.ipRegNum(),
729 null,
730 )).*,
731 );
732
733 const context_copy = try allocator.create(std.debug.ThreadContext);
734 std.debug.copyContext(thread_context, context_copy);
735
736 return .{
737 .allocator = allocator,
738 .cfa = null,
739 .pc = pc,
740 .thread_context = context_copy,
741 .reg_context = undefined,
742 .vm = .{},
743 .stack_machine = .{},
744 };
745 }
746
747 pub fn deinit(self: *UnwindContext) void {
748 self.vm.deinit(self.allocator);
749 self.stack_machine.deinit(self.allocator);
750 self.allocator.destroy(self.thread_context);
751 self.* = undefined;
752 }
753
754 pub fn getFp(self: *const UnwindContext) !usize {
755 return (try abi.regValueNative(usize, self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context)).*;
756 }
757};
758
759const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);
760pub const SectionArray = [num_sections]?Section;
761pub const null_section_array = [_]?Section{null} ** num_sections;
762
763/// Initialize DWARF info. The caller has the responsibility to initialize most
764/// the `Dwarf` fields before calling. `binary_mem` is the raw bytes of the
765/// main binary file (not the secondary debug info file).
766pub fn open(di: *Dwarf, allocator: Allocator) !void {
767 try di.scanAllFunctions(allocator);
768 try di.scanAllCompileUnits(allocator);
769}
770
771const PcRange = struct {
772 start: u64,
773 end: u64,
774};
775
776const Func = struct {
777 pc_range: ?PcRange,
778 name: ?[]const u8,
779};
780
781pub fn section(di: Dwarf, dwarf_section: Section.Id) ?[]const u8 {
782 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.data else null;
783}
784
785pub fn sectionVirtualOffset(di: Dwarf, dwarf_section: Section.Id, base_address: usize) ?i64 {
786 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.virtualOffset(base_address) else null;
787}
788
789pub fn deinit(di: *Dwarf, allocator: Allocator) void {
790 for (di.sections) |opt_section| {
791 if (opt_section) |s| if (s.owned) allocator.free(s.data);
792 }
793 for (di.abbrev_table_list.items) |*abbrev| {
794 abbrev.deinit(allocator);
795 }
796 di.abbrev_table_list.deinit(allocator);
797 for (di.compile_unit_list.items) |*cu| {
798 cu.die.deinit(allocator);
799 }
800 di.compile_unit_list.deinit(allocator);
801 di.func_list.deinit(allocator);
802 di.cie_map.deinit(allocator);
803 di.fde_list.deinit(allocator);
804 di.* = undefined;
805}
806
807pub fn getSymbolName(di: *Dwarf, address: u64) ?[]const u8 {
808 for (di.func_list.items) |*func| {
809 if (func.pc_range) |range| {
810 if (address >= range.start and address < range.end) {
811 return func.name;
812 }
813 }
814 }
815
816 return null;
817}
818
819fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
820 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
821 var this_unit_offset: u64 = 0;
822
823 while (this_unit_offset < fbr.buf.len) {
824 try fbr.seekTo(this_unit_offset);
825
826 const unit_header = try readUnitHeader(&fbr, null);
827 if (unit_header.unit_length == 0) return;
828 const next_offset = unit_header.header_length + unit_header.unit_length;
829
830 const version = try fbr.readInt(u16);
831 if (version < 2 or version > 5) return badDwarf();
832
833 var address_size: u8 = undefined;
834 var debug_abbrev_offset: u64 = undefined;
835 if (version >= 5) {
836 const unit_type = try fbr.readInt(u8);
837 if (unit_type != DW.UT.compile) return badDwarf();
838 address_size = try fbr.readByte();
839 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
840 } else {
841 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
842 address_size = try fbr.readByte();
843 }
844 if (address_size != @sizeOf(usize)) return badDwarf();
845
846 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
847
848 var max_attrs: usize = 0;
849 var zig_padding_abbrev_code: u7 = 0;
850 for (abbrev_table.abbrevs) |abbrev| {
851 max_attrs = @max(max_attrs, abbrev.attrs.len);
852 if (cast(u7, abbrev.code)) |code| {
853 if (abbrev.tag_id == DW.TAG.ZIG_padding and
854 !abbrev.has_children and
855 abbrev.attrs.len == 0)
856 {
857 zig_padding_abbrev_code = code;
858 }
859 }
860 }
861 const attrs_buf = try allocator.alloc(Die.Attr, max_attrs * 3);
862 defer allocator.free(attrs_buf);
863 var attrs_bufs: [3][]Die.Attr = undefined;
864 for (&attrs_bufs, 0..) |*buf, index| buf.* = attrs_buf[index * max_attrs ..][0..max_attrs];
865
866 const next_unit_pos = this_unit_offset + next_offset;
867
868 var compile_unit: CompileUnit = .{
869 .version = version,
870 .format = unit_header.format,
871 .die = undefined,
872 .pc_range = null,
873
874 .str_offsets_base = 0,
875 .addr_base = 0,
876 .rnglists_base = 0,
877 .loclists_base = 0,
878 .frame_base = null,
879 };
880
881 while (true) {
882 fbr.pos = std.mem.indexOfNonePos(u8, fbr.buf, fbr.pos, &.{
883 zig_padding_abbrev_code, 0,
884 }) orelse fbr.buf.len;
885 if (fbr.pos >= next_unit_pos) break;
886 var die_obj = (try parseDie(
887 &fbr,
888 attrs_bufs[0],
889 abbrev_table,
890 unit_header.format,
891 )) orelse continue;
892
893 switch (die_obj.tag_id) {
894 DW.TAG.compile_unit => {
895 compile_unit.die = die_obj;
896 compile_unit.die.attrs = attrs_bufs[1][0..die_obj.attrs.len];
897 @memcpy(compile_unit.die.attrs, die_obj.attrs);
898
899 compile_unit.str_offsets_base = if (die_obj.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0;
900 compile_unit.addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0;
901 compile_unit.rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0;
902 compile_unit.loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0;
903 compile_unit.frame_base = die_obj.getAttr(AT.frame_base);
904 },
905 DW.TAG.subprogram, DW.TAG.inlined_subroutine, DW.TAG.subroutine, DW.TAG.entry_point => {
906 const fn_name = x: {
907 var this_die_obj = die_obj;
908 // Prevent endless loops
909 for (0..3) |_| {
910 if (this_die_obj.getAttr(AT.name)) |_| {
911 break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);
912 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
913 const after_die_offset = fbr.pos;
914 defer fbr.pos = after_die_offset;
915
916 // Follow the DIE it points to and repeat
917 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
918 if (ref_offset > next_offset) return badDwarf();
919 try fbr.seekTo(this_unit_offset + ref_offset);
920 this_die_obj = (try parseDie(
921 &fbr,
922 attrs_bufs[2],
923 abbrev_table,
924 unit_header.format,
925 )) orelse return badDwarf();
926 } else if (this_die_obj.getAttr(AT.specification)) |_| {
927 const after_die_offset = fbr.pos;
928 defer fbr.pos = after_die_offset;
929
930 // Follow the DIE it points to and repeat
931 const ref_offset = try this_die_obj.getAttrRef(AT.specification);
932 if (ref_offset > next_offset) return badDwarf();
933 try fbr.seekTo(this_unit_offset + ref_offset);
934 this_die_obj = (try parseDie(
935 &fbr,
936 attrs_bufs[2],
937 abbrev_table,
938 unit_header.format,
939 )) orelse return badDwarf();
940 } else {
941 break :x null;
942 }
943 }
944
945 break :x null;
946 };
947
948 var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
949 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
950 const pc_end = switch (high_pc_value.*) {
951 .addr => |value| value,
952 .udata => |offset| low_pc + offset,
953 else => return badDwarf(),
954 };
955
956 try di.func_list.append(allocator, .{
957 .name = fn_name,
958 .pc_range = .{
959 .start = low_pc,
960 .end = pc_end,
961 },
962 });
963
964 break :blk true;
965 }
966
967 break :blk false;
968 } else |err| blk: {
969 if (err != error.MissingDebugInfo) return err;
970 break :blk false;
971 };
972
973 if (die_obj.getAttr(AT.ranges)) |ranges_value| blk: {
974 var iter = DebugRangeIterator.init(ranges_value, di, &compile_unit) catch |err| {
975 if (err != error.MissingDebugInfo) return err;
976 break :blk;
977 };
978
979 while (try iter.next()) |range| {
980 range_added = true;
981 try di.func_list.append(allocator, .{
982 .name = fn_name,
983 .pc_range = .{
984 .start = range.start_addr,
985 .end = range.end_addr,
986 },
987 });
988 }
989 }
990
991 if (fn_name != null and !range_added) {
992 try di.func_list.append(allocator, .{
993 .name = fn_name,
994 .pc_range = null,
995 });
996 }
997 },
998 else => {},
999 }
1000 }
1001
1002 this_unit_offset += next_offset;
1003 }
1004}
1005
1006fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
1007 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
1008 var this_unit_offset: u64 = 0;
1009
1010 var attrs_buf = std.ArrayList(Die.Attr).init(allocator);
1011 defer attrs_buf.deinit();
1012
1013 while (this_unit_offset < fbr.buf.len) {
1014 try fbr.seekTo(this_unit_offset);
1015
1016 const unit_header = try readUnitHeader(&fbr, null);
1017 if (unit_header.unit_length == 0) return;
1018 const next_offset = unit_header.header_length + unit_header.unit_length;
1019
1020 const version = try fbr.readInt(u16);
1021 if (version < 2 or version > 5) return badDwarf();
1022
1023 var address_size: u8 = undefined;
1024 var debug_abbrev_offset: u64 = undefined;
1025 if (version >= 5) {
1026 const unit_type = try fbr.readInt(u8);
1027 if (unit_type != UT.compile) return badDwarf();
1028 address_size = try fbr.readByte();
1029 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
1030 } else {
1031 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
1032 address_size = try fbr.readByte();
1033 }
1034 if (address_size != @sizeOf(usize)) return badDwarf();
1035
1036 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
1037
1038 var max_attrs: usize = 0;
1039 for (abbrev_table.abbrevs) |abbrev| {
1040 max_attrs = @max(max_attrs, abbrev.attrs.len);
1041 }
1042 try attrs_buf.resize(max_attrs);
1043
1044 var compile_unit_die = (try parseDie(
1045 &fbr,
1046 attrs_buf.items,
1047 abbrev_table,
1048 unit_header.format,
1049 )) orelse return badDwarf();
1050
1051 if (compile_unit_die.tag_id != DW.TAG.compile_unit) return badDwarf();
1052
1053 compile_unit_die.attrs = try allocator.dupe(Die.Attr, compile_unit_die.attrs);
1054
1055 var compile_unit: CompileUnit = .{
1056 .version = version,
1057 .format = unit_header.format,
1058 .pc_range = null,
1059 .die = compile_unit_die,
1060 .str_offsets_base = if (compile_unit_die.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0,
1061 .addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0,
1062 .rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0,
1063 .loclists_base = if (compile_unit_die.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0,
1064 .frame_base = compile_unit_die.getAttr(AT.frame_base),
1065 };
1066
1067 compile_unit.pc_range = x: {
1068 if (compile_unit_die.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| {
1069 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
1070 const pc_end = switch (high_pc_value.*) {
1071 .addr => |value| value,
1072 .udata => |offset| low_pc + offset,
1073 else => return badDwarf(),
1074 };
1075 break :x PcRange{
1076 .start = low_pc,
1077 .end = pc_end,
1078 };
1079 } else {
1080 break :x null;
1081 }
1082 } else |err| {
1083 if (err != error.MissingDebugInfo) return err;
1084 break :x null;
1085 }
1086 };
1087
1088 try di.compile_unit_list.append(allocator, compile_unit);
1089
1090 this_unit_offset += next_offset;
1091 }
1092}
1093
1094const DebugRangeIterator = struct {
1095 base_address: u64,
1096 section_type: Section.Id,
1097 di: *const Dwarf,
1098 compile_unit: *const CompileUnit,
1099 fbr: FixedBufferReader,
1100
1101 pub fn init(ranges_value: *const FormValue, di: *const Dwarf, compile_unit: *const CompileUnit) !@This() {
1102 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;
1103 const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
1104
1105 const ranges_offset = switch (ranges_value.*) {
1106 .sec_offset, .udata => |off| off,
1107 .rnglistx => |idx| off: {
1108 switch (compile_unit.format) {
1109 .@"32" => {
1110 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));
1111 if (offset_loc + 4 > debug_ranges.len) return badDwarf();
1112 const offset = readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);
1113 break :off compile_unit.rnglists_base + offset;
1114 },
1115 .@"64" => {
1116 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));
1117 if (offset_loc + 8 > debug_ranges.len) return badDwarf();
1118 const offset = readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);
1119 break :off compile_unit.rnglists_base + offset;
1120 },
1121 }
1122 },
1123 else => return badDwarf(),
1124 };
1125
1126 // All the addresses in the list are relative to the value
1127 // specified by DW_AT.low_pc or to some other value encoded
1128 // in the list itself.
1129 // If no starting value is specified use zero.
1130 const base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) {
1131 error.MissingDebugInfo => 0,
1132 else => return err,
1133 };
1134
1135 return .{
1136 .base_address = base_address,
1137 .section_type = section_type,
1138 .di = di,
1139 .compile_unit = compile_unit,
1140 .fbr = .{
1141 .buf = debug_ranges,
1142 .pos = cast(usize, ranges_offset) orelse return badDwarf(),
1143 .endian = di.endian,
1144 },
1145 };
1146 }
1147
1148 // Returns the next range in the list, or null if the end was reached.
1149 pub fn next(self: *@This()) !?struct { start_addr: u64, end_addr: u64 } {
1150 switch (self.section_type) {
1151 .debug_rnglists => {
1152 const kind = try self.fbr.readByte();
1153 switch (kind) {
1154 RLE.end_of_list => return null,
1155 RLE.base_addressx => {
1156 const index = try self.fbr.readUleb128(usize);
1157 self.base_address = try self.di.readDebugAddr(self.compile_unit.*, index);
1158 return try self.next();
1159 },
1160 RLE.startx_endx => {
1161 const start_index = try self.fbr.readUleb128(usize);
1162 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
1163
1164 const end_index = try self.fbr.readUleb128(usize);
1165 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);
1166
1167 return .{
1168 .start_addr = start_addr,
1169 .end_addr = end_addr,
1170 };
1171 },
1172 RLE.startx_length => {
1173 const start_index = try self.fbr.readUleb128(usize);
1174 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
1175
1176 const len = try self.fbr.readUleb128(usize);
1177 const end_addr = start_addr + len;
1178
1179 return .{
1180 .start_addr = start_addr,
1181 .end_addr = end_addr,
1182 };
1183 },
1184 RLE.offset_pair => {
1185 const start_addr = try self.fbr.readUleb128(usize);
1186 const end_addr = try self.fbr.readUleb128(usize);
1187
1188 // This is the only kind that uses the base address
1189 return .{
1190 .start_addr = self.base_address + start_addr,
1191 .end_addr = self.base_address + end_addr,
1192 };
1193 },
1194 RLE.base_address => {
1195 self.base_address = try self.fbr.readInt(usize);
1196 return try self.next();
1197 },
1198 RLE.start_end => {
1199 const start_addr = try self.fbr.readInt(usize);
1200 const end_addr = try self.fbr.readInt(usize);
1201
1202 return .{
1203 .start_addr = start_addr,
1204 .end_addr = end_addr,
1205 };
1206 },
1207 RLE.start_length => {
1208 const start_addr = try self.fbr.readInt(usize);
1209 const len = try self.fbr.readUleb128(usize);
1210 const end_addr = start_addr + len;
1211
1212 return .{
1213 .start_addr = start_addr,
1214 .end_addr = end_addr,
1215 };
1216 },
1217 else => return badDwarf(),
1218 }
1219 },
1220 .debug_ranges => {
1221 const start_addr = try self.fbr.readInt(usize);
1222 const end_addr = try self.fbr.readInt(usize);
1223 if (start_addr == 0 and end_addr == 0) return null;
1224
1225 // This entry selects a new value for the base address
1226 if (start_addr == maxInt(usize)) {
1227 self.base_address = end_addr;
1228 return try self.next();
1229 }
1230
1231 return .{
1232 .start_addr = self.base_address + start_addr,
1233 .end_addr = self.base_address + end_addr,
1234 };
1235 },
1236 else => unreachable,
1237 }
1238 }
1239};
1240
1241pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*const CompileUnit {
1242 for (di.compile_unit_list.items) |*compile_unit| {
1243 if (compile_unit.pc_range) |range| {
1244 if (target_address >= range.start and target_address < range.end) return compile_unit;
1245 }
1246
1247 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;
1248 var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue;
1249 while (try iter.next()) |range| {
1250 if (target_address >= range.start_addr and target_address < range.end_addr) return compile_unit;
1251 }
1252 }
1253
1254 return missingDwarf();
1255}
1256
1257/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
1258/// seeks in the stream and parses it.
1259fn getAbbrevTable(di: *Dwarf, allocator: Allocator, abbrev_offset: u64) !*const Abbrev.Table {
1260 for (di.abbrev_table_list.items) |*table| {
1261 if (table.offset == abbrev_offset) {
1262 return table;
1263 }
1264 }
1265 try di.abbrev_table_list.append(
1266 allocator,
1267 try di.parseAbbrevTable(allocator, abbrev_offset),
1268 );
1269 return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1];
1270}
1271
1272fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {
1273 var fbr: FixedBufferReader = .{
1274 .buf = di.section(.debug_abbrev).?,
1275 .pos = cast(usize, offset) orelse return badDwarf(),
1276 .endian = di.endian,
1277 };
1278
1279 var abbrevs = std.ArrayList(Abbrev).init(allocator);
1280 defer {
1281 for (abbrevs.items) |*abbrev| {
1282 abbrev.deinit(allocator);
1283 }
1284 abbrevs.deinit();
1285 }
1286
1287 var attrs = std.ArrayList(Abbrev.Attr).init(allocator);
1288 defer attrs.deinit();
1289
1290 while (true) {
1291 const code = try fbr.readUleb128(u64);
1292 if (code == 0) break;
1293 const tag_id = try fbr.readUleb128(u64);
1294 const has_children = (try fbr.readByte()) == DW.CHILDREN.yes;
1295
1296 while (true) {
1297 const attr_id = try fbr.readUleb128(u64);
1298 const form_id = try fbr.readUleb128(u64);
1299 if (attr_id == 0 and form_id == 0) break;
1300 try attrs.append(.{
1301 .id = attr_id,
1302 .form_id = form_id,
1303 .payload = switch (form_id) {
1304 FORM.implicit_const => try fbr.readIleb128(i64),
1305 else => undefined,
1306 },
1307 });
1308 }
1309
1310 try abbrevs.append(.{
1311 .code = code,
1312 .tag_id = tag_id,
1313 .has_children = has_children,
1314 .attrs = try attrs.toOwnedSlice(),
1315 });
1316 }
1317
1318 return .{
1319 .offset = offset,
1320 .abbrevs = try abbrevs.toOwnedSlice(),
1321 };
1322}
1323
1324fn parseDie(
1325 fbr: *FixedBufferReader,
1326 attrs_buf: []Die.Attr,
1327 abbrev_table: *const Abbrev.Table,
1328 format: Format,
1329) !?Die {
1330 const abbrev_code = try fbr.readUleb128(u64);
1331 if (abbrev_code == 0) return null;
1332 const table_entry = abbrev_table.get(abbrev_code) orelse return badDwarf();
1333
1334 const attrs = attrs_buf[0..table_entry.attrs.len];
1335 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = Die.Attr{
1336 .id = attr.id,
1337 .value = try parseFormValue(
1338 fbr,
1339 attr.form_id,
1340 format,
1341 attr.payload,
1342 ),
1343 };
1344 return .{
1345 .tag_id = table_entry.tag_id,
1346 .has_children = table_entry.has_children,
1347 .attrs = attrs,
1348 };
1349}
1350
1351pub fn getLineNumberInfo(
1352 di: *Dwarf,
1353 allocator: Allocator,
1354 compile_unit: CompileUnit,
1355 target_address: u64,
1356) !std.debug.LineInfo {
1357 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit);
1358 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
1359
1360 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian };
1361 try fbr.seekTo(line_info_offset);
1362
1363 const unit_header = try readUnitHeader(&fbr, null);
1364 if (unit_header.unit_length == 0) return missingDwarf();
1365 const next_offset = unit_header.header_length + unit_header.unit_length;
1366
1367 const version = try fbr.readInt(u16);
1368 if (version < 2) return badDwarf();
1369
1370 var addr_size: u8 = switch (unit_header.format) {
1371 .@"32" => 4,
1372 .@"64" => 8,
1373 };
1374 var seg_size: u8 = 0;
1375 if (version >= 5) {
1376 addr_size = try fbr.readByte();
1377 seg_size = try fbr.readByte();
1378 }
1379
1380 const prologue_length = try fbr.readAddress(unit_header.format);
1381 const prog_start_offset = fbr.pos + prologue_length;
1382
1383 const minimum_instruction_length = try fbr.readByte();
1384 if (minimum_instruction_length == 0) return badDwarf();
1385
1386 if (version >= 4) {
1387 // maximum_operations_per_instruction
1388 _ = try fbr.readByte();
1389 }
1390
1391 const default_is_stmt = (try fbr.readByte()) != 0;
1392 const line_base = try fbr.readByteSigned();
1393
1394 const line_range = try fbr.readByte();
1395 if (line_range == 0) return badDwarf();
1396
1397 const opcode_base = try fbr.readByte();
1398
1399 const standard_opcode_lengths = try fbr.readBytes(opcode_base - 1);
1400
1401 var include_directories = std.ArrayList(FileEntry).init(allocator);
1402 defer include_directories.deinit();
1403 var file_entries = std.ArrayList(FileEntry).init(allocator);
1404 defer file_entries.deinit();
1405
1406 if (version < 5) {
1407 try include_directories.append(.{ .path = compile_unit_cwd });
1408
1409 while (true) {
1410 const dir = try fbr.readBytesTo(0);
1411 if (dir.len == 0) break;
1412 try include_directories.append(.{ .path = dir });
1413 }
1414
1415 while (true) {
1416 const file_name = try fbr.readBytesTo(0);
1417 if (file_name.len == 0) break;
1418 const dir_index = try fbr.readUleb128(u32);
1419 const mtime = try fbr.readUleb128(u64);
1420 const size = try fbr.readUleb128(u64);
1421 try file_entries.append(.{
1422 .path = file_name,
1423 .dir_index = dir_index,
1424 .mtime = mtime,
1425 .size = size,
1426 });
1427 }
1428 } else {
1429 const FileEntFmt = struct {
1430 content_type_code: u8,
1431 form_code: u16,
1432 };
1433 {
1434 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
1435 const directory_entry_format_count = try fbr.readByte();
1436 if (directory_entry_format_count > dir_ent_fmt_buf.len) return badDwarf();
1437 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
1438 ent_fmt.* = .{
1439 .content_type_code = try fbr.readUleb128(u8),
1440 .form_code = try fbr.readUleb128(u16),
1441 };
1442 }
1443
1444 const directories_count = try fbr.readUleb128(usize);
1445 try include_directories.ensureUnusedCapacity(directories_count);
1446 {
1447 var i: usize = 0;
1448 while (i < directories_count) : (i += 1) {
1449 var e: FileEntry = .{ .path = &.{} };
1450 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
1451 const form_value = try parseFormValue(
1452 &fbr,
1453 ent_fmt.form_code,
1454 unit_header.format,
1455 null,
1456 );
1457 switch (ent_fmt.content_type_code) {
1458 DW.LNCT.path => e.path = try form_value.getString(di.*),
1459 DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
1460 DW.LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
1461 DW.LNCT.size => e.size = try form_value.getUInt(u64),
1462 DW.LNCT.MD5 => e.md5 = switch (form_value) {
1463 .data16 => |data16| data16.*,
1464 else => return badDwarf(),
1465 },
1466 else => continue,
1467 }
1468 }
1469 include_directories.appendAssumeCapacity(e);
1470 }
1471 }
1472 }
1473
1474 var file_ent_fmt_buf: [10]FileEntFmt = undefined;
1475 const file_name_entry_format_count = try fbr.readByte();
1476 if (file_name_entry_format_count > file_ent_fmt_buf.len) return badDwarf();
1477 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
1478 ent_fmt.* = .{
1479 .content_type_code = try fbr.readUleb128(u8),
1480 .form_code = try fbr.readUleb128(u16),
1481 };
1482 }
1483
1484 const file_names_count = try fbr.readUleb128(usize);
1485 try file_entries.ensureUnusedCapacity(file_names_count);
1486 {
1487 var i: usize = 0;
1488 while (i < file_names_count) : (i += 1) {
1489 var e: FileEntry = .{ .path = &.{} };
1490 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
1491 const form_value = try parseFormValue(
1492 &fbr,
1493 ent_fmt.form_code,
1494 unit_header.format,
1495 null,
1496 );
1497 switch (ent_fmt.content_type_code) {
1498 DW.LNCT.path => e.path = try form_value.getString(di.*),
1499 DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
1500 DW.LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
1501 DW.LNCT.size => e.size = try form_value.getUInt(u64),
1502 DW.LNCT.MD5 => e.md5 = switch (form_value) {
1503 .data16 => |data16| data16.*,
1504 else => return badDwarf(),
1505 },
1506 else => continue,
1507 }
1508 }
1509 file_entries.appendAssumeCapacity(e);
1510 }
1511 }
1512 }
1513
1514 var prog = LineNumberProgram.init(
1515 default_is_stmt,
1516 include_directories.items,
1517 target_address,
1518 version,
1519 );
1520
1521 try fbr.seekTo(prog_start_offset);
1522
1523 const next_unit_pos = line_info_offset + next_offset;
1524
1525 while (fbr.pos < next_unit_pos) {
1526 const opcode = try fbr.readByte();
1527
1528 if (opcode == DW.LNS.extended_op) {
1529 const op_size = try fbr.readUleb128(u64);
1530 if (op_size < 1) return badDwarf();
1531 const sub_op = try fbr.readByte();
1532 switch (sub_op) {
1533 DW.LNE.end_sequence => {
1534 prog.end_sequence = true;
1535 if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info;
1536 prog.reset();
1537 },
1538 DW.LNE.set_address => {
1539 const addr = try fbr.readInt(usize);
1540 prog.address = addr;
1541 },
1542 DW.LNE.define_file => {
1543 const path = try fbr.readBytesTo(0);
1544 const dir_index = try fbr.readUleb128(u32);
1545 const mtime = try fbr.readUleb128(u64);
1546 const size = try fbr.readUleb128(u64);
1547 try file_entries.append(.{
1548 .path = path,
1549 .dir_index = dir_index,
1550 .mtime = mtime,
1551 .size = size,
1552 });
1553 },
1554 else => try fbr.seekForward(op_size - 1),
1555 }
1556 } else if (opcode >= opcode_base) {
1557 // special opcodes
1558 const adjusted_opcode = opcode - opcode_base;
1559 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
1560 const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
1561 prog.line += inc_line;
1562 prog.address += inc_addr;
1563 if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info;
1564 prog.basic_block = false;
1565 } else {
1566 switch (opcode) {
1567 DW.LNS.copy => {
1568 if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info;
1569 prog.basic_block = false;
1570 },
1571 DW.LNS.advance_pc => {
1572 const arg = try fbr.readUleb128(usize);
1573 prog.address += arg * minimum_instruction_length;
1574 },
1575 DW.LNS.advance_line => {
1576 const arg = try fbr.readIleb128(i64);
1577 prog.line += arg;
1578 },
1579 DW.LNS.set_file => {
1580 const arg = try fbr.readUleb128(usize);
1581 prog.file = arg;
1582 },
1583 DW.LNS.set_column => {
1584 const arg = try fbr.readUleb128(u64);
1585 prog.column = arg;
1586 },
1587 DW.LNS.negate_stmt => {
1588 prog.is_stmt = !prog.is_stmt;
1589 },
1590 DW.LNS.set_basic_block => {
1591 prog.basic_block = true;
1592 },
1593 DW.LNS.const_add_pc => {
1594 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
1595 prog.address += inc_addr;
1596 },
1597 DW.LNS.fixed_advance_pc => {
1598 const arg = try fbr.readInt(u16);
1599 prog.address += arg;
1600 },
1601 DW.LNS.set_prologue_end => {},
1602 else => {
1603 if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf();
1604 try fbr.seekForward(standard_opcode_lengths[opcode - 1]);
1605 },
1606 }
1607 }
1608 }
1609
1610 return missingDwarf();
1611}
1612
1613fn getString(di: Dwarf, offset: u64) ![:0]const u8 {
1614 return getStringGeneric(di.section(.debug_str), offset);
1615}
1616
1617fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {
1618 return getStringGeneric(di.section(.debug_line_str), offset);
1619}
1620
1621fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
1622 const debug_addr = di.section(.debug_addr) orelse return badDwarf();
1623
1624 // addr_base points to the first item after the header, however we
1625 // need to read the header to know the size of each item. Empirically,
1626 // it may disagree with is_64 on the compile unit.
1627 // The header is 8 or 12 bytes depending on is_64.
1628 if (compile_unit.addr_base < 8) return badDwarf();
1629
1630 const version = readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);
1631 if (version != 5) return badDwarf();
1632
1633 const addr_size = debug_addr[compile_unit.addr_base - 2];
1634 const seg_size = debug_addr[compile_unit.addr_base - 1];
1635
1636 const byte_offset = @as(usize, @intCast(compile_unit.addr_base + (addr_size + seg_size) * index));
1637 if (byte_offset + addr_size > debug_addr.len) return badDwarf();
1638 return switch (addr_size) {
1639 1 => debug_addr[byte_offset],
1640 2 => readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
1641 4 => readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
1642 8 => readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
1643 else => badDwarf(),
1644 };
1645}
1646
1647/// If .eh_frame_hdr is present, then only the header needs to be parsed.
1648///
1649/// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list
1650/// of FDEs is built for binary searching during unwinding.
1651pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
1652 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
1653 var fbr: FixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian };
1654
1655 const version = try fbr.readByte();
1656 if (version != 1) break :blk;
1657
1658 const eh_frame_ptr_enc = try fbr.readByte();
1659 if (eh_frame_ptr_enc == EH.PE.omit) break :blk;
1660 const fde_count_enc = try fbr.readByte();
1661 if (fde_count_enc == EH.PE.omit) break :blk;
1662 const table_enc = try fbr.readByte();
1663 if (table_enc == EH.PE.omit) break :blk;
1664
1665 const eh_frame_ptr = cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{
1666 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
1667 .follow_indirect = true,
1668 }) orelse return badDwarf()) orelse return badDwarf();
1669
1670 const fde_count = cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{
1671 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
1672 .follow_indirect = true,
1673 }) orelse return badDwarf()) orelse return badDwarf();
1674
1675 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);
1676 const entries_len = fde_count * entry_size;
1677 if (entries_len > eh_frame_hdr.len - fbr.pos) return badDwarf();
1678
1679 di.eh_frame_hdr = .{
1680 .eh_frame_ptr = eh_frame_ptr,
1681 .table_enc = table_enc,
1682 .fde_count = fde_count,
1683 .entries = eh_frame_hdr[fbr.pos..][0..entries_len],
1684 };
1685
1686 // No need to scan .eh_frame, we have a binary search table already
1687 return;
1688 }
1689
1690 const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
1691 for (frame_sections) |frame_section| {
1692 if (di.section(frame_section)) |section_data| {
1693 var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian };
1694 while (fbr.pos < fbr.buf.len) {
1695 const entry_header = try EntryHeader.read(&fbr, null, frame_section);
1696 switch (entry_header.type) {
1697 .cie => {
1698 const cie = try CommonInformationEntry.parse(
1699 entry_header.entry_bytes,
1700 di.sectionVirtualOffset(frame_section, base_address).?,
1701 true,
1702 entry_header.format,
1703 frame_section,
1704 entry_header.length_offset,
1705 @sizeOf(usize),
1706 di.endian,
1707 );
1708 try di.cie_map.put(allocator, entry_header.length_offset, cie);
1709 },
1710 .fde => |cie_offset| {
1711 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
1712 const fde = try FrameDescriptionEntry.parse(
1713 entry_header.entry_bytes,
1714 di.sectionVirtualOffset(frame_section, base_address).?,
1715 true,
1716 cie,
1717 @sizeOf(usize),
1718 di.endian,
1719 );
1720 try di.fde_list.append(allocator, fde);
1721 },
1722 .terminator => break,
1723 }
1724 }
1725
1726 std.mem.sortUnstable(FrameDescriptionEntry, di.fde_list.items, {}, struct {
1727 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
1728 _ = ctx;
1729 return a.pc_begin < b.pc_begin;
1730 }
1731 }.lessThan);
1732 }
1733 }
1734}
1735
1736/// Unwind a stack frame using DWARF unwinding info, updating the register context.
1737///
1738/// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE.
1739/// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE.
1740///
1741/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
1742/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
1743pub fn unwindFrame(di: *const Dwarf, context: *UnwindContext, ma: *StackIterator.MemoryAccessor, explicit_fde_offset: ?usize) !usize {
1744 if (!comptime abi.supportsUnwinding(builtin.target)) return error.UnsupportedCpuArchitecture;
1745 if (context.pc == 0) return 0;
1746
1747 // Find the FDE and CIE
1748 var cie: CommonInformationEntry = undefined;
1749 var fde: FrameDescriptionEntry = undefined;
1750
1751 if (explicit_fde_offset) |fde_offset| {
1752 const dwarf_section: Section.Id = .eh_frame;
1753 const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
1754 if (fde_offset >= frame_section.len) return error.MissingFDE;
1755
1756 var fbr: FixedBufferReader = .{
1757 .buf = frame_section,
1758 .pos = fde_offset,
1759 .endian = di.endian,
1760 };
1761
1762 const fde_entry_header = try EntryHeader.read(&fbr, null, dwarf_section);
1763 if (fde_entry_header.type != .fde) return error.MissingFDE;
1764
1765 const cie_offset = fde_entry_header.type.fde;
1766 try fbr.seekTo(cie_offset);
1767
1768 fbr.endian = native_endian;
1769 const cie_entry_header = try EntryHeader.read(&fbr, null, dwarf_section);
1770 if (cie_entry_header.type != .cie) return badDwarf();
1771
1772 cie = try CommonInformationEntry.parse(
1773 cie_entry_header.entry_bytes,
1774 0,
1775 true,
1776 cie_entry_header.format,
1777 dwarf_section,
1778 cie_entry_header.length_offset,
1779 @sizeOf(usize),
1780 native_endian,
1781 );
1782
1783 fde = try FrameDescriptionEntry.parse(
1784 fde_entry_header.entry_bytes,
1785 0,
1786 true,
1787 cie,
1788 @sizeOf(usize),
1789 native_endian,
1790 );
1791 } else if (di.eh_frame_hdr) |header| {
1792 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
1793 try header.findEntry(
1794 ma,
1795 eh_frame_len,
1796 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1797 context.pc,
1798 &cie,
1799 &fde,
1800 );
1801 } else {
1802 const index = std.sort.binarySearch(FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct {
1803 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {
1804 if (pc < mid_item.pc_begin) return .lt;
1805
1806 const range_end = mid_item.pc_begin + mid_item.pc_range;
1807 if (pc < range_end) return .eq;
1808
1809 return .gt;
1810 }
1811 }.compareFn);
1812
1813 fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
1814 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
1815 }
1816
1817 var expression_context: expression.Context = .{
1818 .format = cie.format,
1819 .memory_accessor = ma,
1820 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1821 .thread_context = context.thread_context,
1822 .reg_context = context.reg_context,
1823 .cfa = context.cfa,
1824 };
1825
1826 context.vm.reset();
1827 context.reg_context.eh_frame = cie.version != 4;
1828 context.reg_context.is_macho = di.is_macho;
1829
1830 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
1831 context.cfa = switch (row.cfa.rule) {
1832 .val_offset => |offset| blk: {
1833 const register = row.cfa.register orelse return error.InvalidCFARule;
1834 const value = readInt(usize, (try abi.regBytes(context.thread_context, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
1835 break :blk try call_frame.applyOffset(value, offset);
1836 },
1837 .expression => |expr| blk: {
1838 context.stack_machine.reset();
1839 const value = try context.stack_machine.run(
1840 expr,
1841 context.allocator,
1842 expression_context,
1843 context.cfa,
1844 );
1845
1846 if (value) |v| {
1847 if (v != .generic) return error.InvalidExpressionValue;
1848 break :blk v.generic;
1849 } else return error.NoExpressionValue;
1850 },
1851 else => return error.InvalidCFARule,
1852 };
1853
1854 if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA;
1855 expression_context.cfa = context.cfa;
1856
1857 // Buffering the modifications is done because copying the thread context is not portable,
1858 // some implementations (ie. darwin) use internal pointers to the mcontext.
1859 var arena = std.heap.ArenaAllocator.init(context.allocator);
1860 defer arena.deinit();
1861 const update_allocator = arena.allocator();
1862
1863 const RegisterUpdate = struct {
1864 // Backed by thread_context
1865 dest: []u8,
1866 // Backed by arena
1867 src: []const u8,
1868 prev: ?*@This(),
1869 };
1870
1871 var update_tail: ?*RegisterUpdate = null;
1872 var has_return_address = true;
1873 for (context.vm.rowColumns(row)) |column| {
1874 if (column.register) |register| {
1875 if (register == cie.return_address_register) {
1876 has_return_address = column.rule != .undefined;
1877 }
1878
1879 const dest = try abi.regBytes(context.thread_context, register, context.reg_context);
1880 const src = try update_allocator.alloc(u8, dest.len);
1881
1882 const prev = update_tail;
1883 update_tail = try update_allocator.create(RegisterUpdate);
1884 update_tail.?.* = .{
1885 .dest = dest,
1886 .src = src,
1887 .prev = prev,
1888 };
1889
1890 try column.resolveValue(
1891 context,
1892 expression_context,
1893 ma,
1894 src,
1895 );
1896 }
1897 }
1898
1899 // On all implemented architectures, the CFA is defined as being the previous frame's SP
1900 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?;
1901
1902 while (update_tail) |tail| {
1903 @memcpy(tail.dest, tail.src);
1904 update_tail = tail.prev;
1905 }
1906
1907 if (has_return_address) {
1908 context.pc = abi.stripInstructionPtrAuthCode(readInt(usize, (try abi.regBytes(
1909 context.thread_context,
1910 cie.return_address_register,
1911 context.reg_context,
1912 ))[0..@sizeOf(usize)], native_endian));
1913 } else {
1914 context.pc = 0;
1915 }
1916
1917 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
1918
1919 // The call instruction will have pushed the address of the instruction that follows the call as the return address.
1920 // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
1921 // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
1922 // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
1923 // we subtract one so that the next lookup is guaranteed to land inside the
1924 //
1925 // The exception to this rule is signal frames, where we return execution would be returned to the instruction
1926 // that triggered the handler.
1927 const return_address = context.pc;
1928 if (context.pc > 0 and !cie.isSignalFrame()) context.pc -= 1;
1929
1930 return return_address;
1931}
1932
1933fn parseFormValue(
1934 fbr: *FixedBufferReader,
1935 form_id: u64,
1936 format: Format,
1937 implicit_const: ?i64,
1938) anyerror!FormValue {
1939 return switch (form_id) {
1940 FORM.addr => .{ .addr = try fbr.readAddress(switch (@bitSizeOf(usize)) {
1941 32 => .@"32",
1942 64 => .@"64",
1943 else => @compileError("unsupported @sizeOf(usize)"),
1944 }) },
1945 FORM.addrx1 => .{ .addrx = try fbr.readInt(u8) },
1946 FORM.addrx2 => .{ .addrx = try fbr.readInt(u16) },
1947 FORM.addrx3 => .{ .addrx = try fbr.readInt(u24) },
1948 FORM.addrx4 => .{ .addrx = try fbr.readInt(u32) },
1949 FORM.addrx => .{ .addrx = try fbr.readUleb128(usize) },
1950
1951 FORM.block1,
1952 FORM.block2,
1953 FORM.block4,
1954 FORM.block,
1955 => .{ .block = try fbr.readBytes(switch (form_id) {
1956 FORM.block1 => try fbr.readInt(u8),
1957 FORM.block2 => try fbr.readInt(u16),
1958 FORM.block4 => try fbr.readInt(u32),
1959 FORM.block => try fbr.readUleb128(usize),
1960 else => unreachable,
1961 }) },
1962
1963 FORM.data1 => .{ .udata = try fbr.readInt(u8) },
1964 FORM.data2 => .{ .udata = try fbr.readInt(u16) },
1965 FORM.data4 => .{ .udata = try fbr.readInt(u32) },
1966 FORM.data8 => .{ .udata = try fbr.readInt(u64) },
1967 FORM.data16 => .{ .data16 = (try fbr.readBytes(16))[0..16] },
1968 FORM.udata => .{ .udata = try fbr.readUleb128(u64) },
1969 FORM.sdata => .{ .sdata = try fbr.readIleb128(i64) },
1970 FORM.exprloc => .{ .exprloc = try fbr.readBytes(try fbr.readUleb128(usize)) },
1971 FORM.flag => .{ .flag = (try fbr.readByte()) != 0 },
1972 FORM.flag_present => .{ .flag = true },
1973 FORM.sec_offset => .{ .sec_offset = try fbr.readAddress(format) },
1974
1975 FORM.ref1 => .{ .ref = try fbr.readInt(u8) },
1976 FORM.ref2 => .{ .ref = try fbr.readInt(u16) },
1977 FORM.ref4 => .{ .ref = try fbr.readInt(u32) },
1978 FORM.ref8 => .{ .ref = try fbr.readInt(u64) },
1979 FORM.ref_udata => .{ .ref = try fbr.readUleb128(u64) },
1980
1981 FORM.ref_addr => .{ .ref_addr = try fbr.readAddress(format) },
1982 FORM.ref_sig8 => .{ .ref = try fbr.readInt(u64) },
1983
1984 FORM.string => .{ .string = try fbr.readBytesTo(0) },
1985 FORM.strp => .{ .strp = try fbr.readAddress(format) },
1986 FORM.strx1 => .{ .strx = try fbr.readInt(u8) },
1987 FORM.strx2 => .{ .strx = try fbr.readInt(u16) },
1988 FORM.strx3 => .{ .strx = try fbr.readInt(u24) },
1989 FORM.strx4 => .{ .strx = try fbr.readInt(u32) },
1990 FORM.strx => .{ .strx = try fbr.readUleb128(usize) },
1991 FORM.line_strp => .{ .line_strp = try fbr.readAddress(format) },
1992 FORM.indirect => parseFormValue(fbr, try fbr.readUleb128(u64), format, implicit_const),
1993 FORM.implicit_const => .{ .sdata = implicit_const orelse return badDwarf() },
1994 FORM.loclistx => .{ .loclistx = try fbr.readUleb128(u64) },
1995 FORM.rnglistx => .{ .rnglistx = try fbr.readUleb128(u64) },
1996 else => {
1997 //debug.print("unrecognized form id: {x}\n", .{form_id});
1998 return badDwarf();
1999 },
2000 };
2001}
2002
2003const FileEntry = struct {
2004 path: []const u8,
2005 dir_index: u32 = 0,
2006 mtime: u64 = 0,
2007 size: u64 = 0,
2008 md5: [16]u8 = [1]u8{0} ** 16,
2009};
2010
2011const LineNumberProgram = struct {
2012 address: u64,
2013 file: usize,
2014 line: i64,
2015 column: u64,
2016 version: u16,
2017 is_stmt: bool,
2018 basic_block: bool,
2019 end_sequence: bool,
2020
2021 default_is_stmt: bool,
2022 target_address: u64,
2023 include_dirs: []const FileEntry,
2024
2025 prev_valid: bool,
2026 prev_address: u64,
2027 prev_file: usize,
2028 prev_line: i64,
2029 prev_column: u64,
2030 prev_is_stmt: bool,
2031 prev_basic_block: bool,
2032 prev_end_sequence: bool,
2033
2034 // Reset the state machine following the DWARF specification
2035 pub fn reset(self: *LineNumberProgram) void {
2036 self.address = 0;
2037 self.file = 1;
2038 self.line = 1;
2039 self.column = 0;
2040 self.is_stmt = self.default_is_stmt;
2041 self.basic_block = false;
2042 self.end_sequence = false;
2043 // Invalidate all the remaining fields
2044 self.prev_valid = false;
2045 self.prev_address = 0;
2046 self.prev_file = undefined;
2047 self.prev_line = undefined;
2048 self.prev_column = undefined;
2049 self.prev_is_stmt = undefined;
2050 self.prev_basic_block = undefined;
2051 self.prev_end_sequence = undefined;
2052 }
2053
2054 pub fn init(
2055 is_stmt: bool,
2056 include_dirs: []const FileEntry,
2057 target_address: u64,
2058 version: u16,
2059 ) LineNumberProgram {
2060 return LineNumberProgram{
2061 .address = 0,
2062 .file = 1,
2063 .line = 1,
2064 .column = 0,
2065 .version = version,
2066 .is_stmt = is_stmt,
2067 .basic_block = false,
2068 .end_sequence = false,
2069 .include_dirs = include_dirs,
2070 .default_is_stmt = is_stmt,
2071 .target_address = target_address,
2072 .prev_valid = false,
2073 .prev_address = 0,
2074 .prev_file = undefined,
2075 .prev_line = undefined,
2076 .prev_column = undefined,
2077 .prev_is_stmt = undefined,
2078 .prev_basic_block = undefined,
2079 .prev_end_sequence = undefined,
2080 };
2081 }
2082
2083 pub fn checkLineMatch(
2084 self: *LineNumberProgram,
2085 allocator: Allocator,
2086 file_entries: []const FileEntry,
2087 ) !?std.debug.LineInfo {
2088 if (self.prev_valid and
2089 self.target_address >= self.prev_address and
2090 self.target_address < self.address)
2091 {
2092 const file_index = if (self.version >= 5) self.prev_file else i: {
2093 if (self.prev_file == 0) return missingDwarf();
2094 break :i self.prev_file - 1;
2095 };
2096
2097 if (file_index >= file_entries.len) return badDwarf();
2098 const file_entry = &file_entries[file_index];
2099
2100 if (file_entry.dir_index >= self.include_dirs.len) return badDwarf();
2101 const dir_name = self.include_dirs[file_entry.dir_index].path;
2102
2103 const file_name = try std.fs.path.join(allocator, &[_][]const u8{
2104 dir_name, file_entry.path,
2105 });
2106
2107 return std.debug.LineInfo{
2108 .line = if (self.prev_line >= 0) @as(u64, @intCast(self.prev_line)) else 0,
2109 .column = self.prev_column,
2110 .file_name = file_name,
2111 };
2112 }
2113
2114 self.prev_valid = true;
2115 self.prev_address = self.address;
2116 self.prev_file = self.file;
2117 self.prev_line = self.line;
2118 self.prev_column = self.column;
2119 self.prev_is_stmt = self.is_stmt;
2120 self.prev_basic_block = self.basic_block;
2121 self.prev_end_sequence = self.end_sequence;
2122 return null;
2123 }
2124};
2125
2126const UnitHeader = struct {
2127 format: Format,
2128 header_length: u4,
2129 unit_length: u64,
2130};
2131fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*StackIterator.MemoryAccessor) !UnitHeader {
2132 return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) {
2133 0...0xfffffff0 - 1 => |unit_length| .{
2134 .format = .@"32",
2135 .header_length = 4,
2136 .unit_length = unit_length,
2137 },
2138 0xfffffff0...0xffffffff - 1 => badDwarf(),
2139 0xffffffff => .{
2140 .format = .@"64",
2141 .header_length = 12,
2142 .unit_length = try if (opt_ma) |ma| fbr.readIntChecked(u64, ma) else fbr.readInt(u64),
2143 },
2144 };
2145}
2146
2147/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
2148fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
2149 return switch (unwind_reg_number) {
2150 1 => 3, // RBX
2151 2 => 12, // R12
2152 3 => 13, // R13
2153 4 => 14, // R14
2154 5 => 15, // R15
2155 6 => 6, // RBP
2156 else => error.InvalidUnwindRegisterNumber,
2157 };
2158}
2159
2160/// This function is to make it handy to comment out the return and make it
2161/// into a crash when working on this file.
2162fn badDwarf() error{InvalidDebugInfo} {
2163 //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file
2164 return error.InvalidDebugInfo;
2165}
2166
2167fn missingDwarf() error{MissingDebugInfo} {
2168 //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file
2169 return error.MissingDebugInfo;
2170}
2171
2172fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
2173 const str = opt_str orelse return badDwarf();
2174 if (offset > str.len) return badDwarf();
2175 const casted_offset = cast(usize, offset) orelse return badDwarf();
2176 // Valid strings always have a terminating zero byte
2177 const last = std.mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf();
2178 return str[casted_offset..last :0];
2179}
2180
2181// Reading debug info needs to be fast, even when compiled in debug mode,
2182// so avoid using a `std.io.FixedBufferStream` which is too slow.
2183pub const FixedBufferReader = struct {
2184 buf: []const u8,
2185 pos: usize = 0,
2186 endian: std.builtin.Endian,
2187
2188 pub const Error = error{ EndOfBuffer, Overflow, InvalidBuffer };
2189
2190 fn seekTo(fbr: *FixedBufferReader, pos: u64) Error!void {
2191 if (pos > fbr.buf.len) return error.EndOfBuffer;
2192 fbr.pos = @intCast(pos);
2193 }
2194
2195 fn seekForward(fbr: *FixedBufferReader, amount: u64) Error!void {
2196 if (fbr.buf.len - fbr.pos < amount) return error.EndOfBuffer;
2197 fbr.pos += @intCast(amount);
2198 }
2199
2200 pub inline fn readByte(fbr: *FixedBufferReader) Error!u8 {
2201 if (fbr.pos >= fbr.buf.len) return error.EndOfBuffer;
2202 defer fbr.pos += 1;
2203 return fbr.buf[fbr.pos];
2204 }
2205
2206 fn readByteSigned(fbr: *FixedBufferReader) Error!i8 {
2207 return @bitCast(try fbr.readByte());
2208 }
2209
2210 fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T {
2211 const size = @divExact(@typeInfo(T).Int.bits, 8);
2212 if (fbr.buf.len - fbr.pos < size) return error.EndOfBuffer;
2213 defer fbr.pos += size;
2214 return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian);
2215 }
2216
2217 fn readIntChecked(
2218 fbr: *FixedBufferReader,
2219 comptime T: type,
2220 ma: *std.debug.StackIterator.MemoryAccessor,
2221 ) Error!T {
2222 if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null)
2223 return error.InvalidBuffer;
2224
2225 return fbr.readInt(T);
2226 }
2227
2228 fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
2229 return std.leb.readUleb128(T, fbr);
2230 }
2231
2232 fn readIleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
2233 return std.leb.readIleb128(T, fbr);
2234 }
2235
2236 fn readAddress(fbr: *FixedBufferReader, format: Format) Error!u64 {
2237 return switch (format) {
2238 .@"32" => try fbr.readInt(u32),
2239 .@"64" => try fbr.readInt(u64),
2240 };
2241 }
2242
2243 fn readAddressChecked(
2244 fbr: *FixedBufferReader,
2245 format: Format,
2246 ma: *std.debug.StackIterator.MemoryAccessor,
2247 ) Error!u64 {
2248 return switch (format) {
2249 .@"32" => try fbr.readIntChecked(u32, ma),
2250 .@"64" => try fbr.readIntChecked(u64, ma),
2251 };
2252 }
2253
2254 fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 {
2255 if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer;
2256 defer fbr.pos += len;
2257 return fbr.buf[fbr.pos..][0..len];
2258 }
2259
2260 fn readBytesTo(fbr: *FixedBufferReader, comptime sentinel: u8) Error![:sentinel]const u8 {
2261 const end = @call(.always_inline, std.mem.indexOfScalarPos, .{
2262 u8,
2263 fbr.buf,
2264 fbr.pos,
2265 sentinel,
2266 }) orelse return error.EndOfBuffer;
2267 defer fbr.pos = end + 1;
2268 return fbr.buf[fbr.pos..end :sentinel];
2269 }
2270};
2271
2272/// Unwind a frame using MachO compact unwind info (from __unwind_info).
2273/// If the compact encoding can't encode a way to unwind a frame, it will
2274/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
2275pub fn unwindFrameMachO(
2276 context: *UnwindContext,
2277 ma: *StackIterator.MemoryAccessor,
2278 unwind_info: []const u8,
2279 eh_frame: ?[]const u8,
2280 module_base_address: usize,
2281) !usize {
2282 const macho = std.macho;
2283
2284 const header = std.mem.bytesAsValue(
2285 macho.unwind_info_section_header,
2286 unwind_info[0..@sizeOf(macho.unwind_info_section_header)],
2287 );
2288 const indices = std.mem.bytesAsSlice(
2289 macho.unwind_info_section_header_index_entry,
2290 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry)],
2291 );
2292 if (indices.len == 0) return error.MissingUnwindInfo;
2293
2294 const mapped_pc = context.pc - module_base_address;
2295 const second_level_index = blk: {
2296 var left: usize = 0;
2297 var len: usize = indices.len;
2298
2299 while (len > 1) {
2300 const mid = left + len / 2;
2301 const offset = indices[mid].functionOffset;
2302 if (mapped_pc < offset) {
2303 len /= 2;
2304 } else {
2305 left = mid;
2306 if (mapped_pc == offset) break;
2307 len -= len / 2;
2308 }
2309 }
2310
2311 // Last index is a sentinel containing the highest address as its functionOffset
2312 if (indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
2313 break :blk &indices[left];
2314 };
2315
2316 const common_encodings = std.mem.bytesAsSlice(
2317 macho.compact_unwind_encoding_t,
2318 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t)],
2319 );
2320
2321 const start_offset = second_level_index.secondLevelPagesSectionOffset;
2322 const kind = std.mem.bytesAsValue(
2323 macho.UNWIND_SECOND_LEVEL,
2324 unwind_info[start_offset..][0..@sizeOf(macho.UNWIND_SECOND_LEVEL)],
2325 );
2326
2327 const entry: struct {
2328 function_offset: usize,
2329 raw_encoding: u32,
2330 } = switch (kind.*) {
2331 .REGULAR => blk: {
2332 const page_header = std.mem.bytesAsValue(
2333 macho.unwind_info_regular_second_level_page_header,
2334 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_regular_second_level_page_header)],
2335 );
2336
2337 const entries = std.mem.bytesAsSlice(
2338 macho.unwind_info_regular_second_level_entry,
2339 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry)],
2340 );
2341 if (entries.len == 0) return error.InvalidUnwindInfo;
2342
2343 var left: usize = 0;
2344 var len: usize = entries.len;
2345 while (len > 1) {
2346 const mid = left + len / 2;
2347 const offset = entries[mid].functionOffset;
2348 if (mapped_pc < offset) {
2349 len /= 2;
2350 } else {
2351 left = mid;
2352 if (mapped_pc == offset) break;
2353 len -= len / 2;
2354 }
2355 }
2356
2357 break :blk .{
2358 .function_offset = entries[left].functionOffset,
2359 .raw_encoding = entries[left].encoding,
2360 };
2361 },
2362 .COMPRESSED => blk: {
2363 const page_header = std.mem.bytesAsValue(
2364 macho.unwind_info_compressed_second_level_page_header,
2365 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_compressed_second_level_page_header)],
2366 );
2367
2368 const entries = std.mem.bytesAsSlice(
2369 macho.UnwindInfoCompressedEntry,
2370 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry)],
2371 );
2372 if (entries.len == 0) return error.InvalidUnwindInfo;
2373
2374 var left: usize = 0;
2375 var len: usize = entries.len;
2376 while (len > 1) {
2377 const mid = left + len / 2;
2378 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
2379 if (mapped_pc < offset) {
2380 len /= 2;
2381 } else {
2382 left = mid;
2383 if (mapped_pc == offset) break;
2384 len -= len / 2;
2385 }
2386 }
2387
2388 const entry = entries[left];
2389 const function_offset = second_level_index.functionOffset + entry.funcOffset;
2390 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
2391 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
2392 break :blk .{
2393 .function_offset = function_offset,
2394 .raw_encoding = common_encodings[entry.encodingIndex],
2395 };
2396 } else {
2397 const local_index = try std.math.sub(
2398 u8,
2399 entry.encodingIndex,
2400 cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
2401 );
2402 const local_encodings = std.mem.bytesAsSlice(
2403 macho.compact_unwind_encoding_t,
2404 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t)],
2405 );
2406 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
2407 break :blk .{
2408 .function_offset = function_offset,
2409 .raw_encoding = local_encodings[local_index],
2410 };
2411 }
2412 },
2413 else => return error.InvalidUnwindInfo,
2414 };
2415
2416 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
2417 const reg_context = abi.RegisterContext{
2418 .eh_frame = false,
2419 .is_macho = true,
2420 };
2421
2422 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
2423 const new_ip = switch (builtin.cpu.arch) {
2424 .x86_64 => switch (encoding.mode.x86_64) {
2425 .OLD => return error.UnimplementedUnwindEncoding,
2426 .RBP_FRAME => blk: {
2427 const regs: [5]u3 = .{
2428 encoding.value.x86_64.frame.reg0,
2429 encoding.value.x86_64.frame.reg1,
2430 encoding.value.x86_64.frame.reg2,
2431 encoding.value.x86_64.frame.reg3,
2432 encoding.value.x86_64.frame.reg4,
2433 };
2434
2435 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
2436 var max_reg: usize = 0;
2437 inline for (regs, 0..) |reg, i| {
2438 if (reg > 0) max_reg = i;
2439 }
2440
2441 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2442 const new_sp = fp + 2 * @sizeOf(usize);
2443
2444 // Verify the stack range we're about to read register values from
2445 if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo;
2446
2447 const ip_ptr = fp + @sizeOf(usize);
2448 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2449 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2450
2451 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2452 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2453 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2454
2455 for (regs, 0..) |reg, i| {
2456 if (reg == 0) continue;
2457 const addr = fp - frame_offset + i * @sizeOf(usize);
2458 const reg_number = try compactUnwindToDwarfRegNumber(reg);
2459 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
2460 }
2461
2462 break :blk new_ip;
2463 },
2464 .STACK_IMMD,
2465 .STACK_IND,
2466 => blk: {
2467 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2468 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
2469 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
2470 else stack_size: {
2471 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
2472 const sub_offset_addr =
2473 module_base_address +
2474 entry.function_offset +
2475 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
2476 if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
2477
2478 // `sub_offset_addr` points to the offset of the literal within the instruction
2479 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
2480 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
2481 };
2482
2483 // Decode the Lehmer-coded sequence of registers.
2484 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
2485
2486 // Decode the variable-based permutation number into its digits. Each digit represents
2487 // an index into the list of register numbers that weren't yet used in the sequence at
2488 // the time the digit was added.
2489 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
2490 const ip_ptr = if (reg_count > 0) reg_blk: {
2491 var digits: [6]u3 = undefined;
2492 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
2493 var base: usize = 2;
2494 for (0..reg_count) |i| {
2495 const div = accumulator / base;
2496 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
2497 accumulator = div;
2498 base += 1;
2499 }
2500
2501 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
2502 var registers: [reg_numbers.len]u3 = undefined;
2503 var used_indices = [_]bool{false} ** reg_numbers.len;
2504 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
2505 var unused_count: u8 = 0;
2506 const unused_index = for (used_indices, 0..) |used, index| {
2507 if (!used) {
2508 if (target_unused_index == unused_count) break index;
2509 unused_count += 1;
2510 }
2511 } else unreachable;
2512
2513 registers[i] = reg_numbers[unused_index];
2514 used_indices[unused_index] = true;
2515 }
2516
2517 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
2518 if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo;
2519 for (0..reg_count) |i| {
2520 const reg_number = try compactUnwindToDwarfRegNumber(registers[i]);
2521 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2522 reg_addr += @sizeOf(usize);
2523 }
2524
2525 break :reg_blk reg_addr;
2526 } else sp + stack_size - @sizeOf(usize);
2527
2528 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2529 const new_sp = ip_ptr + @sizeOf(usize);
2530 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
2531
2532 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2533 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2534
2535 break :blk new_ip;
2536 },
2537 .DWARF => {
2538 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
2539 },
2540 },
2541 .aarch64 => switch (encoding.mode.arm64) {
2542 .OLD => return error.UnimplementedUnwindEncoding,
2543 .FRAMELESS => blk: {
2544 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2545 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
2546 const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*;
2547 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
2548 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2549 break :blk new_ip;
2550 },
2551 .DWARF => {
2552 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
2553 },
2554 .FRAME => blk: {
2555 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2556 const new_sp = fp + 16;
2557 const ip_ptr = fp + @sizeOf(usize);
2558
2559 const num_restored_pairs: usize =
2560 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
2561 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
2562 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
2563
2564 if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo;
2565
2566 var reg_addr = fp - @sizeOf(usize);
2567 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
2568 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
2569 (try abi.regValueNative(usize, context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2570 reg_addr += @sizeOf(usize);
2571 (try abi.regValueNative(usize, context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2572 reg_addr += @sizeOf(usize);
2573 }
2574 }
2575
2576 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
2577 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
2578 // Only the lower half of the 128-bit V registers are restored during unwinding
2579 @memcpy(
2580 try abi.regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
2581 std.mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2582 );
2583 reg_addr += @sizeOf(usize);
2584 @memcpy(
2585 try abi.regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
2586 std.mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2587 );
2588 reg_addr += @sizeOf(usize);
2589 }
2590 }
2591
2592 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2593 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2594
2595 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2596 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2597
2598 break :blk new_ip;
2599 },
2600 },
2601 else => return error.UnimplementedArch,
2602 };
2603
2604 context.pc = abi.stripInstructionPtrAuthCode(new_ip);
2605 if (context.pc > 0) context.pc -= 1;
2606 return new_ip;
2607}
2608
2609fn unwindFrameMachODwarf(
2610 context: *UnwindContext,
2611 ma: *std.debug.StackIterator.MemoryAccessor,
2612 eh_frame: []const u8,
2613 fde_offset: usize,
2614) !usize {
2615 var di = Dwarf{
2616 .endian = native_endian,
2617 .is_macho = true,
2618 };
2619 defer di.deinit(context.allocator);
2620
2621 di.sections[@intFromEnum(Section.Id.eh_frame)] = .{
2622 .data = eh_frame,
2623 .owned = false,
2624 };
2625
2626 return di.unwindFrame(context, ma, fde_offset);
2627}
2628
2629const EhPointerContext = struct {
2630 // The address of the pointer field itself
2631 pc_rel_base: u64,
2632
2633 // Whether or not to follow indirect pointers. This should only be
2634 // used when decoding pointers at runtime using the current process's
2635 // debug info
2636 follow_indirect: bool,
2637
2638 // These relative addressing modes are only used in specific cases, and
2639 // might not be available / required in all parsing contexts
2640 data_rel_base: ?u64 = null,
2641 text_rel_base: ?u64 = null,
2642 function_rel_base: ?u64 = null,
2643};
2644fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext) !?u64 {
2645 if (enc == EH.PE.omit) return null;
2646
2647 const value: union(enum) {
2648 signed: i64,
2649 unsigned: u64,
2650 } = switch (enc & EH.PE.type_mask) {
2651 EH.PE.absptr => .{
2652 .unsigned = switch (addr_size_bytes) {
2653 2 => try fbr.readInt(u16),
2654 4 => try fbr.readInt(u32),
2655 8 => try fbr.readInt(u64),
2656 else => return error.InvalidAddrSize,
2657 },
2658 },
2659 EH.PE.uleb128 => .{ .unsigned = try fbr.readUleb128(u64) },
2660 EH.PE.udata2 => .{ .unsigned = try fbr.readInt(u16) },
2661 EH.PE.udata4 => .{ .unsigned = try fbr.readInt(u32) },
2662 EH.PE.udata8 => .{ .unsigned = try fbr.readInt(u64) },
2663 EH.PE.sleb128 => .{ .signed = try fbr.readIleb128(i64) },
2664 EH.PE.sdata2 => .{ .signed = try fbr.readInt(i16) },
2665 EH.PE.sdata4 => .{ .signed = try fbr.readInt(i32) },
2666 EH.PE.sdata8 => .{ .signed = try fbr.readInt(i64) },
2667 else => return badDwarf(),
2668 };
2669
2670 const base = switch (enc & EH.PE.rel_mask) {
2671 EH.PE.pcrel => ctx.pc_rel_base,
2672 EH.PE.textrel => ctx.text_rel_base orelse return error.PointerBaseNotSpecified,
2673 EH.PE.datarel => ctx.data_rel_base orelse return error.PointerBaseNotSpecified,
2674 EH.PE.funcrel => ctx.function_rel_base orelse return error.PointerBaseNotSpecified,
2675 else => null,
2676 };
2677
2678 const ptr: u64 = if (base) |b| switch (value) {
2679 .signed => |s| @intCast(try std.math.add(i64, s, @as(i64, @intCast(b)))),
2680 // absptr can actually contain signed values in some cases (aarch64 MachO)
2681 .unsigned => |u| u +% b,
2682 } else switch (value) {
2683 .signed => |s| @as(u64, @intCast(s)),
2684 .unsigned => |u| u,
2685 };
2686
2687 if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {
2688 if (@sizeOf(usize) != addr_size_bytes) {
2689 // See the documentation for `follow_indirect`
2690 return error.NonNativeIndirection;
2691 }
2692
2693 const native_ptr = cast(usize, ptr) orelse return error.PointerOverflow;
2694 return switch (addr_size_bytes) {
2695 2, 4, 8 => return @as(*const usize, @ptrFromInt(native_ptr)).*,
2696 else => return error.UnsupportedAddrSize,
2697 };
2698 } else {
2699 return ptr;
2700 }
2701}
2702
2703fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
2704 if (pc_rel_offset < 0) {
2705 return std.math.sub(usize, field_ptr, @as(usize, @intCast(-pc_rel_offset)));
2706 } else {
2707 return std.math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
2708 }
2709}
lib/std/debug/Dwarf/abi.zig created+410
......@@ -0,0 +1,410 @@
1const builtin = @import("builtin");
2const std = @import("../../std.zig");
3const mem = std.mem;
4const native_os = builtin.os.tag;
5const posix = std.posix;
6
7pub fn supportsUnwinding(target: std.Target) bool {
8 return switch (target.cpu.arch) {
9 .x86 => switch (target.os.tag) {
10 .linux, .netbsd, .solaris, .illumos => true,
11 else => false,
12 },
13 .x86_64 => switch (target.os.tag) {
14 .linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true,
15 else => false,
16 },
17 .arm => switch (target.os.tag) {
18 .linux => true,
19 else => false,
20 },
21 .aarch64 => switch (target.os.tag) {
22 .linux, .netbsd, .freebsd, .macos, .ios => true,
23 else => false,
24 },
25 else => false,
26 };
27}
28
29pub fn ipRegNum() u8 {
30 return switch (builtin.cpu.arch) {
31 .x86 => 8,
32 .x86_64 => 16,
33 .arm => 15,
34 .aarch64 => 32,
35 else => unreachable,
36 };
37}
38
39pub fn fpRegNum(reg_context: RegisterContext) u8 {
40 return switch (builtin.cpu.arch) {
41 // GCC on OS X historically did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
42 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
43 .x86_64 => 6,
44 .arm => 11,
45 .aarch64 => 29,
46 else => unreachable,
47 };
48}
49
50pub fn spRegNum(reg_context: RegisterContext) u8 {
51 return switch (builtin.cpu.arch) {
52 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
53 .x86_64 => 7,
54 .arm => 13,
55 .aarch64 => 31,
56 else => unreachable,
57 };
58}
59
60/// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature.
61/// This function clears these signature bits to make the pointer usable.
62pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
63 if (builtin.cpu.arch == .aarch64) {
64 // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it)
65 // The save / restore is because `xpaclri` operates on x30 (LR)
66 return asm (
67 \\mov x16, x30
68 \\mov x30, x15
69 \\hint 0x07
70 \\mov x15, x30
71 \\mov x30, x16
72 : [ret] "={x15}" (-> usize),
73 : [ptr] "{x15}" (ptr),
74 : "x16"
75 );
76 }
77
78 return ptr;
79}
80
81pub const RegisterContext = struct {
82 eh_frame: bool,
83 is_macho: bool,
84};
85
86pub const AbiError = error{
87 InvalidRegister,
88 UnimplementedArch,
89 UnimplementedOs,
90 RegisterContextRequired,
91 ThreadContextNotSupported,
92};
93
94fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
95 const reg_bytes_type = comptime RegBytesReturnType(ContextPtrType);
96 const info = @typeInfo(reg_bytes_type).Pointer;
97 return @Type(.{
98 .Pointer = .{
99 .size = .One,
100 .is_const = info.is_const,
101 .is_volatile = info.is_volatile,
102 .is_allowzero = info.is_allowzero,
103 .alignment = info.alignment,
104 .address_space = info.address_space,
105 .child = T,
106 .sentinel = null,
107 },
108 });
109}
110
111/// Returns a pointer to a register stored in a ThreadContext, preserving the pointer attributes of the context.
112pub fn regValueNative(
113 comptime T: type,
114 thread_context_ptr: anytype,
115 reg_number: u8,
116 reg_context: ?RegisterContext,
117) !RegValueReturnType(@TypeOf(thread_context_ptr), T) {
118 const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
119 if (@sizeOf(T) != reg_bytes.len) return error.IncompatibleRegisterSize;
120 return mem.bytesAsValue(T, reg_bytes[0..@sizeOf(T)]);
121}
122
123fn RegBytesReturnType(comptime ContextPtrType: type) type {
124 const info = @typeInfo(ContextPtrType);
125 if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
126 @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
127 }
128
129 return if (info.Pointer.is_const) return []const u8 else []u8;
130}
131
132/// Returns a slice containing the backing storage for `reg_number`.
133///
134/// `reg_context` describes in what context the register number is used, as it can have different
135/// meanings depending on the DWARF container. It is only required when getting the stack or
136/// frame pointer register on some architectures.
137pub fn regBytes(
138 thread_context_ptr: anytype,
139 reg_number: u8,
140 reg_context: ?RegisterContext,
141) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
142 if (native_os == .windows) {
143 return switch (builtin.cpu.arch) {
144 .x86 => switch (reg_number) {
145 0 => mem.asBytes(&thread_context_ptr.Eax),
146 1 => mem.asBytes(&thread_context_ptr.Ecx),
147 2 => mem.asBytes(&thread_context_ptr.Edx),
148 3 => mem.asBytes(&thread_context_ptr.Ebx),
149 4 => mem.asBytes(&thread_context_ptr.Esp),
150 5 => mem.asBytes(&thread_context_ptr.Ebp),
151 6 => mem.asBytes(&thread_context_ptr.Esi),
152 7 => mem.asBytes(&thread_context_ptr.Edi),
153 8 => mem.asBytes(&thread_context_ptr.Eip),
154 9 => mem.asBytes(&thread_context_ptr.EFlags),
155 10 => mem.asBytes(&thread_context_ptr.SegCs),
156 11 => mem.asBytes(&thread_context_ptr.SegSs),
157 12 => mem.asBytes(&thread_context_ptr.SegDs),
158 13 => mem.asBytes(&thread_context_ptr.SegEs),
159 14 => mem.asBytes(&thread_context_ptr.SegFs),
160 15 => mem.asBytes(&thread_context_ptr.SegGs),
161 else => error.InvalidRegister,
162 },
163 .x86_64 => switch (reg_number) {
164 0 => mem.asBytes(&thread_context_ptr.Rax),
165 1 => mem.asBytes(&thread_context_ptr.Rdx),
166 2 => mem.asBytes(&thread_context_ptr.Rcx),
167 3 => mem.asBytes(&thread_context_ptr.Rbx),
168 4 => mem.asBytes(&thread_context_ptr.Rsi),
169 5 => mem.asBytes(&thread_context_ptr.Rdi),
170 6 => mem.asBytes(&thread_context_ptr.Rbp),
171 7 => mem.asBytes(&thread_context_ptr.Rsp),
172 8 => mem.asBytes(&thread_context_ptr.R8),
173 9 => mem.asBytes(&thread_context_ptr.R9),
174 10 => mem.asBytes(&thread_context_ptr.R10),
175 11 => mem.asBytes(&thread_context_ptr.R11),
176 12 => mem.asBytes(&thread_context_ptr.R12),
177 13 => mem.asBytes(&thread_context_ptr.R13),
178 14 => mem.asBytes(&thread_context_ptr.R14),
179 15 => mem.asBytes(&thread_context_ptr.R15),
180 16 => mem.asBytes(&thread_context_ptr.Rip),
181 else => error.InvalidRegister,
182 },
183 .aarch64 => switch (reg_number) {
184 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
185 31 => mem.asBytes(&thread_context_ptr.Sp),
186 32 => mem.asBytes(&thread_context_ptr.Pc),
187 else => error.InvalidRegister,
188 },
189 else => error.UnimplementedArch,
190 };
191 }
192
193 if (!std.debug.have_ucontext) return error.ThreadContextNotSupported;
194
195 const ucontext_ptr = thread_context_ptr;
196 return switch (builtin.cpu.arch) {
197 .x86 => switch (native_os) {
198 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
199 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
200 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
201 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]),
202 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]),
203 4...5 => if (reg_context) |r| bytes: {
204 if (reg_number == 4) {
205 break :bytes if (r.eh_frame and r.is_macho)
206 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP])
207 else
208 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]);
209 } else {
210 break :bytes if (r.eh_frame and r.is_macho)
211 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP])
212 else
213 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]);
214 }
215 } else error.RegisterContextRequired,
216 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]),
217 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]),
218 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]),
219 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]),
220 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]),
221 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]),
222 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]),
223 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]),
224 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]),
225 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]),
226 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
227 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
228 else => error.InvalidRegister,
229 },
230 else => error.UnimplementedOs,
231 },
232 .x86_64 => switch (native_os) {
233 .linux, .solaris, .illumos => switch (reg_number) {
234 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
235 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
236 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]),
237 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]),
238 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]),
239 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]),
240 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]),
241 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]),
242 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]),
243 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]),
244 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]),
245 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]),
246 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]),
247 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]),
248 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
249 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
250 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
251 17...32 => |i| if (native_os.isSolarish())
252 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
253 else
254 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
255 else => error.InvalidRegister,
256 },
257 .freebsd => switch (reg_number) {
258 0 => mem.asBytes(&ucontext_ptr.mcontext.rax),
259 1 => mem.asBytes(&ucontext_ptr.mcontext.rdx),
260 2 => mem.asBytes(&ucontext_ptr.mcontext.rcx),
261 3 => mem.asBytes(&ucontext_ptr.mcontext.rbx),
262 4 => mem.asBytes(&ucontext_ptr.mcontext.rsi),
263 5 => mem.asBytes(&ucontext_ptr.mcontext.rdi),
264 6 => mem.asBytes(&ucontext_ptr.mcontext.rbp),
265 7 => mem.asBytes(&ucontext_ptr.mcontext.rsp),
266 8 => mem.asBytes(&ucontext_ptr.mcontext.r8),
267 9 => mem.asBytes(&ucontext_ptr.mcontext.r9),
268 10 => mem.asBytes(&ucontext_ptr.mcontext.r10),
269 11 => mem.asBytes(&ucontext_ptr.mcontext.r11),
270 12 => mem.asBytes(&ucontext_ptr.mcontext.r12),
271 13 => mem.asBytes(&ucontext_ptr.mcontext.r13),
272 14 => mem.asBytes(&ucontext_ptr.mcontext.r14),
273 15 => mem.asBytes(&ucontext_ptr.mcontext.r15),
274 16 => mem.asBytes(&ucontext_ptr.mcontext.rip),
275 // TODO: Extract xmm state from mcontext.fpstate?
276 else => error.InvalidRegister,
277 },
278 .openbsd => switch (reg_number) {
279 0 => mem.asBytes(&ucontext_ptr.sc_rax),
280 1 => mem.asBytes(&ucontext_ptr.sc_rdx),
281 2 => mem.asBytes(&ucontext_ptr.sc_rcx),
282 3 => mem.asBytes(&ucontext_ptr.sc_rbx),
283 4 => mem.asBytes(&ucontext_ptr.sc_rsi),
284 5 => mem.asBytes(&ucontext_ptr.sc_rdi),
285 6 => mem.asBytes(&ucontext_ptr.sc_rbp),
286 7 => mem.asBytes(&ucontext_ptr.sc_rsp),
287 8 => mem.asBytes(&ucontext_ptr.sc_r8),
288 9 => mem.asBytes(&ucontext_ptr.sc_r9),
289 10 => mem.asBytes(&ucontext_ptr.sc_r10),
290 11 => mem.asBytes(&ucontext_ptr.sc_r11),
291 12 => mem.asBytes(&ucontext_ptr.sc_r12),
292 13 => mem.asBytes(&ucontext_ptr.sc_r13),
293 14 => mem.asBytes(&ucontext_ptr.sc_r14),
294 15 => mem.asBytes(&ucontext_ptr.sc_r15),
295 16 => mem.asBytes(&ucontext_ptr.sc_rip),
296 // TODO: Extract xmm state from sc_fpstate?
297 else => error.InvalidRegister,
298 },
299 .macos, .ios => switch (reg_number) {
300 0 => mem.asBytes(&ucontext_ptr.mcontext.ss.rax),
301 1 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdx),
302 2 => mem.asBytes(&ucontext_ptr.mcontext.ss.rcx),
303 3 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbx),
304 4 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsi),
305 5 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdi),
306 6 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbp),
307 7 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsp),
308 8 => mem.asBytes(&ucontext_ptr.mcontext.ss.r8),
309 9 => mem.asBytes(&ucontext_ptr.mcontext.ss.r9),
310 10 => mem.asBytes(&ucontext_ptr.mcontext.ss.r10),
311 11 => mem.asBytes(&ucontext_ptr.mcontext.ss.r11),
312 12 => mem.asBytes(&ucontext_ptr.mcontext.ss.r12),
313 13 => mem.asBytes(&ucontext_ptr.mcontext.ss.r13),
314 14 => mem.asBytes(&ucontext_ptr.mcontext.ss.r14),
315 15 => mem.asBytes(&ucontext_ptr.mcontext.ss.r15),
316 16 => mem.asBytes(&ucontext_ptr.mcontext.ss.rip),
317 else => error.InvalidRegister,
318 },
319 else => error.UnimplementedOs,
320 },
321 .arm => switch (native_os) {
322 .linux => switch (reg_number) {
323 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
324 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
325 2 => mem.asBytes(&ucontext_ptr.mcontext.arm_r2),
326 3 => mem.asBytes(&ucontext_ptr.mcontext.arm_r3),
327 4 => mem.asBytes(&ucontext_ptr.mcontext.arm_r4),
328 5 => mem.asBytes(&ucontext_ptr.mcontext.arm_r5),
329 6 => mem.asBytes(&ucontext_ptr.mcontext.arm_r6),
330 7 => mem.asBytes(&ucontext_ptr.mcontext.arm_r7),
331 8 => mem.asBytes(&ucontext_ptr.mcontext.arm_r8),
332 9 => mem.asBytes(&ucontext_ptr.mcontext.arm_r9),
333 10 => mem.asBytes(&ucontext_ptr.mcontext.arm_r10),
334 11 => mem.asBytes(&ucontext_ptr.mcontext.arm_fp),
335 12 => mem.asBytes(&ucontext_ptr.mcontext.arm_ip),
336 13 => mem.asBytes(&ucontext_ptr.mcontext.arm_sp),
337 14 => mem.asBytes(&ucontext_ptr.mcontext.arm_lr),
338 15 => mem.asBytes(&ucontext_ptr.mcontext.arm_pc),
339 // CPSR is not allocated a register number (See: https://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst, Section 4.1)
340 else => error.InvalidRegister,
341 },
342 else => error.UnimplementedOs,
343 },
344 .aarch64 => switch (native_os) {
345 .macos, .ios => switch (reg_number) {
346 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
347 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
348 30 => mem.asBytes(&ucontext_ptr.mcontext.ss.lr),
349 31 => mem.asBytes(&ucontext_ptr.mcontext.ss.sp),
350 32 => mem.asBytes(&ucontext_ptr.mcontext.ss.pc),
351
352 // TODO: Find storage for this state
353 //34 => mem.asBytes(&ucontext_ptr.ra_sign_state),
354
355 // V0-V31
356 64...95 => mem.asBytes(&ucontext_ptr.mcontext.ns.q[reg_number - 64]),
357 else => error.InvalidRegister,
358 },
359 .netbsd => switch (reg_number) {
360 0...34 => mem.asBytes(&ucontext_ptr.mcontext.gregs[reg_number]),
361 else => error.InvalidRegister,
362 },
363 .freebsd => switch (reg_number) {
364 0...29 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.x[reg_number]),
365 30 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.lr),
366 31 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.sp),
367
368 // TODO: This seems wrong, but it was in the previous debug.zig code for mapping PC, check this
369 32 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.elr),
370
371 else => error.InvalidRegister,
372 },
373 .openbsd => switch (reg_number) {
374 0...30 => mem.asBytes(&ucontext_ptr.sc_x[reg_number]),
375 31 => mem.asBytes(&ucontext_ptr.sc_sp),
376 32 => mem.asBytes(&ucontext_ptr.sc_lr),
377 33 => mem.asBytes(&ucontext_ptr.sc_elr),
378 34 => mem.asBytes(&ucontext_ptr.sc_spsr),
379 else => error.InvalidRegister,
380 },
381 else => switch (reg_number) {
382 0...30 => mem.asBytes(&ucontext_ptr.mcontext.regs[reg_number]),
383 31 => mem.asBytes(&ucontext_ptr.mcontext.sp),
384 32 => mem.asBytes(&ucontext_ptr.mcontext.pc),
385 else => error.InvalidRegister,
386 },
387 },
388 else => error.UnimplementedArch,
389 };
390}
391
392/// Returns the ABI-defined default value this register has in the unwinding table
393/// before running any of the CIE instructions. The DWARF spec defines these as having
394/// the .undefined rule by default, but allows ABI authors to override that.
395pub fn getRegDefaultValue(reg_number: u8, context: *std.debug.Dwarf.UnwindContext, out: []u8) !void {
396 switch (builtin.cpu.arch) {
397 .aarch64 => {
398 // Callee-saved registers are initialized as if they had the .same_value rule
399 if (reg_number >= 19 and reg_number <= 28) {
400 const src = try regBytes(context.thread_context, reg_number, context.reg_context);
401 if (src.len != out.len) return error.RegisterSizeMismatch;
402 @memcpy(out, src);
403 return;
404 }
405 },
406 else => {},
407 }
408
409 @memset(out, undefined);
410}
lib/std/debug/Dwarf/call_frame.zig created+687
......@@ -0,0 +1,687 @@
1const builtin = @import("builtin");
2const std = @import("../../std.zig");
3const mem = std.mem;
4const debug = std.debug;
5const leb = std.leb;
6const DW = std.dwarf;
7const abi = std.debug.Dwarf.abi;
8const assert = std.debug.assert;
9const native_endian = builtin.cpu.arch.endian();
10
11/// TODO merge with std.dwarf.CFA
12const Opcode = enum(u8) {
13 advance_loc = 0x1 << 6,
14 offset = 0x2 << 6,
15 restore = 0x3 << 6,
16
17 nop = 0x00,
18 set_loc = 0x01,
19 advance_loc1 = 0x02,
20 advance_loc2 = 0x03,
21 advance_loc4 = 0x04,
22 offset_extended = 0x05,
23 restore_extended = 0x06,
24 undefined = 0x07,
25 same_value = 0x08,
26 register = 0x09,
27 remember_state = 0x0a,
28 restore_state = 0x0b,
29 def_cfa = 0x0c,
30 def_cfa_register = 0x0d,
31 def_cfa_offset = 0x0e,
32 def_cfa_expression = 0x0f,
33 expression = 0x10,
34 offset_extended_sf = 0x11,
35 def_cfa_sf = 0x12,
36 def_cfa_offset_sf = 0x13,
37 val_offset = 0x14,
38 val_offset_sf = 0x15,
39 val_expression = 0x16,
40
41 // These opcodes encode an operand in the lower 6 bits of the opcode itself
42 pub const lo_inline = @intFromEnum(Opcode.advance_loc);
43 pub const hi_inline = @intFromEnum(Opcode.restore) | 0b111111;
44
45 // These opcodes are trailed by zero or more operands
46 pub const lo_reserved = @intFromEnum(Opcode.nop);
47 pub const hi_reserved = @intFromEnum(Opcode.val_expression);
48
49 // Vendor-specific opcodes
50 pub const lo_user = 0x1c;
51 pub const hi_user = 0x3f;
52};
53
54fn readBlock(stream: *std.io.FixedBufferStream([]const u8)) ![]const u8 {
55 const reader = stream.reader();
56 const block_len = try leb.readUleb128(usize, reader);
57 if (stream.pos + block_len > stream.buffer.len) return error.InvalidOperand;
58
59 const block = stream.buffer[stream.pos..][0..block_len];
60 reader.context.pos += block_len;
61
62 return block;
63}
64
65pub const Instruction = union(Opcode) {
66 advance_loc: struct {
67 delta: u8,
68 },
69 offset: struct {
70 register: u8,
71 offset: u64,
72 },
73 restore: struct {
74 register: u8,
75 },
76 nop: void,
77 set_loc: struct {
78 address: u64,
79 },
80 advance_loc1: struct {
81 delta: u8,
82 },
83 advance_loc2: struct {
84 delta: u16,
85 },
86 advance_loc4: struct {
87 delta: u32,
88 },
89 offset_extended: struct {
90 register: u8,
91 offset: u64,
92 },
93 restore_extended: struct {
94 register: u8,
95 },
96 undefined: struct {
97 register: u8,
98 },
99 same_value: struct {
100 register: u8,
101 },
102 register: struct {
103 register: u8,
104 target_register: u8,
105 },
106 remember_state: void,
107 restore_state: void,
108 def_cfa: struct {
109 register: u8,
110 offset: u64,
111 },
112 def_cfa_register: struct {
113 register: u8,
114 },
115 def_cfa_offset: struct {
116 offset: u64,
117 },
118 def_cfa_expression: struct {
119 block: []const u8,
120 },
121 expression: struct {
122 register: u8,
123 block: []const u8,
124 },
125 offset_extended_sf: struct {
126 register: u8,
127 offset: i64,
128 },
129 def_cfa_sf: struct {
130 register: u8,
131 offset: i64,
132 },
133 def_cfa_offset_sf: struct {
134 offset: i64,
135 },
136 val_offset: struct {
137 register: u8,
138 offset: u64,
139 },
140 val_offset_sf: struct {
141 register: u8,
142 offset: i64,
143 },
144 val_expression: struct {
145 register: u8,
146 block: []const u8,
147 },
148
149 pub fn read(
150 stream: *std.io.FixedBufferStream([]const u8),
151 addr_size_bytes: u8,
152 endian: std.builtin.Endian,
153 ) !Instruction {
154 const reader = stream.reader();
155 switch (try reader.readByte()) {
156 Opcode.lo_inline...Opcode.hi_inline => |opcode| {
157 const e: Opcode = @enumFromInt(opcode & 0b11000000);
158 const value: u6 = @intCast(opcode & 0b111111);
159 return switch (e) {
160 .advance_loc => .{
161 .advance_loc = .{ .delta = value },
162 },
163 .offset => .{
164 .offset = .{
165 .register = value,
166 .offset = try leb.readUleb128(u64, reader),
167 },
168 },
169 .restore => .{
170 .restore = .{ .register = value },
171 },
172 else => unreachable,
173 };
174 },
175 Opcode.lo_reserved...Opcode.hi_reserved => |opcode| {
176 const e: Opcode = @enumFromInt(opcode);
177 return switch (e) {
178 .advance_loc,
179 .offset,
180 .restore,
181 => unreachable,
182 .nop => .{ .nop = {} },
183 .set_loc => .{
184 .set_loc = .{
185 .address = switch (addr_size_bytes) {
186 2 => try reader.readInt(u16, endian),
187 4 => try reader.readInt(u32, endian),
188 8 => try reader.readInt(u64, endian),
189 else => return error.InvalidAddrSize,
190 },
191 },
192 },
193 .advance_loc1 => .{
194 .advance_loc1 = .{ .delta = try reader.readByte() },
195 },
196 .advance_loc2 => .{
197 .advance_loc2 = .{ .delta = try reader.readInt(u16, endian) },
198 },
199 .advance_loc4 => .{
200 .advance_loc4 = .{ .delta = try reader.readInt(u32, endian) },
201 },
202 .offset_extended => .{
203 .offset_extended = .{
204 .register = try leb.readUleb128(u8, reader),
205 .offset = try leb.readUleb128(u64, reader),
206 },
207 },
208 .restore_extended => .{
209 .restore_extended = .{
210 .register = try leb.readUleb128(u8, reader),
211 },
212 },
213 .undefined => .{
214 .undefined = .{
215 .register = try leb.readUleb128(u8, reader),
216 },
217 },
218 .same_value => .{
219 .same_value = .{
220 .register = try leb.readUleb128(u8, reader),
221 },
222 },
223 .register => .{
224 .register = .{
225 .register = try leb.readUleb128(u8, reader),
226 .target_register = try leb.readUleb128(u8, reader),
227 },
228 },
229 .remember_state => .{ .remember_state = {} },
230 .restore_state => .{ .restore_state = {} },
231 .def_cfa => .{
232 .def_cfa = .{
233 .register = try leb.readUleb128(u8, reader),
234 .offset = try leb.readUleb128(u64, reader),
235 },
236 },
237 .def_cfa_register => .{
238 .def_cfa_register = .{
239 .register = try leb.readUleb128(u8, reader),
240 },
241 },
242 .def_cfa_offset => .{
243 .def_cfa_offset = .{
244 .offset = try leb.readUleb128(u64, reader),
245 },
246 },
247 .def_cfa_expression => .{
248 .def_cfa_expression = .{
249 .block = try readBlock(stream),
250 },
251 },
252 .expression => .{
253 .expression = .{
254 .register = try leb.readUleb128(u8, reader),
255 .block = try readBlock(stream),
256 },
257 },
258 .offset_extended_sf => .{
259 .offset_extended_sf = .{
260 .register = try leb.readUleb128(u8, reader),
261 .offset = try leb.readIleb128(i64, reader),
262 },
263 },
264 .def_cfa_sf => .{
265 .def_cfa_sf = .{
266 .register = try leb.readUleb128(u8, reader),
267 .offset = try leb.readIleb128(i64, reader),
268 },
269 },
270 .def_cfa_offset_sf => .{
271 .def_cfa_offset_sf = .{
272 .offset = try leb.readIleb128(i64, reader),
273 },
274 },
275 .val_offset => .{
276 .val_offset = .{
277 .register = try leb.readUleb128(u8, reader),
278 .offset = try leb.readUleb128(u64, reader),
279 },
280 },
281 .val_offset_sf => .{
282 .val_offset_sf = .{
283 .register = try leb.readUleb128(u8, reader),
284 .offset = try leb.readIleb128(i64, reader),
285 },
286 },
287 .val_expression => .{
288 .val_expression = .{
289 .register = try leb.readUleb128(u8, reader),
290 .block = try readBlock(stream),
291 },
292 },
293 };
294 },
295 Opcode.lo_user...Opcode.hi_user => return error.UnimplementedUserOpcode,
296 else => return error.InvalidOpcode,
297 }
298 }
299};
300
301/// Since register rules are applied (usually) during a panic,
302/// checked addition / subtraction is used so that we can return
303/// an error and fall back to FP-based unwinding.
304pub fn applyOffset(base: usize, offset: i64) !usize {
305 return if (offset >= 0)
306 try std.math.add(usize, base, @as(usize, @intCast(offset)))
307 else
308 try std.math.sub(usize, base, @as(usize, @intCast(-offset)));
309}
310
311/// This is a virtual machine that runs DWARF call frame instructions.
312pub const VirtualMachine = struct {
313 /// See section 6.4.1 of the DWARF5 specification for details on each
314 const RegisterRule = union(enum) {
315 // The spec says that the default rule for each column is the undefined rule.
316 // However, it also allows ABI / compiler authors to specify alternate defaults, so
317 // there is a distinction made here.
318 default: void,
319
320 undefined: void,
321 same_value: void,
322
323 // offset(N)
324 offset: i64,
325
326 // val_offset(N)
327 val_offset: i64,
328
329 // register(R)
330 register: u8,
331
332 // expression(E)
333 expression: []const u8,
334
335 // val_expression(E)
336 val_expression: []const u8,
337
338 // Augmenter-defined rule
339 architectural: void,
340 };
341
342 /// Each row contains unwinding rules for a set of registers.
343 pub const Row = struct {
344 /// Offset from `FrameDescriptionEntry.pc_begin`
345 offset: u64 = 0,
346
347 /// Special-case column that defines the CFA (Canonical Frame Address) rule.
348 /// The register field of this column defines the register that CFA is derived from.
349 cfa: Column = .{},
350
351 /// The register fields in these columns define the register the rule applies to.
352 columns: ColumnRange = .{},
353
354 /// Indicates that the next write to any column in this row needs to copy
355 /// the backing column storage first, as it may be referenced by previous rows.
356 copy_on_write: bool = false,
357 };
358
359 pub const Column = struct {
360 register: ?u8 = null,
361 rule: RegisterRule = .{ .default = {} },
362
363 /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
364 pub fn resolveValue(
365 self: Column,
366 context: *std.debug.Dwarf.UnwindContext,
367 expression_context: std.debug.Dwarf.expression.Context,
368 ma: *debug.StackIterator.MemoryAccessor,
369 out: []u8,
370 ) !void {
371 switch (self.rule) {
372 .default => {
373 const register = self.register orelse return error.InvalidRegister;
374 try abi.getRegDefaultValue(register, context, out);
375 },
376 .undefined => {
377 @memset(out, undefined);
378 },
379 .same_value => {
380 // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
381 const register = self.register orelse return error.InvalidRegister;
382 const src = try abi.regBytes(context.thread_context, register, context.reg_context);
383 if (src.len != out.len) return error.RegisterSizeMismatch;
384 @memcpy(out, src);
385 },
386 .offset => |offset| {
387 if (context.cfa) |cfa| {
388 const addr = try applyOffset(cfa, offset);
389 if (ma.load(usize, addr) == null) return error.InvalidAddress;
390 const ptr: *const usize = @ptrFromInt(addr);
391 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
392 } else return error.InvalidCFA;
393 },
394 .val_offset => |offset| {
395 if (context.cfa) |cfa| {
396 mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
397 } else return error.InvalidCFA;
398 },
399 .register => |register| {
400 const src = try abi.regBytes(context.thread_context, register, context.reg_context);
401 if (src.len != out.len) return error.RegisterSizeMismatch;
402 @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context));
403 },
404 .expression => |expression| {
405 context.stack_machine.reset();
406 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
407 const addr = if (value) |v| blk: {
408 if (v != .generic) return error.InvalidExpressionValue;
409 break :blk v.generic;
410 } else return error.NoExpressionValue;
411
412 if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress;
413 const ptr: *usize = @ptrFromInt(addr);
414 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
415 },
416 .val_expression => |expression| {
417 context.stack_machine.reset();
418 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
419 if (value) |v| {
420 if (v != .generic) return error.InvalidExpressionValue;
421 mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian);
422 } else return error.NoExpressionValue;
423 },
424 .architectural => return error.UnimplementedRegisterRule,
425 }
426 }
427 };
428
429 const ColumnRange = struct {
430 /// Index into `columns` of the first column in this row.
431 start: usize = undefined,
432 len: u8 = 0,
433 };
434
435 columns: std.ArrayListUnmanaged(Column) = .{},
436 stack: std.ArrayListUnmanaged(ColumnRange) = .{},
437 current_row: Row = .{},
438
439 /// The result of executing the CIE's initial_instructions
440 cie_row: ?Row = null,
441
442 pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
443 self.stack.deinit(allocator);
444 self.columns.deinit(allocator);
445 self.* = undefined;
446 }
447
448 pub fn reset(self: *VirtualMachine) void {
449 self.stack.clearRetainingCapacity();
450 self.columns.clearRetainingCapacity();
451 self.current_row = .{};
452 self.cie_row = null;
453 }
454
455 /// Return a slice backed by the row's non-CFA columns
456 pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
457 if (row.columns.len == 0) return &.{};
458 return self.columns.items[row.columns.start..][0..row.columns.len];
459 }
460
461 /// Either retrieves or adds a column for `register` (non-CFA) in the current row.
462 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
463 for (self.rowColumns(self.current_row)) |*c| {
464 if (c.register == register) return c;
465 }
466
467 if (self.current_row.columns.len == 0) {
468 self.current_row.columns.start = self.columns.items.len;
469 }
470 self.current_row.columns.len += 1;
471
472 const column = try self.columns.addOne(allocator);
473 column.* = .{
474 .register = register,
475 };
476
477 return column;
478 }
479
480 /// Runs the CIE instructions, then the FDE instructions. Execution halts
481 /// once the row that corresponds to `pc` is known, and the row is returned.
482 pub fn runTo(
483 self: *VirtualMachine,
484 allocator: std.mem.Allocator,
485 pc: u64,
486 cie: std.debug.Dwarf.CommonInformationEntry,
487 fde: std.debug.Dwarf.FrameDescriptionEntry,
488 addr_size_bytes: u8,
489 endian: std.builtin.Endian,
490 ) !Row {
491 assert(self.cie_row == null);
492 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
493
494 var prev_row: Row = self.current_row;
495
496 var cie_stream = std.io.fixedBufferStream(cie.initial_instructions);
497 var fde_stream = std.io.fixedBufferStream(fde.instructions);
498 var streams = [_]*std.io.FixedBufferStream([]const u8){
499 &cie_stream,
500 &fde_stream,
501 };
502
503 for (&streams, 0..) |stream, i| {
504 while (stream.pos < stream.buffer.len) {
505 const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
506 prev_row = try self.step(allocator, cie, i == 0, instruction);
507 if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
508 }
509 }
510
511 return self.current_row;
512 }
513
514 pub fn runToNative(
515 self: *VirtualMachine,
516 allocator: std.mem.Allocator,
517 pc: u64,
518 cie: std.debug.Dwarf.CommonInformationEntry,
519 fde: std.debug.Dwarf.FrameDescriptionEntry,
520 ) !Row {
521 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());
522 }
523
524 fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {
525 if (!self.current_row.copy_on_write) return;
526
527 const new_start = self.columns.items.len;
528 if (self.current_row.columns.len > 0) {
529 try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);
530 self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
531 self.current_row.columns.start = new_start;
532 }
533 }
534
535 /// Executes a single instruction.
536 /// If this instruction is from the CIE, `is_initial` should be set.
537 /// Returns the value of `current_row` before executing this instruction.
538 pub fn step(
539 self: *VirtualMachine,
540 allocator: std.mem.Allocator,
541 cie: std.debug.Dwarf.CommonInformationEntry,
542 is_initial: bool,
543 instruction: Instruction,
544 ) !Row {
545 // CIE instructions must be run before FDE instructions
546 assert(!is_initial or self.cie_row == null);
547 if (!is_initial and self.cie_row == null) {
548 self.cie_row = self.current_row;
549 self.current_row.copy_on_write = true;
550 }
551
552 const prev_row = self.current_row;
553 switch (instruction) {
554 .set_loc => |i| {
555 if (i.address <= self.current_row.offset) return error.InvalidOperation;
556 // TODO: Check cie.segment_selector_size != 0 for DWARFV4
557 self.current_row.offset = i.address;
558 },
559 inline .advance_loc,
560 .advance_loc1,
561 .advance_loc2,
562 .advance_loc4,
563 => |i| {
564 self.current_row.offset += i.delta * cie.code_alignment_factor;
565 self.current_row.copy_on_write = true;
566 },
567 inline .offset,
568 .offset_extended,
569 .offset_extended_sf,
570 => |i| {
571 try self.resolveCopyOnWrite(allocator);
572 const column = try self.getOrAddColumn(allocator, i.register);
573 column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
574 },
575 inline .restore,
576 .restore_extended,
577 => |i| {
578 try self.resolveCopyOnWrite(allocator);
579 if (self.cie_row) |cie_row| {
580 const column = try self.getOrAddColumn(allocator, i.register);
581 column.rule = for (self.rowColumns(cie_row)) |cie_column| {
582 if (cie_column.register == i.register) break cie_column.rule;
583 } else .{ .default = {} };
584 } else return error.InvalidOperation;
585 },
586 .nop => {},
587 .undefined => |i| {
588 try self.resolveCopyOnWrite(allocator);
589 const column = try self.getOrAddColumn(allocator, i.register);
590 column.rule = .{ .undefined = {} };
591 },
592 .same_value => |i| {
593 try self.resolveCopyOnWrite(allocator);
594 const column = try self.getOrAddColumn(allocator, i.register);
595 column.rule = .{ .same_value = {} };
596 },
597 .register => |i| {
598 try self.resolveCopyOnWrite(allocator);
599 const column = try self.getOrAddColumn(allocator, i.register);
600 column.rule = .{ .register = i.target_register };
601 },
602 .remember_state => {
603 try self.stack.append(allocator, self.current_row.columns);
604 self.current_row.copy_on_write = true;
605 },
606 .restore_state => {
607 const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
608 self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
609 try self.columns.ensureUnusedCapacity(allocator, restored_columns.len);
610
611 self.current_row.columns.start = self.columns.items.len;
612 self.current_row.columns.len = restored_columns.len;
613 self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
614 },
615 .def_cfa => |i| {
616 try self.resolveCopyOnWrite(allocator);
617 self.current_row.cfa = .{
618 .register = i.register,
619 .rule = .{ .val_offset = @intCast(i.offset) },
620 };
621 },
622 .def_cfa_sf => |i| {
623 try self.resolveCopyOnWrite(allocator);
624 self.current_row.cfa = .{
625 .register = i.register,
626 .rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
627 };
628 },
629 .def_cfa_register => |i| {
630 try self.resolveCopyOnWrite(allocator);
631 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
632 self.current_row.cfa.register = i.register;
633 },
634 .def_cfa_offset => |i| {
635 try self.resolveCopyOnWrite(allocator);
636 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
637 self.current_row.cfa.rule = .{
638 .val_offset = @intCast(i.offset),
639 };
640 },
641 .def_cfa_offset_sf => |i| {
642 try self.resolveCopyOnWrite(allocator);
643 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
644 self.current_row.cfa.rule = .{
645 .val_offset = i.offset * cie.data_alignment_factor,
646 };
647 },
648 .def_cfa_expression => |i| {
649 try self.resolveCopyOnWrite(allocator);
650 self.current_row.cfa.register = undefined;
651 self.current_row.cfa.rule = .{
652 .expression = i.block,
653 };
654 },
655 .expression => |i| {
656 try self.resolveCopyOnWrite(allocator);
657 const column = try self.getOrAddColumn(allocator, i.register);
658 column.rule = .{
659 .expression = i.block,
660 };
661 },
662 .val_offset => |i| {
663 try self.resolveCopyOnWrite(allocator);
664 const column = try self.getOrAddColumn(allocator, i.register);
665 column.rule = .{
666 .val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
667 };
668 },
669 .val_offset_sf => |i| {
670 try self.resolveCopyOnWrite(allocator);
671 const column = try self.getOrAddColumn(allocator, i.register);
672 column.rule = .{
673 .val_offset = i.offset * cie.data_alignment_factor,
674 };
675 },
676 .val_expression => |i| {
677 try self.resolveCopyOnWrite(allocator);
678 const column = try self.getOrAddColumn(allocator, i.register);
679 column.rule = .{
680 .val_expression = i.block,
681 };
682 },
683 }
684
685 return prev_row;
686 }
687};
lib/std/debug/Dwarf/expression.zig created+1638
......@@ -0,0 +1,1638 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const leb = std.leb;
4const OP = std.dwarf.OP;
5const abi = std.debug.Dwarf.abi;
6const mem = std.mem;
7const assert = std.debug.assert;
8const native_endian = builtin.cpu.arch.endian();
9
10/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
11/// Callers should specify all the fields relevant to their context. If a field is required
12/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
13pub const Context = struct {
14 /// The dwarf format of the section this expression is in
15 format: std.dwarf.Format = .@"32",
16 /// If specified, any addresses will pass through before being accessed
17 memory_accessor: ?*std.debug.StackIterator.MemoryAccessor = null,
18 /// The compilation unit this expression relates to, if any
19 compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,
20 /// When evaluating a user-presented expression, this is the address of the object being evaluated
21 object_address: ?*const anyopaque = null,
22 /// .debug_addr section
23 debug_addr: ?[]const u8 = null,
24 /// Thread context
25 thread_context: ?*std.debug.ThreadContext = null,
26 reg_context: ?abi.RegisterContext = null,
27 /// Call frame address, if in a CFI context
28 cfa: ?usize = null,
29 /// This expression is a sub-expression from an OP.entry_value instruction
30 entry_value_context: bool = false,
31};
32
33pub const Options = struct {
34 /// The address size of the target architecture
35 addr_size: u8 = @sizeOf(usize),
36 /// Endianness of the target architecture
37 endian: std.builtin.Endian = builtin.target.cpu.arch.endian(),
38 /// Restrict the stack machine to a subset of opcodes used in call frame instructions
39 call_frame_context: bool = false,
40};
41
42// Explicitly defined to support executing sub-expressions
43pub const Error = error{
44 UnimplementedExpressionCall,
45 UnimplementedOpcode,
46 UnimplementedUserOpcode,
47 UnimplementedTypedComparison,
48 UnimplementedTypeConversion,
49
50 UnknownExpressionOpcode,
51
52 IncompleteExpressionContext,
53
54 InvalidCFAOpcode,
55 InvalidExpression,
56 InvalidFrameBase,
57 InvalidIntegralTypeSize,
58 InvalidRegister,
59 InvalidSubExpression,
60 InvalidTypeLength,
61
62 TruncatedIntegralType,
63} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
64
65/// A stack machine that can decode and run DWARF expressions.
66/// Expressions can be decoded for non-native address size and endianness,
67/// but can only be executed if the current target matches the configuration.
68pub fn StackMachine(comptime options: Options) type {
69 const addr_type = switch (options.addr_size) {
70 2 => u16,
71 4 => u32,
72 8 => u64,
73 else => @compileError("Unsupported address size of " ++ options.addr_size),
74 };
75
76 const addr_type_signed = switch (options.addr_size) {
77 2 => i16,
78 4 => i32,
79 8 => i64,
80 else => @compileError("Unsupported address size of " ++ options.addr_size),
81 };
82
83 return struct {
84 const Self = @This();
85
86 const Operand = union(enum) {
87 generic: addr_type,
88 register: u8,
89 type_size: u8,
90 branch_offset: i16,
91 base_register: struct {
92 base_register: u8,
93 offset: i64,
94 },
95 composite_location: struct {
96 size: u64,
97 offset: i64,
98 },
99 block: []const u8,
100 register_type: struct {
101 register: u8,
102 type_offset: addr_type,
103 },
104 const_type: struct {
105 type_offset: addr_type,
106 value_bytes: []const u8,
107 },
108 deref_type: struct {
109 size: u8,
110 type_offset: addr_type,
111 },
112 };
113
114 const Value = union(enum) {
115 generic: addr_type,
116
117 // Typed value with a maximum size of a register
118 regval_type: struct {
119 // Offset of DW_TAG_base_type DIE
120 type_offset: addr_type,
121 type_size: u8,
122 value: addr_type,
123 },
124
125 // Typed value specified directly in the instruction stream
126 const_type: struct {
127 // Offset of DW_TAG_base_type DIE
128 type_offset: addr_type,
129 // Backed by the instruction stream
130 value_bytes: []const u8,
131 },
132
133 pub fn asIntegral(self: Value) !addr_type {
134 return switch (self) {
135 .generic => |v| v,
136
137 // TODO: For these two prongs, look up the type and assert it's integral?
138 .regval_type => |regval_type| regval_type.value,
139 .const_type => |const_type| {
140 const value: u64 = switch (const_type.value_bytes.len) {
141 1 => mem.readInt(u8, const_type.value_bytes[0..1], native_endian),
142 2 => mem.readInt(u16, const_type.value_bytes[0..2], native_endian),
143 4 => mem.readInt(u32, const_type.value_bytes[0..4], native_endian),
144 8 => mem.readInt(u64, const_type.value_bytes[0..8], native_endian),
145 else => return error.InvalidIntegralTypeSize,
146 };
147
148 return std.math.cast(addr_type, value) orelse error.TruncatedIntegralType;
149 },
150 };
151 }
152 };
153
154 stack: std.ArrayListUnmanaged(Value) = .{},
155
156 pub fn reset(self: *Self) void {
157 self.stack.clearRetainingCapacity();
158 }
159
160 pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
161 self.stack.deinit(allocator);
162 }
163
164 fn generic(value: anytype) Operand {
165 const int_info = @typeInfo(@TypeOf(value)).Int;
166 if (@sizeOf(@TypeOf(value)) > options.addr_size) {
167 return .{ .generic = switch (int_info.signedness) {
168 .signed => @bitCast(@as(addr_type_signed, @truncate(value))),
169 .unsigned => @truncate(value),
170 } };
171 } else {
172 return .{ .generic = switch (int_info.signedness) {
173 .signed => @bitCast(@as(addr_type_signed, @intCast(value))),
174 .unsigned => @intCast(value),
175 } };
176 }
177 }
178
179 pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8, context: Context) !?Operand {
180 const reader = stream.reader();
181 return switch (opcode) {
182 OP.addr => generic(try reader.readInt(addr_type, options.endian)),
183 OP.call_ref => switch (context.format) {
184 .@"32" => generic(try reader.readInt(u32, options.endian)),
185 .@"64" => generic(try reader.readInt(u64, options.endian)),
186 },
187 OP.const1u,
188 OP.pick,
189 => generic(try reader.readByte()),
190 OP.deref_size,
191 OP.xderef_size,
192 => .{ .type_size = try reader.readByte() },
193 OP.const1s => generic(try reader.readByteSigned()),
194 OP.const2u,
195 OP.call2,
196 => generic(try reader.readInt(u16, options.endian)),
197 OP.call4 => generic(try reader.readInt(u32, options.endian)),
198 OP.const2s => generic(try reader.readInt(i16, options.endian)),
199 OP.bra,
200 OP.skip,
201 => .{ .branch_offset = try reader.readInt(i16, options.endian) },
202 OP.const4u => generic(try reader.readInt(u32, options.endian)),
203 OP.const4s => generic(try reader.readInt(i32, options.endian)),
204 OP.const8u => generic(try reader.readInt(u64, options.endian)),
205 OP.const8s => generic(try reader.readInt(i64, options.endian)),
206 OP.constu,
207 OP.plus_uconst,
208 OP.addrx,
209 OP.constx,
210 OP.convert,
211 OP.reinterpret,
212 => generic(try leb.readUleb128(u64, reader)),
213 OP.consts,
214 OP.fbreg,
215 => generic(try leb.readIleb128(i64, reader)),
216 OP.lit0...OP.lit31 => |n| generic(n - OP.lit0),
217 OP.reg0...OP.reg31 => |n| .{ .register = n - OP.reg0 },
218 OP.breg0...OP.breg31 => |n| .{ .base_register = .{
219 .base_register = n - OP.breg0,
220 .offset = try leb.readIleb128(i64, reader),
221 } },
222 OP.regx => .{ .register = try leb.readUleb128(u8, reader) },
223 OP.bregx => blk: {
224 const base_register = try leb.readUleb128(u8, reader);
225 const offset = try leb.readIleb128(i64, reader);
226 break :blk .{ .base_register = .{
227 .base_register = base_register,
228 .offset = offset,
229 } };
230 },
231 OP.regval_type => blk: {
232 const register = try leb.readUleb128(u8, reader);
233 const type_offset = try leb.readUleb128(addr_type, reader);
234 break :blk .{ .register_type = .{
235 .register = register,
236 .type_offset = type_offset,
237 } };
238 },
239 OP.piece => .{
240 .composite_location = .{
241 .size = try leb.readUleb128(u8, reader),
242 .offset = 0,
243 },
244 },
245 OP.bit_piece => blk: {
246 const size = try leb.readUleb128(u8, reader);
247 const offset = try leb.readIleb128(i64, reader);
248 break :blk .{ .composite_location = .{
249 .size = size,
250 .offset = offset,
251 } };
252 },
253 OP.implicit_value, OP.entry_value => blk: {
254 const size = try leb.readUleb128(u8, reader);
255 if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
256 const block = stream.buffer[stream.pos..][0..size];
257 stream.pos += size;
258 break :blk .{
259 .block = block,
260 };
261 },
262 OP.const_type => blk: {
263 const type_offset = try leb.readUleb128(addr_type, reader);
264 const size = try reader.readByte();
265 if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
266 const value_bytes = stream.buffer[stream.pos..][0..size];
267 stream.pos += size;
268 break :blk .{ .const_type = .{
269 .type_offset = type_offset,
270 .value_bytes = value_bytes,
271 } };
272 },
273 OP.deref_type,
274 OP.xderef_type,
275 => .{
276 .deref_type = .{
277 .size = try reader.readByte(),
278 .type_offset = try leb.readUleb128(addr_type, reader),
279 },
280 },
281 OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,
282 else => null,
283 };
284 }
285
286 pub fn run(
287 self: *Self,
288 expression: []const u8,
289 allocator: std.mem.Allocator,
290 context: Context,
291 initial_value: ?usize,
292 ) Error!?Value {
293 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
294 var stream = std.io.fixedBufferStream(expression);
295 while (try self.step(&stream, allocator, context)) {}
296 if (self.stack.items.len == 0) return null;
297 return self.stack.items[self.stack.items.len - 1];
298 }
299
300 /// Reads an opcode and its operands from `stream`, then executes it
301 pub fn step(
302 self: *Self,
303 stream: *std.io.FixedBufferStream([]const u8),
304 allocator: std.mem.Allocator,
305 context: Context,
306 ) Error!bool {
307 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
308 @compileError("Execution of non-native address sizes / endianness is not supported");
309
310 const opcode = try stream.reader().readByte();
311 if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
312 const operand = try readOperand(stream, opcode, context);
313 switch (opcode) {
314
315 // 2.5.1.1: Literal Encodings
316 OP.lit0...OP.lit31,
317 OP.addr,
318 OP.const1u,
319 OP.const2u,
320 OP.const4u,
321 OP.const8u,
322 OP.const1s,
323 OP.const2s,
324 OP.const4s,
325 OP.const8s,
326 OP.constu,
327 OP.consts,
328 => try self.stack.append(allocator, .{ .generic = operand.?.generic }),
329
330 OP.const_type => {
331 const const_type = operand.?.const_type;
332 try self.stack.append(allocator, .{ .const_type = .{
333 .type_offset = const_type.type_offset,
334 .value_bytes = const_type.value_bytes,
335 } });
336 },
337
338 OP.addrx,
339 OP.constx,
340 => {
341 if (context.compile_unit == null) return error.IncompleteExpressionContext;
342 if (context.debug_addr == null) return error.IncompleteExpressionContext;
343 const debug_addr_index = operand.?.generic;
344 const offset = context.compile_unit.?.addr_base + debug_addr_index;
345 if (offset >= context.debug_addr.?.len) return error.InvalidExpression;
346 const value = mem.readInt(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)], native_endian);
347 try self.stack.append(allocator, .{ .generic = value });
348 },
349
350 // 2.5.1.2: Register Values
351 OP.fbreg => {
352 if (context.compile_unit == null) return error.IncompleteExpressionContext;
353 if (context.compile_unit.?.frame_base == null) return error.IncompleteExpressionContext;
354
355 const offset: i64 = @intCast(operand.?.generic);
356 _ = offset;
357
358 switch (context.compile_unit.?.frame_base.?.*) {
359 .exprloc => {
360 // TODO: Run this expression in a nested stack machine
361 return error.UnimplementedOpcode;
362 },
363 .loclistx => {
364 // TODO: Read value from .debug_loclists
365 return error.UnimplementedOpcode;
366 },
367 .sec_offset => {
368 // TODO: Read value from .debug_loclists
369 return error.UnimplementedOpcode;
370 },
371 else => return error.InvalidFrameBase,
372 }
373 },
374 OP.breg0...OP.breg31,
375 OP.bregx,
376 => {
377 if (context.thread_context == null) return error.IncompleteExpressionContext;
378
379 const base_register = operand.?.base_register;
380 var value: i64 = @intCast(mem.readInt(usize, (try abi.regBytes(
381 context.thread_context.?,
382 base_register.base_register,
383 context.reg_context,
384 ))[0..@sizeOf(usize)], native_endian));
385 value += base_register.offset;
386 try self.stack.append(allocator, .{ .generic = @intCast(value) });
387 },
388 OP.regval_type => {
389 const register_type = operand.?.register_type;
390 const value = mem.readInt(usize, (try abi.regBytes(
391 context.thread_context.?,
392 register_type.register,
393 context.reg_context,
394 ))[0..@sizeOf(usize)], native_endian);
395 try self.stack.append(allocator, .{
396 .regval_type = .{
397 .type_offset = register_type.type_offset,
398 .type_size = @sizeOf(addr_type),
399 .value = value,
400 },
401 });
402 },
403
404 // 2.5.1.3: Stack Operations
405 OP.dup => {
406 if (self.stack.items.len == 0) return error.InvalidExpression;
407 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1]);
408 },
409 OP.drop => {
410 _ = self.stack.pop();
411 },
412 OP.pick, OP.over => {
413 const stack_index = if (opcode == OP.over) 1 else operand.?.generic;
414 if (stack_index >= self.stack.items.len) return error.InvalidExpression;
415 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
416 },
417 OP.swap => {
418 if (self.stack.items.len < 2) return error.InvalidExpression;
419 mem.swap(Value, &self.stack.items[self.stack.items.len - 1], &self.stack.items[self.stack.items.len - 2]);
420 },
421 OP.rot => {
422 if (self.stack.items.len < 3) return error.InvalidExpression;
423 const first = self.stack.items[self.stack.items.len - 1];
424 self.stack.items[self.stack.items.len - 1] = self.stack.items[self.stack.items.len - 2];
425 self.stack.items[self.stack.items.len - 2] = self.stack.items[self.stack.items.len - 3];
426 self.stack.items[self.stack.items.len - 3] = first;
427 },
428 OP.deref,
429 OP.xderef,
430 OP.deref_size,
431 OP.xderef_size,
432 OP.deref_type,
433 OP.xderef_type,
434 => {
435 if (self.stack.items.len == 0) return error.InvalidExpression;
436 const addr = try self.stack.items[self.stack.items.len - 1].asIntegral();
437 const addr_space_identifier: ?usize = switch (opcode) {
438 OP.xderef,
439 OP.xderef_size,
440 OP.xderef_type,
441 => blk: {
442 _ = self.stack.pop();
443 if (self.stack.items.len == 0) return error.InvalidExpression;
444 break :blk try self.stack.items[self.stack.items.len - 1].asIntegral();
445 },
446 else => null,
447 };
448
449 // Usage of addr_space_identifier in the address calculation is implementation defined.
450 // This code will need to be updated to handle any architectures that utilize this.
451 _ = addr_space_identifier;
452
453 const size = switch (opcode) {
454 OP.deref,
455 OP.xderef,
456 => @sizeOf(addr_type),
457 OP.deref_size,
458 OP.xderef_size,
459 => operand.?.type_size,
460 OP.deref_type,
461 OP.xderef_type,
462 => operand.?.deref_type.size,
463 else => unreachable,
464 };
465
466 if (context.memory_accessor) |memory_accessor| {
467 if (!switch (size) {
468 1 => memory_accessor.load(u8, addr) != null,
469 2 => memory_accessor.load(u16, addr) != null,
470 4 => memory_accessor.load(u32, addr) != null,
471 8 => memory_accessor.load(u64, addr) != null,
472 else => return error.InvalidExpression,
473 }) return error.InvalidExpression;
474 }
475
476 const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) {
477 1 => @as(*const u8, @ptrFromInt(addr)).*,
478 2 => @as(*const u16, @ptrFromInt(addr)).*,
479 4 => @as(*const u32, @ptrFromInt(addr)).*,
480 8 => @as(*const u64, @ptrFromInt(addr)).*,
481 else => return error.InvalidExpression,
482 })) orelse return error.InvalidExpression;
483
484 switch (opcode) {
485 OP.deref_type,
486 OP.xderef_type,
487 => {
488 self.stack.items[self.stack.items.len - 1] = .{
489 .regval_type = .{
490 .type_offset = operand.?.deref_type.type_offset,
491 .type_size = operand.?.deref_type.size,
492 .value = value,
493 },
494 };
495 },
496 else => {
497 self.stack.items[self.stack.items.len - 1] = .{ .generic = value };
498 },
499 }
500 },
501 OP.push_object_address => {
502 // In sub-expressions, `push_object_address` is not meaningful (as per the
503 // spec), so treat it like a nop
504 if (!context.entry_value_context) {
505 if (context.object_address == null) return error.IncompleteExpressionContext;
506 try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });
507 }
508 },
509 OP.form_tls_address => {
510 return error.UnimplementedOpcode;
511 },
512 OP.call_frame_cfa => {
513 if (context.cfa) |cfa| {
514 try self.stack.append(allocator, .{ .generic = cfa });
515 } else return error.IncompleteExpressionContext;
516 },
517
518 // 2.5.1.4: Arithmetic and Logical Operations
519 OP.abs => {
520 if (self.stack.items.len == 0) return error.InvalidExpression;
521 const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
522 self.stack.items[self.stack.items.len - 1] = .{
523 .generic = @abs(value),
524 };
525 },
526 OP.@"and" => {
527 if (self.stack.items.len < 2) return error.InvalidExpression;
528 const a = try self.stack.pop().asIntegral();
529 self.stack.items[self.stack.items.len - 1] = .{
530 .generic = a & try self.stack.items[self.stack.items.len - 1].asIntegral(),
531 };
532 },
533 OP.div => {
534 if (self.stack.items.len < 2) return error.InvalidExpression;
535 const a: isize = @bitCast(try self.stack.pop().asIntegral());
536 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
537 self.stack.items[self.stack.items.len - 1] = .{
538 .generic = @bitCast(try std.math.divTrunc(isize, b, a)),
539 };
540 },
541 OP.minus => {
542 if (self.stack.items.len < 2) return error.InvalidExpression;
543 const b = try self.stack.pop().asIntegral();
544 self.stack.items[self.stack.items.len - 1] = .{
545 .generic = try std.math.sub(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
546 };
547 },
548 OP.mod => {
549 if (self.stack.items.len < 2) return error.InvalidExpression;
550 const a: isize = @bitCast(try self.stack.pop().asIntegral());
551 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
552 self.stack.items[self.stack.items.len - 1] = .{
553 .generic = @bitCast(@mod(b, a)),
554 };
555 },
556 OP.mul => {
557 if (self.stack.items.len < 2) return error.InvalidExpression;
558 const a: isize = @bitCast(try self.stack.pop().asIntegral());
559 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
560 self.stack.items[self.stack.items.len - 1] = .{
561 .generic = @bitCast(@mulWithOverflow(a, b)[0]),
562 };
563 },
564 OP.neg => {
565 if (self.stack.items.len == 0) return error.InvalidExpression;
566 self.stack.items[self.stack.items.len - 1] = .{
567 .generic = @bitCast(
568 try std.math.negate(
569 @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
570 ),
571 ),
572 };
573 },
574 OP.not => {
575 if (self.stack.items.len == 0) return error.InvalidExpression;
576 self.stack.items[self.stack.items.len - 1] = .{
577 .generic = ~try self.stack.items[self.stack.items.len - 1].asIntegral(),
578 };
579 },
580 OP.@"or" => {
581 if (self.stack.items.len < 2) return error.InvalidExpression;
582 const a = try self.stack.pop().asIntegral();
583 self.stack.items[self.stack.items.len - 1] = .{
584 .generic = a | try self.stack.items[self.stack.items.len - 1].asIntegral(),
585 };
586 },
587 OP.plus => {
588 if (self.stack.items.len < 2) return error.InvalidExpression;
589 const b = try self.stack.pop().asIntegral();
590 self.stack.items[self.stack.items.len - 1] = .{
591 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
592 };
593 },
594 OP.plus_uconst => {
595 if (self.stack.items.len == 0) return error.InvalidExpression;
596 const constant = operand.?.generic;
597 self.stack.items[self.stack.items.len - 1] = .{
598 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
599 };
600 },
601 OP.shl => {
602 if (self.stack.items.len < 2) return error.InvalidExpression;
603 const a = try self.stack.pop().asIntegral();
604 const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
605 self.stack.items[self.stack.items.len - 1] = .{
606 .generic = std.math.shl(usize, b, a),
607 };
608 },
609 OP.shr => {
610 if (self.stack.items.len < 2) return error.InvalidExpression;
611 const a = try self.stack.pop().asIntegral();
612 const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
613 self.stack.items[self.stack.items.len - 1] = .{
614 .generic = std.math.shr(usize, b, a),
615 };
616 },
617 OP.shra => {
618 if (self.stack.items.len < 2) return error.InvalidExpression;
619 const a = try self.stack.pop().asIntegral();
620 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
621 self.stack.items[self.stack.items.len - 1] = .{
622 .generic = @bitCast(std.math.shr(isize, b, a)),
623 };
624 },
625 OP.xor => {
626 if (self.stack.items.len < 2) return error.InvalidExpression;
627 const a = try self.stack.pop().asIntegral();
628 self.stack.items[self.stack.items.len - 1] = .{
629 .generic = a ^ try self.stack.items[self.stack.items.len - 1].asIntegral(),
630 };
631 },
632
633 // 2.5.1.5: Control Flow Operations
634 OP.le,
635 OP.ge,
636 OP.eq,
637 OP.lt,
638 OP.gt,
639 OP.ne,
640 => {
641 if (self.stack.items.len < 2) return error.InvalidExpression;
642 const a = self.stack.pop();
643 const b = self.stack.items[self.stack.items.len - 1];
644
645 if (a == .generic and b == .generic) {
646 const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
647 const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
648 const result = @intFromBool(switch (opcode) {
649 OP.le => b_int <= a_int,
650 OP.ge => b_int >= a_int,
651 OP.eq => b_int == a_int,
652 OP.lt => b_int < a_int,
653 OP.gt => b_int > a_int,
654 OP.ne => b_int != a_int,
655 else => unreachable,
656 });
657
658 self.stack.items[self.stack.items.len - 1] = .{ .generic = result };
659 } else {
660 // TODO: Load the types referenced by these values, find their comparison operator, and run it
661 return error.UnimplementedTypedComparison;
662 }
663 },
664 OP.skip, OP.bra => {
665 const branch_offset = operand.?.branch_offset;
666 const condition = if (opcode == OP.bra) blk: {
667 if (self.stack.items.len == 0) return error.InvalidExpression;
668 break :blk try self.stack.pop().asIntegral() != 0;
669 } else true;
670
671 if (condition) {
672 const new_pos = std.math.cast(
673 usize,
674 try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
675 ) orelse return error.InvalidExpression;
676
677 if (new_pos < 0 or new_pos > stream.buffer.len) return error.InvalidExpression;
678 stream.pos = new_pos;
679 }
680 },
681 OP.call2,
682 OP.call4,
683 OP.call_ref,
684 => {
685 const debug_info_offset = operand.?.generic;
686 _ = debug_info_offset;
687
688 // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it
689 // can be in a separate exe / shared object from the one containing this expression).
690 // Transfer control to the DW_AT_location attribute, with the current stack as input.
691
692 return error.UnimplementedExpressionCall;
693 },
694
695 // 2.5.1.6: Type Conversions
696 OP.convert => {
697 if (self.stack.items.len == 0) return error.InvalidExpression;
698 const type_offset = operand.?.generic;
699
700 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
701 const value = self.stack.items[self.stack.items.len - 1];
702 if (type_offset == 0) {
703 self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
704 } else {
705 // TODO: Load the DW_TAG_base_type entry in context.compile_unit, find a conversion operator
706 // from the old type to the new type, run it.
707 return error.UnimplementedTypeConversion;
708 }
709 },
710 OP.reinterpret => {
711 if (self.stack.items.len == 0) return error.InvalidExpression;
712 const type_offset = operand.?.generic;
713
714 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
715 const value = self.stack.items[self.stack.items.len - 1];
716 if (type_offset == 0) {
717 self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
718 } else {
719 self.stack.items[self.stack.items.len - 1] = switch (value) {
720 .generic => |v| .{
721 .regval_type = .{
722 .type_offset = type_offset,
723 .type_size = @sizeOf(addr_type),
724 .value = v,
725 },
726 },
727 .regval_type => |r| .{
728 .regval_type = .{
729 .type_offset = type_offset,
730 .type_size = r.type_size,
731 .value = r.value,
732 },
733 },
734 .const_type => |c| .{
735 .const_type = .{
736 .type_offset = type_offset,
737 .value_bytes = c.value_bytes,
738 },
739 },
740 };
741 }
742 },
743
744 // 2.5.1.7: Special Operations
745 OP.nop => {},
746 OP.entry_value => {
747 const block = operand.?.block;
748 if (block.len == 0) return error.InvalidSubExpression;
749
750 // TODO: The spec states that this sub-expression needs to observe the state (ie. registers)
751 // as it was upon entering the current subprogram. If this isn't being called at the
752 // end of a frame unwind operation, an additional ThreadContext with this state will be needed.
753
754 if (isOpcodeRegisterLocation(block[0])) {
755 if (context.thread_context == null) return error.IncompleteExpressionContext;
756
757 var block_stream = std.io.fixedBufferStream(block);
758 const register = (try readOperand(&block_stream, block[0], context)).?.register;
759 const value = mem.readInt(usize, (try abi.regBytes(context.thread_context.?, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
760 try self.stack.append(allocator, .{ .generic = value });
761 } else {
762 var stack_machine: Self = .{};
763 defer stack_machine.deinit(allocator);
764
765 var sub_context = context;
766 sub_context.entry_value_context = true;
767 const result = try stack_machine.run(block, allocator, sub_context, null);
768 try self.stack.append(allocator, result orelse return error.InvalidSubExpression);
769 }
770 },
771
772 // These have already been handled by readOperand
773 OP.lo_user...OP.hi_user => unreachable,
774 else => {
775 //std.debug.print("Unknown DWARF expression opcode: {x}\n", .{opcode});
776 return error.UnknownExpressionOpcode;
777 },
778 }
779
780 return stream.pos < stream.buffer.len;
781 }
782 };
783}
784
785pub fn Builder(comptime options: Options) type {
786 const addr_type = switch (options.addr_size) {
787 2 => u16,
788 4 => u32,
789 8 => u64,
790 else => @compileError("Unsupported address size of " ++ options.addr_size),
791 };
792
793 return struct {
794 /// Zero-operand instructions
795 pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {
796 if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
797 switch (opcode) {
798 OP.dup,
799 OP.drop,
800 OP.over,
801 OP.swap,
802 OP.rot,
803 OP.deref,
804 OP.xderef,
805 OP.push_object_address,
806 OP.form_tls_address,
807 OP.call_frame_cfa,
808 OP.abs,
809 OP.@"and",
810 OP.div,
811 OP.minus,
812 OP.mod,
813 OP.mul,
814 OP.neg,
815 OP.not,
816 OP.@"or",
817 OP.plus,
818 OP.shl,
819 OP.shr,
820 OP.shra,
821 OP.xor,
822 OP.le,
823 OP.ge,
824 OP.eq,
825 OP.lt,
826 OP.gt,
827 OP.ne,
828 OP.nop,
829 OP.stack_value,
830 => try writer.writeByte(opcode),
831 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
832 }
833 }
834
835 // 2.5.1.1: Literal Encodings
836 pub fn writeLiteral(writer: anytype, literal: u8) !void {
837 switch (literal) {
838 0...31 => |n| try writer.writeByte(n + OP.lit0),
839 else => return error.InvalidLiteral,
840 }
841 }
842
843 pub fn writeConst(writer: anytype, comptime T: type, value: T) !void {
844 if (@typeInfo(T) != .Int) @compileError("Constants must be integers");
845
846 switch (T) {
847 u8, i8, u16, i16, u32, i32, u64, i64 => {
848 try writer.writeByte(switch (T) {
849 u8 => OP.const1u,
850 i8 => OP.const1s,
851 u16 => OP.const2u,
852 i16 => OP.const2s,
853 u32 => OP.const4u,
854 i32 => OP.const4s,
855 u64 => OP.const8u,
856 i64 => OP.const8s,
857 else => unreachable,
858 });
859
860 try writer.writeInt(T, value, options.endian);
861 },
862 else => switch (@typeInfo(T).Int.signedness) {
863 .unsigned => {
864 try writer.writeByte(OP.constu);
865 try leb.writeUleb128(writer, value);
866 },
867 .signed => {
868 try writer.writeByte(OP.consts);
869 try leb.writeIleb128(writer, value);
870 },
871 },
872 }
873 }
874
875 pub fn writeConstx(writer: anytype, debug_addr_offset: anytype) !void {
876 try writer.writeByte(OP.constx);
877 try leb.writeUleb128(writer, debug_addr_offset);
878 }
879
880 pub fn writeConstType(writer: anytype, die_offset: anytype, value_bytes: []const u8) !void {
881 if (options.call_frame_context) return error.InvalidCFAOpcode;
882 if (value_bytes.len > 0xff) return error.InvalidTypeLength;
883 try writer.writeByte(OP.const_type);
884 try leb.writeUleb128(writer, die_offset);
885 try writer.writeByte(@intCast(value_bytes.len));
886 try writer.writeAll(value_bytes);
887 }
888
889 pub fn writeAddr(writer: anytype, value: addr_type) !void {
890 try writer.writeByte(OP.addr);
891 try writer.writeInt(addr_type, value, options.endian);
892 }
893
894 pub fn writeAddrx(writer: anytype, debug_addr_offset: anytype) !void {
895 if (options.call_frame_context) return error.InvalidCFAOpcode;
896 try writer.writeByte(OP.addrx);
897 try leb.writeUleb128(writer, debug_addr_offset);
898 }
899
900 // 2.5.1.2: Register Values
901 pub fn writeFbreg(writer: anytype, offset: anytype) !void {
902 try writer.writeByte(OP.fbreg);
903 try leb.writeIleb128(writer, offset);
904 }
905
906 pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {
907 if (register > 31) return error.InvalidRegister;
908 try writer.writeByte(OP.breg0 + register);
909 try leb.writeIleb128(writer, offset);
910 }
911
912 pub fn writeBregx(writer: anytype, register: anytype, offset: anytype) !void {
913 try writer.writeByte(OP.bregx);
914 try leb.writeUleb128(writer, register);
915 try leb.writeIleb128(writer, offset);
916 }
917
918 pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {
919 if (options.call_frame_context) return error.InvalidCFAOpcode;
920 try writer.writeByte(OP.regval_type);
921 try leb.writeUleb128(writer, register);
922 try leb.writeUleb128(writer, offset);
923 }
924
925 // 2.5.1.3: Stack Operations
926 pub fn writePick(writer: anytype, index: u8) !void {
927 try writer.writeByte(OP.pick);
928 try writer.writeByte(index);
929 }
930
931 pub fn writeDerefSize(writer: anytype, size: u8) !void {
932 try writer.writeByte(OP.deref_size);
933 try writer.writeByte(size);
934 }
935
936 pub fn writeXDerefSize(writer: anytype, size: u8) !void {
937 try writer.writeByte(OP.xderef_size);
938 try writer.writeByte(size);
939 }
940
941 pub fn writeDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
942 if (options.call_frame_context) return error.InvalidCFAOpcode;
943 try writer.writeByte(OP.deref_type);
944 try writer.writeByte(size);
945 try leb.writeUleb128(writer, die_offset);
946 }
947
948 pub fn writeXDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
949 try writer.writeByte(OP.xderef_type);
950 try writer.writeByte(size);
951 try leb.writeUleb128(writer, die_offset);
952 }
953
954 // 2.5.1.4: Arithmetic and Logical Operations
955
956 pub fn writePlusUconst(writer: anytype, uint_value: anytype) !void {
957 try writer.writeByte(OP.plus_uconst);
958 try leb.writeUleb128(writer, uint_value);
959 }
960
961 // 2.5.1.5: Control Flow Operations
962
963 pub fn writeSkip(writer: anytype, offset: i16) !void {
964 try writer.writeByte(OP.skip);
965 try writer.writeInt(i16, offset, options.endian);
966 }
967
968 pub fn writeBra(writer: anytype, offset: i16) !void {
969 try writer.writeByte(OP.bra);
970 try writer.writeInt(i16, offset, options.endian);
971 }
972
973 pub fn writeCall(writer: anytype, comptime T: type, offset: T) !void {
974 if (options.call_frame_context) return error.InvalidCFAOpcode;
975 switch (T) {
976 u16 => try writer.writeByte(OP.call2),
977 u32 => try writer.writeByte(OP.call4),
978 else => @compileError("Call operand must be a 2 or 4 byte offset"),
979 }
980
981 try writer.writeInt(T, offset, options.endian);
982 }
983
984 pub fn writeCallRef(writer: anytype, comptime is_64: bool, value: if (is_64) u64 else u32) !void {
985 if (options.call_frame_context) return error.InvalidCFAOpcode;
986 try writer.writeByte(OP.call_ref);
987 try writer.writeInt(if (is_64) u64 else u32, value, options.endian);
988 }
989
990 pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
991 if (options.call_frame_context) return error.InvalidCFAOpcode;
992 try writer.writeByte(OP.convert);
993 try leb.writeUleb128(writer, die_offset);
994 }
995
996 pub fn writeReinterpret(writer: anytype, die_offset: anytype) !void {
997 if (options.call_frame_context) return error.InvalidCFAOpcode;
998 try writer.writeByte(OP.reinterpret);
999 try leb.writeUleb128(writer, die_offset);
1000 }
1001
1002 // 2.5.1.7: Special Operations
1003
1004 pub fn writeEntryValue(writer: anytype, expression: []const u8) !void {
1005 try writer.writeByte(OP.entry_value);
1006 try leb.writeUleb128(writer, expression.len);
1007 try writer.writeAll(expression);
1008 }
1009
1010 // 2.6: Location Descriptions
1011 pub fn writeReg(writer: anytype, register: u8) !void {
1012 try writer.writeByte(OP.reg0 + register);
1013 }
1014
1015 pub fn writeRegx(writer: anytype, register: anytype) !void {
1016 try writer.writeByte(OP.regx);
1017 try leb.writeUleb128(writer, register);
1018 }
1019
1020 pub fn writeImplicitValue(writer: anytype, value_bytes: []const u8) !void {
1021 try writer.writeByte(OP.implicit_value);
1022 try leb.writeUleb128(writer, value_bytes.len);
1023 try writer.writeAll(value_bytes);
1024 }
1025 };
1026}
1027
1028// Certain opcodes are not allowed in a CFA context, see 6.4.2
1029fn isOpcodeValidInCFA(opcode: u8) bool {
1030 return switch (opcode) {
1031 OP.addrx,
1032 OP.call2,
1033 OP.call4,
1034 OP.call_ref,
1035 OP.const_type,
1036 OP.constx,
1037 OP.convert,
1038 OP.deref_type,
1039 OP.regval_type,
1040 OP.reinterpret,
1041 OP.push_object_address,
1042 OP.call_frame_cfa,
1043 => false,
1044 else => true,
1045 };
1046}
1047
1048fn isOpcodeRegisterLocation(opcode: u8) bool {
1049 return switch (opcode) {
1050 OP.reg0...OP.reg31, OP.regx => true,
1051 else => false,
1052 };
1053}
1054
1055const testing = std.testing;
1056test "DWARF expressions" {
1057 const allocator = std.testing.allocator;
1058
1059 const options = Options{};
1060 var stack_machine = StackMachine(options){};
1061 defer stack_machine.deinit(allocator);
1062
1063 const b = Builder(options);
1064
1065 var program = std.ArrayList(u8).init(allocator);
1066 defer program.deinit();
1067
1068 const writer = program.writer();
1069
1070 // Literals
1071 {
1072 const context = Context{};
1073 for (0..32) |i| {
1074 try b.writeLiteral(writer, @intCast(i));
1075 }
1076
1077 _ = try stack_machine.run(program.items, allocator, context, 0);
1078
1079 for (0..32) |i| {
1080 const expected = 31 - i;
1081 try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic);
1082 }
1083 }
1084
1085 // Constants
1086 {
1087 stack_machine.reset();
1088 program.clearRetainingCapacity();
1089
1090 const input = [_]comptime_int{
1091 1,
1092 -1,
1093 @as(usize, @truncate(0x0fff)),
1094 @as(isize, @truncate(-0x0fff)),
1095 @as(usize, @truncate(0x0fffffff)),
1096 @as(isize, @truncate(-0x0fffffff)),
1097 @as(usize, @truncate(0x0fffffffffffffff)),
1098 @as(isize, @truncate(-0x0fffffffffffffff)),
1099 @as(usize, @truncate(0x8000000)),
1100 @as(isize, @truncate(-0x8000000)),
1101 @as(usize, @truncate(0x12345678_12345678)),
1102 @as(usize, @truncate(0xffffffff_ffffffff)),
1103 @as(usize, @truncate(0xeeeeeeee_eeeeeeee)),
1104 };
1105
1106 try b.writeConst(writer, u8, input[0]);
1107 try b.writeConst(writer, i8, input[1]);
1108 try b.writeConst(writer, u16, input[2]);
1109 try b.writeConst(writer, i16, input[3]);
1110 try b.writeConst(writer, u32, input[4]);
1111 try b.writeConst(writer, i32, input[5]);
1112 try b.writeConst(writer, u64, input[6]);
1113 try b.writeConst(writer, i64, input[7]);
1114 try b.writeConst(writer, u28, input[8]);
1115 try b.writeConst(writer, i28, input[9]);
1116 try b.writeAddr(writer, input[10]);
1117
1118 var mock_compile_unit: std.debug.Dwarf.CompileUnit = undefined;
1119 mock_compile_unit.addr_base = 1;
1120
1121 var mock_debug_addr = std.ArrayList(u8).init(allocator);
1122 defer mock_debug_addr.deinit();
1123
1124 try mock_debug_addr.writer().writeInt(u16, 0, native_endian);
1125 try mock_debug_addr.writer().writeInt(usize, input[11], native_endian);
1126 try mock_debug_addr.writer().writeInt(usize, input[12], native_endian);
1127
1128 const context = Context{
1129 .compile_unit = &mock_compile_unit,
1130 .debug_addr = mock_debug_addr.items,
1131 };
1132
1133 try b.writeConstx(writer, @as(usize, 1));
1134 try b.writeAddrx(writer, @as(usize, 1 + @sizeOf(usize)));
1135
1136 const die_offset: usize = @truncate(0xaabbccdd);
1137 const type_bytes: []const u8 = &.{ 1, 2, 3, 4 };
1138 try b.writeConstType(writer, die_offset, type_bytes);
1139
1140 _ = try stack_machine.run(program.items, allocator, context, 0);
1141
1142 const const_type = stack_machine.stack.popOrNull().?.const_type;
1143 try testing.expectEqual(die_offset, const_type.type_offset);
1144 try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes);
1145
1146 const expected = .{
1147 .{ usize, input[12], usize },
1148 .{ usize, input[11], usize },
1149 .{ usize, input[10], usize },
1150 .{ isize, input[9], isize },
1151 .{ usize, input[8], usize },
1152 .{ isize, input[7], isize },
1153 .{ usize, input[6], usize },
1154 .{ isize, input[5], isize },
1155 .{ usize, input[4], usize },
1156 .{ isize, input[3], isize },
1157 .{ usize, input[2], usize },
1158 .{ isize, input[1], isize },
1159 .{ usize, input[0], usize },
1160 };
1161
1162 inline for (expected) |e| {
1163 try testing.expectEqual(@as(e[0], e[1]), @as(e[2], @bitCast(stack_machine.stack.popOrNull().?.generic)));
1164 }
1165 }
1166
1167 // Register values
1168 if (@sizeOf(std.debug.ThreadContext) != 0) {
1169 stack_machine.reset();
1170 program.clearRetainingCapacity();
1171
1172 const reg_context = abi.RegisterContext{
1173 .eh_frame = true,
1174 .is_macho = builtin.os.tag == .macos,
1175 };
1176 var thread_context: std.debug.ThreadContext = undefined;
1177 std.debug.relocateContext(&thread_context);
1178 const context = Context{
1179 .thread_context = &thread_context,
1180 .reg_context = reg_context,
1181 };
1182
1183 // Only test register operations on arch / os that have them implemented
1184 if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
1185
1186 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
1187
1188 mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1189 (try abi.regValueNative(usize, &thread_context, abi.fpRegNum(reg_context), reg_context)).* = 1;
1190 (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2;
1191 (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3;
1192
1193 try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
1194 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
1195 try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
1196 try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
1197
1198 _ = try stack_machine.run(program.items, allocator, context, 0);
1199
1200 const regval_type = stack_machine.stack.popOrNull().?.regval_type;
1201 try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
1202 try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
1203 try testing.expectEqual(@as(usize, 0xee), regval_type.value);
1204
1205 try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic);
1206 try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
1207 try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
1208 } else |err| {
1209 switch (err) {
1210 error.UnimplementedArch,
1211 error.UnimplementedOs,
1212 error.ThreadContextNotSupported,
1213 => {},
1214 else => return err,
1215 }
1216 }
1217 }
1218
1219 // Stack operations
1220 {
1221 var context = Context{};
1222
1223 stack_machine.reset();
1224 program.clearRetainingCapacity();
1225 try b.writeConst(writer, u8, 1);
1226 try b.writeOpcode(writer, OP.dup);
1227 _ = try stack_machine.run(program.items, allocator, context, null);
1228 try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
1229 try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
1230
1231 stack_machine.reset();
1232 program.clearRetainingCapacity();
1233 try b.writeConst(writer, u8, 1);
1234 try b.writeOpcode(writer, OP.drop);
1235 _ = try stack_machine.run(program.items, allocator, context, null);
1236 try testing.expect(stack_machine.stack.popOrNull() == null);
1237
1238 stack_machine.reset();
1239 program.clearRetainingCapacity();
1240 try b.writeConst(writer, u8, 4);
1241 try b.writeConst(writer, u8, 5);
1242 try b.writeConst(writer, u8, 6);
1243 try b.writePick(writer, 2);
1244 _ = try stack_machine.run(program.items, allocator, context, null);
1245 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1246
1247 stack_machine.reset();
1248 program.clearRetainingCapacity();
1249 try b.writeConst(writer, u8, 4);
1250 try b.writeConst(writer, u8, 5);
1251 try b.writeConst(writer, u8, 6);
1252 try b.writeOpcode(writer, OP.over);
1253 _ = try stack_machine.run(program.items, allocator, context, null);
1254 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1255
1256 stack_machine.reset();
1257 program.clearRetainingCapacity();
1258 try b.writeConst(writer, u8, 5);
1259 try b.writeConst(writer, u8, 6);
1260 try b.writeOpcode(writer, OP.swap);
1261 _ = try stack_machine.run(program.items, allocator, context, null);
1262 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1263 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1264
1265 stack_machine.reset();
1266 program.clearRetainingCapacity();
1267 try b.writeConst(writer, u8, 4);
1268 try b.writeConst(writer, u8, 5);
1269 try b.writeConst(writer, u8, 6);
1270 try b.writeOpcode(writer, OP.rot);
1271 _ = try stack_machine.run(program.items, allocator, context, null);
1272 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1273 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1274 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1275
1276 const deref_target: usize = @truncate(0xffeeffee_ffeeffee);
1277
1278 stack_machine.reset();
1279 program.clearRetainingCapacity();
1280 try b.writeAddr(writer, @intFromPtr(&deref_target));
1281 try b.writeOpcode(writer, OP.deref);
1282 _ = try stack_machine.run(program.items, allocator, context, null);
1283 try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
1284
1285 stack_machine.reset();
1286 program.clearRetainingCapacity();
1287 try b.writeLiteral(writer, 0);
1288 try b.writeAddr(writer, @intFromPtr(&deref_target));
1289 try b.writeOpcode(writer, OP.xderef);
1290 _ = try stack_machine.run(program.items, allocator, context, null);
1291 try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
1292
1293 stack_machine.reset();
1294 program.clearRetainingCapacity();
1295 try b.writeAddr(writer, @intFromPtr(&deref_target));
1296 try b.writeDerefSize(writer, 1);
1297 _ = try stack_machine.run(program.items, allocator, context, null);
1298 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
1299
1300 stack_machine.reset();
1301 program.clearRetainingCapacity();
1302 try b.writeLiteral(writer, 0);
1303 try b.writeAddr(writer, @intFromPtr(&deref_target));
1304 try b.writeXDerefSize(writer, 1);
1305 _ = try stack_machine.run(program.items, allocator, context, null);
1306 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
1307
1308 const type_offset: usize = @truncate(0xaabbaabb_aabbaabb);
1309
1310 stack_machine.reset();
1311 program.clearRetainingCapacity();
1312 try b.writeAddr(writer, @intFromPtr(&deref_target));
1313 try b.writeDerefType(writer, 1, type_offset);
1314 _ = try stack_machine.run(program.items, allocator, context, null);
1315 const deref_type = stack_machine.stack.popOrNull().?.regval_type;
1316 try testing.expectEqual(type_offset, deref_type.type_offset);
1317 try testing.expectEqual(@as(u8, 1), deref_type.type_size);
1318 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), deref_type.value);
1319
1320 stack_machine.reset();
1321 program.clearRetainingCapacity();
1322 try b.writeLiteral(writer, 0);
1323 try b.writeAddr(writer, @intFromPtr(&deref_target));
1324 try b.writeXDerefType(writer, 1, type_offset);
1325 _ = try stack_machine.run(program.items, allocator, context, null);
1326 const xderef_type = stack_machine.stack.popOrNull().?.regval_type;
1327 try testing.expectEqual(type_offset, xderef_type.type_offset);
1328 try testing.expectEqual(@as(u8, 1), xderef_type.type_size);
1329 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value);
1330
1331 context.object_address = &deref_target;
1332
1333 stack_machine.reset();
1334 program.clearRetainingCapacity();
1335 try b.writeOpcode(writer, OP.push_object_address);
1336 _ = try stack_machine.run(program.items, allocator, context, null);
1337 try testing.expectEqual(@as(usize, @intFromPtr(context.object_address.?)), stack_machine.stack.popOrNull().?.generic);
1338
1339 // TODO: Test OP.form_tls_address
1340
1341 context.cfa = @truncate(0xccddccdd_ccddccdd);
1342
1343 stack_machine.reset();
1344 program.clearRetainingCapacity();
1345 try b.writeOpcode(writer, OP.call_frame_cfa);
1346 _ = try stack_machine.run(program.items, allocator, context, null);
1347 try testing.expectEqual(context.cfa.?, stack_machine.stack.popOrNull().?.generic);
1348 }
1349
1350 // Arithmetic and Logical Operations
1351 {
1352 const context = Context{};
1353
1354 stack_machine.reset();
1355 program.clearRetainingCapacity();
1356 try b.writeConst(writer, i16, -4096);
1357 try b.writeOpcode(writer, OP.abs);
1358 _ = try stack_machine.run(program.items, allocator, context, null);
1359 try testing.expectEqual(@as(usize, 4096), stack_machine.stack.popOrNull().?.generic);
1360
1361 stack_machine.reset();
1362 program.clearRetainingCapacity();
1363 try b.writeConst(writer, u16, 0xff0f);
1364 try b.writeConst(writer, u16, 0xf0ff);
1365 try b.writeOpcode(writer, OP.@"and");
1366 _ = try stack_machine.run(program.items, allocator, context, null);
1367 try testing.expectEqual(@as(usize, 0xf00f), stack_machine.stack.popOrNull().?.generic);
1368
1369 stack_machine.reset();
1370 program.clearRetainingCapacity();
1371 try b.writeConst(writer, i16, -404);
1372 try b.writeConst(writer, i16, 100);
1373 try b.writeOpcode(writer, OP.div);
1374 _ = try stack_machine.run(program.items, allocator, context, null);
1375 try testing.expectEqual(@as(isize, -404 / 100), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
1376
1377 stack_machine.reset();
1378 program.clearRetainingCapacity();
1379 try b.writeConst(writer, u16, 200);
1380 try b.writeConst(writer, u16, 50);
1381 try b.writeOpcode(writer, OP.minus);
1382 _ = try stack_machine.run(program.items, allocator, context, null);
1383 try testing.expectEqual(@as(usize, 150), stack_machine.stack.popOrNull().?.generic);
1384
1385 stack_machine.reset();
1386 program.clearRetainingCapacity();
1387 try b.writeConst(writer, u16, 123);
1388 try b.writeConst(writer, u16, 100);
1389 try b.writeOpcode(writer, OP.mod);
1390 _ = try stack_machine.run(program.items, allocator, context, null);
1391 try testing.expectEqual(@as(usize, 23), stack_machine.stack.popOrNull().?.generic);
1392
1393 stack_machine.reset();
1394 program.clearRetainingCapacity();
1395 try b.writeConst(writer, u16, 0xff);
1396 try b.writeConst(writer, u16, 0xee);
1397 try b.writeOpcode(writer, OP.mul);
1398 _ = try stack_machine.run(program.items, allocator, context, null);
1399 try testing.expectEqual(@as(usize, 0xed12), stack_machine.stack.popOrNull().?.generic);
1400
1401 stack_machine.reset();
1402 program.clearRetainingCapacity();
1403 try b.writeConst(writer, u16, 5);
1404 try b.writeOpcode(writer, OP.neg);
1405 try b.writeConst(writer, i16, -6);
1406 try b.writeOpcode(writer, OP.neg);
1407 _ = try stack_machine.run(program.items, allocator, context, null);
1408 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1409 try testing.expectEqual(@as(isize, -5), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
1410
1411 stack_machine.reset();
1412 program.clearRetainingCapacity();
1413 try b.writeConst(writer, u16, 0xff0f);
1414 try b.writeOpcode(writer, OP.not);
1415 _ = try stack_machine.run(program.items, allocator, context, null);
1416 try testing.expectEqual(~@as(usize, 0xff0f), stack_machine.stack.popOrNull().?.generic);
1417
1418 stack_machine.reset();
1419 program.clearRetainingCapacity();
1420 try b.writeConst(writer, u16, 0xff0f);
1421 try b.writeConst(writer, u16, 0xf0ff);
1422 try b.writeOpcode(writer, OP.@"or");
1423 _ = try stack_machine.run(program.items, allocator, context, null);
1424 try testing.expectEqual(@as(usize, 0xffff), stack_machine.stack.popOrNull().?.generic);
1425
1426 stack_machine.reset();
1427 program.clearRetainingCapacity();
1428 try b.writeConst(writer, i16, 402);
1429 try b.writeConst(writer, i16, 100);
1430 try b.writeOpcode(writer, OP.plus);
1431 _ = try stack_machine.run(program.items, allocator, context, null);
1432 try testing.expectEqual(@as(usize, 502), stack_machine.stack.popOrNull().?.generic);
1433
1434 stack_machine.reset();
1435 program.clearRetainingCapacity();
1436 try b.writeConst(writer, u16, 4096);
1437 try b.writePlusUconst(writer, @as(usize, 8192));
1438 _ = try stack_machine.run(program.items, allocator, context, null);
1439 try testing.expectEqual(@as(usize, 4096 + 8192), stack_machine.stack.popOrNull().?.generic);
1440
1441 stack_machine.reset();
1442 program.clearRetainingCapacity();
1443 try b.writeConst(writer, u16, 0xfff);
1444 try b.writeConst(writer, u16, 1);
1445 try b.writeOpcode(writer, OP.shl);
1446 _ = try stack_machine.run(program.items, allocator, context, null);
1447 try testing.expectEqual(@as(usize, 0xfff << 1), stack_machine.stack.popOrNull().?.generic);
1448
1449 stack_machine.reset();
1450 program.clearRetainingCapacity();
1451 try b.writeConst(writer, u16, 0xfff);
1452 try b.writeConst(writer, u16, 1);
1453 try b.writeOpcode(writer, OP.shr);
1454 _ = try stack_machine.run(program.items, allocator, context, null);
1455 try testing.expectEqual(@as(usize, 0xfff >> 1), stack_machine.stack.popOrNull().?.generic);
1456
1457 stack_machine.reset();
1458 program.clearRetainingCapacity();
1459 try b.writeConst(writer, u16, 0xfff);
1460 try b.writeConst(writer, u16, 1);
1461 try b.writeOpcode(writer, OP.shr);
1462 _ = try stack_machine.run(program.items, allocator, context, null);
1463 try testing.expectEqual(@as(usize, @bitCast(@as(isize, 0xfff) >> 1)), stack_machine.stack.popOrNull().?.generic);
1464
1465 stack_machine.reset();
1466 program.clearRetainingCapacity();
1467 try b.writeConst(writer, u16, 0xf0ff);
1468 try b.writeConst(writer, u16, 0xff0f);
1469 try b.writeOpcode(writer, OP.xor);
1470 _ = try stack_machine.run(program.items, allocator, context, null);
1471 try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
1472 }
1473
1474 // Control Flow Operations
1475 {
1476 const context = Context{};
1477 const expected = .{
1478 .{ OP.le, 1, 1, 0 },
1479 .{ OP.ge, 1, 0, 1 },
1480 .{ OP.eq, 1, 0, 0 },
1481 .{ OP.lt, 0, 1, 0 },
1482 .{ OP.gt, 0, 0, 1 },
1483 .{ OP.ne, 0, 1, 1 },
1484 };
1485
1486 inline for (expected) |e| {
1487 stack_machine.reset();
1488 program.clearRetainingCapacity();
1489
1490 try b.writeConst(writer, u16, 0);
1491 try b.writeConst(writer, u16, 0);
1492 try b.writeOpcode(writer, e[0]);
1493 try b.writeConst(writer, u16, 0);
1494 try b.writeConst(writer, u16, 1);
1495 try b.writeOpcode(writer, e[0]);
1496 try b.writeConst(writer, u16, 1);
1497 try b.writeConst(writer, u16, 0);
1498 try b.writeOpcode(writer, e[0]);
1499 _ = try stack_machine.run(program.items, allocator, context, null);
1500 try testing.expectEqual(@as(usize, e[3]), stack_machine.stack.popOrNull().?.generic);
1501 try testing.expectEqual(@as(usize, e[2]), stack_machine.stack.popOrNull().?.generic);
1502 try testing.expectEqual(@as(usize, e[1]), stack_machine.stack.popOrNull().?.generic);
1503 }
1504
1505 stack_machine.reset();
1506 program.clearRetainingCapacity();
1507 try b.writeLiteral(writer, 2);
1508 try b.writeSkip(writer, 1);
1509 try b.writeLiteral(writer, 3);
1510 _ = try stack_machine.run(program.items, allocator, context, null);
1511 try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic);
1512
1513 stack_machine.reset();
1514 program.clearRetainingCapacity();
1515 try b.writeLiteral(writer, 2);
1516 try b.writeBra(writer, 1);
1517 try b.writeLiteral(writer, 3);
1518 try b.writeLiteral(writer, 0);
1519 try b.writeBra(writer, 1);
1520 try b.writeLiteral(writer, 4);
1521 try b.writeLiteral(writer, 5);
1522 _ = try stack_machine.run(program.items, allocator, context, null);
1523 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1524 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1525 try testing.expect(stack_machine.stack.popOrNull() == null);
1526
1527 // TODO: Test call2, call4, call_ref once implemented
1528
1529 }
1530
1531 // Type conversions
1532 {
1533 const context = Context{};
1534 stack_machine.reset();
1535 program.clearRetainingCapacity();
1536
1537 // TODO: Test typed OP.convert once implemented
1538
1539 const value: usize = @truncate(0xffeeffee_ffeeffee);
1540 var value_bytes: [options.addr_size]u8 = undefined;
1541 mem.writeInt(usize, &value_bytes, value, native_endian);
1542
1543 // Convert to generic type
1544 stack_machine.reset();
1545 program.clearRetainingCapacity();
1546 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1547 try b.writeConvert(writer, @as(usize, 0));
1548 _ = try stack_machine.run(program.items, allocator, context, null);
1549 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
1550
1551 // Reinterpret to generic type
1552 stack_machine.reset();
1553 program.clearRetainingCapacity();
1554 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1555 try b.writeReinterpret(writer, @as(usize, 0));
1556 _ = try stack_machine.run(program.items, allocator, context, null);
1557 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
1558
1559 // Reinterpret to new type
1560 const die_offset: usize = 0xffee;
1561
1562 stack_machine.reset();
1563 program.clearRetainingCapacity();
1564 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1565 try b.writeReinterpret(writer, die_offset);
1566 _ = try stack_machine.run(program.items, allocator, context, null);
1567 const const_type = stack_machine.stack.popOrNull().?.const_type;
1568 try testing.expectEqual(die_offset, const_type.type_offset);
1569
1570 stack_machine.reset();
1571 program.clearRetainingCapacity();
1572 try b.writeLiteral(writer, 0);
1573 try b.writeReinterpret(writer, die_offset);
1574 _ = try stack_machine.run(program.items, allocator, context, null);
1575 const regval_type = stack_machine.stack.popOrNull().?.regval_type;
1576 try testing.expectEqual(die_offset, regval_type.type_offset);
1577 }
1578
1579 // Special operations
1580 {
1581 var context = Context{};
1582
1583 stack_machine.reset();
1584 program.clearRetainingCapacity();
1585 try b.writeOpcode(writer, OP.nop);
1586 _ = try stack_machine.run(program.items, allocator, context, null);
1587 try testing.expect(stack_machine.stack.popOrNull() == null);
1588
1589 // Sub-expression
1590 {
1591 var sub_program = std.ArrayList(u8).init(allocator);
1592 defer sub_program.deinit();
1593 const sub_writer = sub_program.writer();
1594 try b.writeLiteral(sub_writer, 3);
1595
1596 stack_machine.reset();
1597 program.clearRetainingCapacity();
1598 try b.writeEntryValue(writer, sub_program.items);
1599 _ = try stack_machine.run(program.items, allocator, context, null);
1600 try testing.expectEqual(@as(usize, 3), stack_machine.stack.popOrNull().?.generic);
1601 }
1602
1603 // Register location description
1604 const reg_context = abi.RegisterContext{
1605 .eh_frame = true,
1606 .is_macho = builtin.os.tag == .macos,
1607 };
1608 var thread_context: std.debug.ThreadContext = undefined;
1609 std.debug.relocateContext(&thread_context);
1610 context = Context{
1611 .thread_context = &thread_context,
1612 .reg_context = reg_context,
1613 };
1614
1615 if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
1616 mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1617
1618 var sub_program = std.ArrayList(u8).init(allocator);
1619 defer sub_program.deinit();
1620 const sub_writer = sub_program.writer();
1621 try b.writeReg(sub_writer, 0);
1622
1623 stack_machine.reset();
1624 program.clearRetainingCapacity();
1625 try b.writeEntryValue(writer, sub_program.items);
1626 _ = try stack_machine.run(program.items, allocator, context, null);
1627 try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.popOrNull().?.generic);
1628 } else |err| {
1629 switch (err) {
1630 error.UnimplementedArch,
1631 error.UnimplementedOs,
1632 error.ThreadContextNotSupported,
1633 => {},
1634 else => return err,
1635 }
1636 }
1637 }
1638}
lib/std/dwarf.zig+5-2697
......@@ -1,12 +1,8 @@
11//! DWARF debugging data format.
2
3const builtin = @import("builtin");
4const std = @import("std.zig");
5const debug = std.debug;
6const mem = std.mem;
7const math = std.math;
8const assert = debug.assert;
9const native_endian = builtin.cpu.arch.endian();
2//!
3//! This namespace contains unopinionated types and data definitions only. For
4//! an implementation of parsing and caching DWARF information, see
5//! `std.debug.Dwarf`.
106
117pub const TAG = @import("dwarf/TAG.zig");
128pub const AT = @import("dwarf/AT.zig");
......@@ -15,9 +11,7 @@ pub const LANG = @import("dwarf/LANG.zig");
1511pub const FORM = @import("dwarf/FORM.zig");
1612pub const ATE = @import("dwarf/ATE.zig");
1713pub const EH = @import("dwarf/EH.zig");
18pub const abi = @import("dwarf/abi.zig");
19pub const call_frame = @import("dwarf/call_frame.zig");
20pub const expressions = @import("dwarf/expressions.zig");
14pub const Format = enum { @"32", @"64" };
2115
2216pub const LLE = struct {
2317 pub const end_of_list = 0x00;
......@@ -151,2689 +145,3 @@ pub const CC = enum(u8) {
151145 pub const lo_user = 0x40;
152146 pub const hi_user = 0xff;
153147};
154
155pub const Format = enum { @"32", @"64" };
156
157const PcRange = struct {
158 start: u64,
159 end: u64,
160};
161
162const Func = struct {
163 pc_range: ?PcRange,
164 name: ?[]const u8,
165};
166
167pub const CompileUnit = struct {
168 version: u16,
169 format: Format,
170 die: Die,
171 pc_range: ?PcRange,
172
173 str_offsets_base: usize,
174 addr_base: usize,
175 rnglists_base: usize,
176 loclists_base: usize,
177 frame_base: ?*const FormValue,
178};
179
180const Abbrev = struct {
181 code: u64,
182 tag_id: u64,
183 has_children: bool,
184 attrs: []Attr,
185
186 fn deinit(abbrev: *Abbrev, allocator: mem.Allocator) void {
187 allocator.free(abbrev.attrs);
188 abbrev.* = undefined;
189 }
190
191 const Attr = struct {
192 id: u64,
193 form_id: u64,
194 /// Only valid if form_id is .implicit_const
195 payload: i64,
196 };
197
198 const Table = struct {
199 // offset from .debug_abbrev
200 offset: u64,
201 abbrevs: []Abbrev,
202
203 fn deinit(table: *Table, allocator: mem.Allocator) void {
204 for (table.abbrevs) |*abbrev| {
205 abbrev.deinit(allocator);
206 }
207 allocator.free(table.abbrevs);
208 table.* = undefined;
209 }
210
211 fn get(table: *const Table, abbrev_code: u64) ?*const Abbrev {
212 return for (table.abbrevs) |*abbrev| {
213 if (abbrev.code == abbrev_code) break abbrev;
214 } else null;
215 }
216 };
217};
218
219pub const FormValue = union(enum) {
220 addr: u64,
221 addrx: usize,
222 block: []const u8,
223 udata: u64,
224 data16: *const [16]u8,
225 sdata: i64,
226 exprloc: []const u8,
227 flag: bool,
228 sec_offset: u64,
229 ref: u64,
230 ref_addr: u64,
231 string: [:0]const u8,
232 strp: u64,
233 strx: usize,
234 line_strp: u64,
235 loclistx: u64,
236 rnglistx: u64,
237
238 fn getString(fv: FormValue, di: DwarfInfo) ![:0]const u8 {
239 switch (fv) {
240 .string => |s| return s,
241 .strp => |off| return di.getString(off),
242 .line_strp => |off| return di.getLineString(off),
243 else => return badDwarf(),
244 }
245 }
246
247 fn getUInt(fv: FormValue, comptime U: type) !U {
248 return switch (fv) {
249 inline .udata,
250 .sdata,
251 .sec_offset,
252 => |c| math.cast(U, c) orelse badDwarf(),
253 else => badDwarf(),
254 };
255 }
256};
257
258const Die = struct {
259 tag_id: u64,
260 has_children: bool,
261 attrs: []Attr,
262
263 const Attr = struct {
264 id: u64,
265 value: FormValue,
266 };
267
268 fn deinit(self: *Die, allocator: mem.Allocator) void {
269 allocator.free(self.attrs);
270 self.* = undefined;
271 }
272
273 fn getAttr(self: *const Die, id: u64) ?*const FormValue {
274 for (self.attrs) |*attr| {
275 if (attr.id == id) return &attr.value;
276 }
277 return null;
278 }
279
280 fn getAttrAddr(
281 self: *const Die,
282 di: *const DwarfInfo,
283 id: u64,
284 compile_unit: CompileUnit,
285 ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
286 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
287 return switch (form_value.*) {
288 .addr => |value| value,
289 .addrx => |index| di.readDebugAddr(compile_unit, index),
290 else => error.InvalidDebugInfo,
291 };
292 }
293
294 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
295 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
296 return form_value.getUInt(u64);
297 }
298
299 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
300 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
301 return switch (form_value.*) {
302 .Const => |value| value.asUnsignedLe(),
303 else => error.InvalidDebugInfo,
304 };
305 }
306
307 fn getAttrRef(self: *const Die, id: u64) !u64 {
308 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
309 return switch (form_value.*) {
310 .ref => |value| value,
311 else => error.InvalidDebugInfo,
312 };
313 }
314
315 pub fn getAttrString(
316 self: *const Die,
317 di: *DwarfInfo,
318 id: u64,
319 opt_str: ?[]const u8,
320 compile_unit: CompileUnit,
321 ) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {
322 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
323 switch (form_value.*) {
324 .string => |value| return value,
325 .strp => |offset| return di.getString(offset),
326 .strx => |index| {
327 const debug_str_offsets = di.section(.debug_str_offsets) orelse return badDwarf();
328 if (compile_unit.str_offsets_base == 0) return badDwarf();
329 switch (compile_unit.format) {
330 .@"32" => {
331 const byte_offset = compile_unit.str_offsets_base + 4 * index;
332 if (byte_offset + 4 > debug_str_offsets.len) return badDwarf();
333 const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);
334 return getStringGeneric(opt_str, offset);
335 },
336 .@"64" => {
337 const byte_offset = compile_unit.str_offsets_base + 8 * index;
338 if (byte_offset + 8 > debug_str_offsets.len) return badDwarf();
339 const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);
340 return getStringGeneric(opt_str, offset);
341 },
342 }
343 },
344 .line_strp => |offset| return di.getLineString(offset),
345 else => return badDwarf(),
346 }
347 }
348};
349
350const FileEntry = struct {
351 path: []const u8,
352 dir_index: u32 = 0,
353 mtime: u64 = 0,
354 size: u64 = 0,
355 md5: [16]u8 = [1]u8{0} ** 16,
356};
357
358const LineNumberProgram = struct {
359 address: u64,
360 file: usize,
361 line: i64,
362 column: u64,
363 version: u16,
364 is_stmt: bool,
365 basic_block: bool,
366 end_sequence: bool,
367
368 default_is_stmt: bool,
369 target_address: u64,
370 include_dirs: []const FileEntry,
371
372 prev_valid: bool,
373 prev_address: u64,
374 prev_file: usize,
375 prev_line: i64,
376 prev_column: u64,
377 prev_is_stmt: bool,
378 prev_basic_block: bool,
379 prev_end_sequence: bool,
380
381 // Reset the state machine following the DWARF specification
382 pub fn reset(self: *LineNumberProgram) void {
383 self.address = 0;
384 self.file = 1;
385 self.line = 1;
386 self.column = 0;
387 self.is_stmt = self.default_is_stmt;
388 self.basic_block = false;
389 self.end_sequence = false;
390 // Invalidate all the remaining fields
391 self.prev_valid = false;
392 self.prev_address = 0;
393 self.prev_file = undefined;
394 self.prev_line = undefined;
395 self.prev_column = undefined;
396 self.prev_is_stmt = undefined;
397 self.prev_basic_block = undefined;
398 self.prev_end_sequence = undefined;
399 }
400
401 pub fn init(
402 is_stmt: bool,
403 include_dirs: []const FileEntry,
404 target_address: u64,
405 version: u16,
406 ) LineNumberProgram {
407 return LineNumberProgram{
408 .address = 0,
409 .file = 1,
410 .line = 1,
411 .column = 0,
412 .version = version,
413 .is_stmt = is_stmt,
414 .basic_block = false,
415 .end_sequence = false,
416 .include_dirs = include_dirs,
417 .default_is_stmt = is_stmt,
418 .target_address = target_address,
419 .prev_valid = false,
420 .prev_address = 0,
421 .prev_file = undefined,
422 .prev_line = undefined,
423 .prev_column = undefined,
424 .prev_is_stmt = undefined,
425 .prev_basic_block = undefined,
426 .prev_end_sequence = undefined,
427 };
428 }
429
430 pub fn checkLineMatch(
431 self: *LineNumberProgram,
432 allocator: mem.Allocator,
433 file_entries: []const FileEntry,
434 ) !?debug.LineInfo {
435 if (self.prev_valid and
436 self.target_address >= self.prev_address and
437 self.target_address < self.address)
438 {
439 const file_index = if (self.version >= 5) self.prev_file else i: {
440 if (self.prev_file == 0) return missingDwarf();
441 break :i self.prev_file - 1;
442 };
443
444 if (file_index >= file_entries.len) return badDwarf();
445 const file_entry = &file_entries[file_index];
446
447 if (file_entry.dir_index >= self.include_dirs.len) return badDwarf();
448 const dir_name = self.include_dirs[file_entry.dir_index].path;
449
450 const file_name = try std.fs.path.join(allocator, &[_][]const u8{
451 dir_name, file_entry.path,
452 });
453
454 return debug.LineInfo{
455 .line = if (self.prev_line >= 0) @as(u64, @intCast(self.prev_line)) else 0,
456 .column = self.prev_column,
457 .file_name = file_name,
458 };
459 }
460
461 self.prev_valid = true;
462 self.prev_address = self.address;
463 self.prev_file = self.file;
464 self.prev_line = self.line;
465 self.prev_column = self.column;
466 self.prev_is_stmt = self.is_stmt;
467 self.prev_basic_block = self.basic_block;
468 self.prev_end_sequence = self.end_sequence;
469 return null;
470 }
471};
472
473const UnitHeader = struct {
474 format: Format,
475 header_length: u4,
476 unit_length: u64,
477};
478fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*debug.StackIterator.MemoryAccessor) !UnitHeader {
479 return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) {
480 0...0xfffffff0 - 1 => |unit_length| .{
481 .format = .@"32",
482 .header_length = 4,
483 .unit_length = unit_length,
484 },
485 0xfffffff0...0xffffffff - 1 => badDwarf(),
486 0xffffffff => .{
487 .format = .@"64",
488 .header_length = 12,
489 .unit_length = try if (opt_ma) |ma| fbr.readIntChecked(u64, ma) else fbr.readInt(u64),
490 },
491 };
492}
493
494fn parseFormValue(
495 fbr: *FixedBufferReader,
496 form_id: u64,
497 format: Format,
498 implicit_const: ?i64,
499) anyerror!FormValue {
500 return switch (form_id) {
501 FORM.addr => .{ .addr = try fbr.readAddress(switch (@bitSizeOf(usize)) {
502 32 => .@"32",
503 64 => .@"64",
504 else => @compileError("unsupported @sizeOf(usize)"),
505 }) },
506 FORM.addrx1 => .{ .addrx = try fbr.readInt(u8) },
507 FORM.addrx2 => .{ .addrx = try fbr.readInt(u16) },
508 FORM.addrx3 => .{ .addrx = try fbr.readInt(u24) },
509 FORM.addrx4 => .{ .addrx = try fbr.readInt(u32) },
510 FORM.addrx => .{ .addrx = try fbr.readUleb128(usize) },
511
512 FORM.block1,
513 FORM.block2,
514 FORM.block4,
515 FORM.block,
516 => .{ .block = try fbr.readBytes(switch (form_id) {
517 FORM.block1 => try fbr.readInt(u8),
518 FORM.block2 => try fbr.readInt(u16),
519 FORM.block4 => try fbr.readInt(u32),
520 FORM.block => try fbr.readUleb128(usize),
521 else => unreachable,
522 }) },
523
524 FORM.data1 => .{ .udata = try fbr.readInt(u8) },
525 FORM.data2 => .{ .udata = try fbr.readInt(u16) },
526 FORM.data4 => .{ .udata = try fbr.readInt(u32) },
527 FORM.data8 => .{ .udata = try fbr.readInt(u64) },
528 FORM.data16 => .{ .data16 = (try fbr.readBytes(16))[0..16] },
529 FORM.udata => .{ .udata = try fbr.readUleb128(u64) },
530 FORM.sdata => .{ .sdata = try fbr.readIleb128(i64) },
531 FORM.exprloc => .{ .exprloc = try fbr.readBytes(try fbr.readUleb128(usize)) },
532 FORM.flag => .{ .flag = (try fbr.readByte()) != 0 },
533 FORM.flag_present => .{ .flag = true },
534 FORM.sec_offset => .{ .sec_offset = try fbr.readAddress(format) },
535
536 FORM.ref1 => .{ .ref = try fbr.readInt(u8) },
537 FORM.ref2 => .{ .ref = try fbr.readInt(u16) },
538 FORM.ref4 => .{ .ref = try fbr.readInt(u32) },
539 FORM.ref8 => .{ .ref = try fbr.readInt(u64) },
540 FORM.ref_udata => .{ .ref = try fbr.readUleb128(u64) },
541
542 FORM.ref_addr => .{ .ref_addr = try fbr.readAddress(format) },
543 FORM.ref_sig8 => .{ .ref = try fbr.readInt(u64) },
544
545 FORM.string => .{ .string = try fbr.readBytesTo(0) },
546 FORM.strp => .{ .strp = try fbr.readAddress(format) },
547 FORM.strx1 => .{ .strx = try fbr.readInt(u8) },
548 FORM.strx2 => .{ .strx = try fbr.readInt(u16) },
549 FORM.strx3 => .{ .strx = try fbr.readInt(u24) },
550 FORM.strx4 => .{ .strx = try fbr.readInt(u32) },
551 FORM.strx => .{ .strx = try fbr.readUleb128(usize) },
552 FORM.line_strp => .{ .line_strp = try fbr.readAddress(format) },
553 FORM.indirect => parseFormValue(fbr, try fbr.readUleb128(u64), format, implicit_const),
554 FORM.implicit_const => .{ .sdata = implicit_const orelse return badDwarf() },
555 FORM.loclistx => .{ .loclistx = try fbr.readUleb128(u64) },
556 FORM.rnglistx => .{ .rnglistx = try fbr.readUleb128(u64) },
557 else => {
558 //debug.print("unrecognized form id: {x}\n", .{form_id});
559 return badDwarf();
560 },
561 };
562}
563
564pub const DwarfSection = enum {
565 debug_info,
566 debug_abbrev,
567 debug_str,
568 debug_str_offsets,
569 debug_line,
570 debug_line_str,
571 debug_ranges,
572 debug_loclists,
573 debug_rnglists,
574 debug_addr,
575 debug_names,
576 debug_frame,
577 eh_frame,
578 eh_frame_hdr,
579};
580
581pub const DwarfInfo = struct {
582 pub const Section = struct {
583 data: []const u8,
584 // Module-relative virtual address.
585 // Only set if the section data was loaded from disk.
586 virtual_address: ?usize = null,
587 // If `data` is owned by this DwarfInfo.
588 owned: bool,
589
590 // For sections that are not memory mapped by the loader, this is an offset
591 // from `data.ptr` to where the section would have been mapped. Otherwise,
592 // `data` is directly backed by the section and the offset is zero.
593 pub fn virtualOffset(self: Section, base_address: usize) i64 {
594 return if (self.virtual_address) |va|
595 @as(i64, @intCast(base_address + va)) -
596 @as(i64, @intCast(@intFromPtr(self.data.ptr)))
597 else
598 0;
599 }
600 };
601
602 const num_sections = std.enums.directEnumArrayLen(DwarfSection, 0);
603 pub const SectionArray = [num_sections]?Section;
604 pub const null_section_array = [_]?Section{null} ** num_sections;
605
606 endian: std.builtin.Endian,
607 sections: SectionArray = null_section_array,
608 is_macho: bool,
609
610 // Filled later by the initializer
611 abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{},
612 compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
613 func_list: std.ArrayListUnmanaged(Func) = .{},
614
615 eh_frame_hdr: ?ExceptionFrameHeader = null,
616 // These lookup tables are only used if `eh_frame_hdr` is null
617 cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{},
618 // Sorted by start_pc
619 fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},
620
621 pub fn section(di: DwarfInfo, dwarf_section: DwarfSection) ?[]const u8 {
622 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.data else null;
623 }
624
625 pub fn sectionVirtualOffset(di: DwarfInfo, dwarf_section: DwarfSection, base_address: usize) ?i64 {
626 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.virtualOffset(base_address) else null;
627 }
628
629 pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void {
630 for (di.sections) |opt_section| {
631 if (opt_section) |s| if (s.owned) allocator.free(s.data);
632 }
633 for (di.abbrev_table_list.items) |*abbrev| {
634 abbrev.deinit(allocator);
635 }
636 di.abbrev_table_list.deinit(allocator);
637 for (di.compile_unit_list.items) |*cu| {
638 cu.die.deinit(allocator);
639 }
640 di.compile_unit_list.deinit(allocator);
641 di.func_list.deinit(allocator);
642 di.cie_map.deinit(allocator);
643 di.fde_list.deinit(allocator);
644 di.* = undefined;
645 }
646
647 pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
648 for (di.func_list.items) |*func| {
649 if (func.pc_range) |range| {
650 if (address >= range.start and address < range.end) {
651 return func.name;
652 }
653 }
654 }
655
656 return null;
657 }
658
659 fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void {
660 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
661 var this_unit_offset: u64 = 0;
662
663 while (this_unit_offset < fbr.buf.len) {
664 try fbr.seekTo(this_unit_offset);
665
666 const unit_header = try readUnitHeader(&fbr, null);
667 if (unit_header.unit_length == 0) return;
668 const next_offset = unit_header.header_length + unit_header.unit_length;
669
670 const version = try fbr.readInt(u16);
671 if (version < 2 or version > 5) return badDwarf();
672
673 var address_size: u8 = undefined;
674 var debug_abbrev_offset: u64 = undefined;
675 if (version >= 5) {
676 const unit_type = try fbr.readInt(u8);
677 if (unit_type != UT.compile) return badDwarf();
678 address_size = try fbr.readByte();
679 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
680 } else {
681 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
682 address_size = try fbr.readByte();
683 }
684 if (address_size != @sizeOf(usize)) return badDwarf();
685
686 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
687
688 var max_attrs: usize = 0;
689 var zig_padding_abbrev_code: u7 = 0;
690 for (abbrev_table.abbrevs) |abbrev| {
691 max_attrs = @max(max_attrs, abbrev.attrs.len);
692 if (math.cast(u7, abbrev.code)) |code| {
693 if (abbrev.tag_id == TAG.ZIG_padding and
694 !abbrev.has_children and
695 abbrev.attrs.len == 0)
696 {
697 zig_padding_abbrev_code = code;
698 }
699 }
700 }
701 const attrs_buf = try allocator.alloc(Die.Attr, max_attrs * 3);
702 defer allocator.free(attrs_buf);
703 var attrs_bufs: [3][]Die.Attr = undefined;
704 for (&attrs_bufs, 0..) |*buf, index| buf.* = attrs_buf[index * max_attrs ..][0..max_attrs];
705
706 const next_unit_pos = this_unit_offset + next_offset;
707
708 var compile_unit: CompileUnit = .{
709 .version = version,
710 .format = unit_header.format,
711 .die = undefined,
712 .pc_range = null,
713
714 .str_offsets_base = 0,
715 .addr_base = 0,
716 .rnglists_base = 0,
717 .loclists_base = 0,
718 .frame_base = null,
719 };
720
721 while (true) {
722 fbr.pos = mem.indexOfNonePos(u8, fbr.buf, fbr.pos, &.{
723 zig_padding_abbrev_code, 0,
724 }) orelse fbr.buf.len;
725 if (fbr.pos >= next_unit_pos) break;
726 var die_obj = (try parseDie(
727 &fbr,
728 attrs_bufs[0],
729 abbrev_table,
730 unit_header.format,
731 )) orelse continue;
732
733 switch (die_obj.tag_id) {
734 TAG.compile_unit => {
735 compile_unit.die = die_obj;
736 compile_unit.die.attrs = attrs_bufs[1][0..die_obj.attrs.len];
737 @memcpy(compile_unit.die.attrs, die_obj.attrs);
738
739 compile_unit.str_offsets_base = if (die_obj.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0;
740 compile_unit.addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0;
741 compile_unit.rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0;
742 compile_unit.loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0;
743 compile_unit.frame_base = die_obj.getAttr(AT.frame_base);
744 },
745 TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => {
746 const fn_name = x: {
747 var this_die_obj = die_obj;
748 // Prevent endless loops
749 for (0..3) |_| {
750 if (this_die_obj.getAttr(AT.name)) |_| {
751 break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);
752 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
753 const after_die_offset = fbr.pos;
754 defer fbr.pos = after_die_offset;
755
756 // Follow the DIE it points to and repeat
757 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
758 if (ref_offset > next_offset) return badDwarf();
759 try fbr.seekTo(this_unit_offset + ref_offset);
760 this_die_obj = (try parseDie(
761 &fbr,
762 attrs_bufs[2],
763 abbrev_table,
764 unit_header.format,
765 )) orelse return badDwarf();
766 } else if (this_die_obj.getAttr(AT.specification)) |_| {
767 const after_die_offset = fbr.pos;
768 defer fbr.pos = after_die_offset;
769
770 // Follow the DIE it points to and repeat
771 const ref_offset = try this_die_obj.getAttrRef(AT.specification);
772 if (ref_offset > next_offset) return badDwarf();
773 try fbr.seekTo(this_unit_offset + ref_offset);
774 this_die_obj = (try parseDie(
775 &fbr,
776 attrs_bufs[2],
777 abbrev_table,
778 unit_header.format,
779 )) orelse return badDwarf();
780 } else {
781 break :x null;
782 }
783 }
784
785 break :x null;
786 };
787
788 var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
789 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
790 const pc_end = switch (high_pc_value.*) {
791 .addr => |value| value,
792 .udata => |offset| low_pc + offset,
793 else => return badDwarf(),
794 };
795
796 try di.func_list.append(allocator, .{
797 .name = fn_name,
798 .pc_range = .{
799 .start = low_pc,
800 .end = pc_end,
801 },
802 });
803
804 break :blk true;
805 }
806
807 break :blk false;
808 } else |err| blk: {
809 if (err != error.MissingDebugInfo) return err;
810 break :blk false;
811 };
812
813 if (die_obj.getAttr(AT.ranges)) |ranges_value| blk: {
814 var iter = DebugRangeIterator.init(ranges_value, di, &compile_unit) catch |err| {
815 if (err != error.MissingDebugInfo) return err;
816 break :blk;
817 };
818
819 while (try iter.next()) |range| {
820 range_added = true;
821 try di.func_list.append(allocator, .{
822 .name = fn_name,
823 .pc_range = .{
824 .start = range.start_addr,
825 .end = range.end_addr,
826 },
827 });
828 }
829 }
830
831 if (fn_name != null and !range_added) {
832 try di.func_list.append(allocator, .{
833 .name = fn_name,
834 .pc_range = null,
835 });
836 }
837 },
838 else => {},
839 }
840 }
841
842 this_unit_offset += next_offset;
843 }
844 }
845
846 fn scanAllCompileUnits(di: *DwarfInfo, allocator: mem.Allocator) !void {
847 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
848 var this_unit_offset: u64 = 0;
849
850 var attrs_buf = std.ArrayList(Die.Attr).init(allocator);
851 defer attrs_buf.deinit();
852
853 while (this_unit_offset < fbr.buf.len) {
854 try fbr.seekTo(this_unit_offset);
855
856 const unit_header = try readUnitHeader(&fbr, null);
857 if (unit_header.unit_length == 0) return;
858 const next_offset = unit_header.header_length + unit_header.unit_length;
859
860 const version = try fbr.readInt(u16);
861 if (version < 2 or version > 5) return badDwarf();
862
863 var address_size: u8 = undefined;
864 var debug_abbrev_offset: u64 = undefined;
865 if (version >= 5) {
866 const unit_type = try fbr.readInt(u8);
867 if (unit_type != UT.compile) return badDwarf();
868 address_size = try fbr.readByte();
869 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
870 } else {
871 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
872 address_size = try fbr.readByte();
873 }
874 if (address_size != @sizeOf(usize)) return badDwarf();
875
876 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
877
878 var max_attrs: usize = 0;
879 for (abbrev_table.abbrevs) |abbrev| {
880 max_attrs = @max(max_attrs, abbrev.attrs.len);
881 }
882 try attrs_buf.resize(max_attrs);
883
884 var compile_unit_die = (try parseDie(
885 &fbr,
886 attrs_buf.items,
887 abbrev_table,
888 unit_header.format,
889 )) orelse return badDwarf();
890
891 if (compile_unit_die.tag_id != TAG.compile_unit) return badDwarf();
892
893 compile_unit_die.attrs = try allocator.dupe(Die.Attr, compile_unit_die.attrs);
894
895 var compile_unit: CompileUnit = .{
896 .version = version,
897 .format = unit_header.format,
898 .pc_range = null,
899 .die = compile_unit_die,
900 .str_offsets_base = if (compile_unit_die.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0,
901 .addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0,
902 .rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0,
903 .loclists_base = if (compile_unit_die.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0,
904 .frame_base = compile_unit_die.getAttr(AT.frame_base),
905 };
906
907 compile_unit.pc_range = x: {
908 if (compile_unit_die.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| {
909 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
910 const pc_end = switch (high_pc_value.*) {
911 .addr => |value| value,
912 .udata => |offset| low_pc + offset,
913 else => return badDwarf(),
914 };
915 break :x PcRange{
916 .start = low_pc,
917 .end = pc_end,
918 };
919 } else {
920 break :x null;
921 }
922 } else |err| {
923 if (err != error.MissingDebugInfo) return err;
924 break :x null;
925 }
926 };
927
928 try di.compile_unit_list.append(allocator, compile_unit);
929
930 this_unit_offset += next_offset;
931 }
932 }
933
934 const DebugRangeIterator = struct {
935 base_address: u64,
936 section_type: DwarfSection,
937 di: *const DwarfInfo,
938 compile_unit: *const CompileUnit,
939 fbr: FixedBufferReader,
940
941 pub fn init(ranges_value: *const FormValue, di: *const DwarfInfo, compile_unit: *const CompileUnit) !@This() {
942 const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges;
943 const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
944
945 const ranges_offset = switch (ranges_value.*) {
946 .sec_offset, .udata => |off| off,
947 .rnglistx => |idx| off: {
948 switch (compile_unit.format) {
949 .@"32" => {
950 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));
951 if (offset_loc + 4 > debug_ranges.len) return badDwarf();
952 const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);
953 break :off compile_unit.rnglists_base + offset;
954 },
955 .@"64" => {
956 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));
957 if (offset_loc + 8 > debug_ranges.len) return badDwarf();
958 const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);
959 break :off compile_unit.rnglists_base + offset;
960 },
961 }
962 },
963 else => return badDwarf(),
964 };
965
966 // All the addresses in the list are relative to the value
967 // specified by DW_AT.low_pc or to some other value encoded
968 // in the list itself.
969 // If no starting value is specified use zero.
970 const base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) {
971 error.MissingDebugInfo => 0,
972 else => return err,
973 };
974
975 return .{
976 .base_address = base_address,
977 .section_type = section_type,
978 .di = di,
979 .compile_unit = compile_unit,
980 .fbr = .{
981 .buf = debug_ranges,
982 .pos = math.cast(usize, ranges_offset) orelse return badDwarf(),
983 .endian = di.endian,
984 },
985 };
986 }
987
988 // Returns the next range in the list, or null if the end was reached.
989 pub fn next(self: *@This()) !?struct { start_addr: u64, end_addr: u64 } {
990 switch (self.section_type) {
991 .debug_rnglists => {
992 const kind = try self.fbr.readByte();
993 switch (kind) {
994 RLE.end_of_list => return null,
995 RLE.base_addressx => {
996 const index = try self.fbr.readUleb128(usize);
997 self.base_address = try self.di.readDebugAddr(self.compile_unit.*, index);
998 return try self.next();
999 },
1000 RLE.startx_endx => {
1001 const start_index = try self.fbr.readUleb128(usize);
1002 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
1003
1004 const end_index = try self.fbr.readUleb128(usize);
1005 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);
1006
1007 return .{
1008 .start_addr = start_addr,
1009 .end_addr = end_addr,
1010 };
1011 },
1012 RLE.startx_length => {
1013 const start_index = try self.fbr.readUleb128(usize);
1014 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
1015
1016 const len = try self.fbr.readUleb128(usize);
1017 const end_addr = start_addr + len;
1018
1019 return .{
1020 .start_addr = start_addr,
1021 .end_addr = end_addr,
1022 };
1023 },
1024 RLE.offset_pair => {
1025 const start_addr = try self.fbr.readUleb128(usize);
1026 const end_addr = try self.fbr.readUleb128(usize);
1027
1028 // This is the only kind that uses the base address
1029 return .{
1030 .start_addr = self.base_address + start_addr,
1031 .end_addr = self.base_address + end_addr,
1032 };
1033 },
1034 RLE.base_address => {
1035 self.base_address = try self.fbr.readInt(usize);
1036 return try self.next();
1037 },
1038 RLE.start_end => {
1039 const start_addr = try self.fbr.readInt(usize);
1040 const end_addr = try self.fbr.readInt(usize);
1041
1042 return .{
1043 .start_addr = start_addr,
1044 .end_addr = end_addr,
1045 };
1046 },
1047 RLE.start_length => {
1048 const start_addr = try self.fbr.readInt(usize);
1049 const len = try self.fbr.readUleb128(usize);
1050 const end_addr = start_addr + len;
1051
1052 return .{
1053 .start_addr = start_addr,
1054 .end_addr = end_addr,
1055 };
1056 },
1057 else => return badDwarf(),
1058 }
1059 },
1060 .debug_ranges => {
1061 const start_addr = try self.fbr.readInt(usize);
1062 const end_addr = try self.fbr.readInt(usize);
1063 if (start_addr == 0 and end_addr == 0) return null;
1064
1065 // This entry selects a new value for the base address
1066 if (start_addr == math.maxInt(usize)) {
1067 self.base_address = end_addr;
1068 return try self.next();
1069 }
1070
1071 return .{
1072 .start_addr = self.base_address + start_addr,
1073 .end_addr = self.base_address + end_addr,
1074 };
1075 },
1076 else => unreachable,
1077 }
1078 }
1079 };
1080
1081 pub fn findCompileUnit(di: *const DwarfInfo, target_address: u64) !*const CompileUnit {
1082 for (di.compile_unit_list.items) |*compile_unit| {
1083 if (compile_unit.pc_range) |range| {
1084 if (target_address >= range.start and target_address < range.end) return compile_unit;
1085 }
1086
1087 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;
1088 var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue;
1089 while (try iter.next()) |range| {
1090 if (target_address >= range.start_addr and target_address < range.end_addr) return compile_unit;
1091 }
1092 }
1093
1094 return missingDwarf();
1095 }
1096
1097 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
1098 /// seeks in the stream and parses it.
1099 fn getAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, abbrev_offset: u64) !*const Abbrev.Table {
1100 for (di.abbrev_table_list.items) |*table| {
1101 if (table.offset == abbrev_offset) {
1102 return table;
1103 }
1104 }
1105 try di.abbrev_table_list.append(
1106 allocator,
1107 try di.parseAbbrevTable(allocator, abbrev_offset),
1108 );
1109 return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1];
1110 }
1111
1112 fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !Abbrev.Table {
1113 var fbr: FixedBufferReader = .{
1114 .buf = di.section(.debug_abbrev).?,
1115 .pos = math.cast(usize, offset) orelse return badDwarf(),
1116 .endian = di.endian,
1117 };
1118
1119 var abbrevs = std.ArrayList(Abbrev).init(allocator);
1120 defer {
1121 for (abbrevs.items) |*abbrev| {
1122 abbrev.deinit(allocator);
1123 }
1124 abbrevs.deinit();
1125 }
1126
1127 var attrs = std.ArrayList(Abbrev.Attr).init(allocator);
1128 defer attrs.deinit();
1129
1130 while (true) {
1131 const code = try fbr.readUleb128(u64);
1132 if (code == 0) break;
1133 const tag_id = try fbr.readUleb128(u64);
1134 const has_children = (try fbr.readByte()) == CHILDREN.yes;
1135
1136 while (true) {
1137 const attr_id = try fbr.readUleb128(u64);
1138 const form_id = try fbr.readUleb128(u64);
1139 if (attr_id == 0 and form_id == 0) break;
1140 try attrs.append(.{
1141 .id = attr_id,
1142 .form_id = form_id,
1143 .payload = switch (form_id) {
1144 FORM.implicit_const => try fbr.readIleb128(i64),
1145 else => undefined,
1146 },
1147 });
1148 }
1149
1150 try abbrevs.append(.{
1151 .code = code,
1152 .tag_id = tag_id,
1153 .has_children = has_children,
1154 .attrs = try attrs.toOwnedSlice(),
1155 });
1156 }
1157
1158 return .{
1159 .offset = offset,
1160 .abbrevs = try abbrevs.toOwnedSlice(),
1161 };
1162 }
1163
1164 fn parseDie(
1165 fbr: *FixedBufferReader,
1166 attrs_buf: []Die.Attr,
1167 abbrev_table: *const Abbrev.Table,
1168 format: Format,
1169 ) !?Die {
1170 const abbrev_code = try fbr.readUleb128(u64);
1171 if (abbrev_code == 0) return null;
1172 const table_entry = abbrev_table.get(abbrev_code) orelse return badDwarf();
1173
1174 const attrs = attrs_buf[0..table_entry.attrs.len];
1175 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = Die.Attr{
1176 .id = attr.id,
1177 .value = try parseFormValue(
1178 fbr,
1179 attr.form_id,
1180 format,
1181 attr.payload,
1182 ),
1183 };
1184 return .{
1185 .tag_id = table_entry.tag_id,
1186 .has_children = table_entry.has_children,
1187 .attrs = attrs,
1188 };
1189 }
1190
1191 pub fn getLineNumberInfo(
1192 di: *DwarfInfo,
1193 allocator: mem.Allocator,
1194 compile_unit: CompileUnit,
1195 target_address: u64,
1196 ) !debug.LineInfo {
1197 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit);
1198 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
1199
1200 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian };
1201 try fbr.seekTo(line_info_offset);
1202
1203 const unit_header = try readUnitHeader(&fbr, null);
1204 if (unit_header.unit_length == 0) return missingDwarf();
1205 const next_offset = unit_header.header_length + unit_header.unit_length;
1206
1207 const version = try fbr.readInt(u16);
1208 if (version < 2) return badDwarf();
1209
1210 var addr_size: u8 = switch (unit_header.format) {
1211 .@"32" => 4,
1212 .@"64" => 8,
1213 };
1214 var seg_size: u8 = 0;
1215 if (version >= 5) {
1216 addr_size = try fbr.readByte();
1217 seg_size = try fbr.readByte();
1218 }
1219
1220 const prologue_length = try fbr.readAddress(unit_header.format);
1221 const prog_start_offset = fbr.pos + prologue_length;
1222
1223 const minimum_instruction_length = try fbr.readByte();
1224 if (minimum_instruction_length == 0) return badDwarf();
1225
1226 if (version >= 4) {
1227 // maximum_operations_per_instruction
1228 _ = try fbr.readByte();
1229 }
1230
1231 const default_is_stmt = (try fbr.readByte()) != 0;
1232 const line_base = try fbr.readByteSigned();
1233
1234 const line_range = try fbr.readByte();
1235 if (line_range == 0) return badDwarf();
1236
1237 const opcode_base = try fbr.readByte();
1238
1239 const standard_opcode_lengths = try fbr.readBytes(opcode_base - 1);
1240
1241 var include_directories = std.ArrayList(FileEntry).init(allocator);
1242 defer include_directories.deinit();
1243 var file_entries = std.ArrayList(FileEntry).init(allocator);
1244 defer file_entries.deinit();
1245
1246 if (version < 5) {
1247 try include_directories.append(.{ .path = compile_unit_cwd });
1248
1249 while (true) {
1250 const dir = try fbr.readBytesTo(0);
1251 if (dir.len == 0) break;
1252 try include_directories.append(.{ .path = dir });
1253 }
1254
1255 while (true) {
1256 const file_name = try fbr.readBytesTo(0);
1257 if (file_name.len == 0) break;
1258 const dir_index = try fbr.readUleb128(u32);
1259 const mtime = try fbr.readUleb128(u64);
1260 const size = try fbr.readUleb128(u64);
1261 try file_entries.append(.{
1262 .path = file_name,
1263 .dir_index = dir_index,
1264 .mtime = mtime,
1265 .size = size,
1266 });
1267 }
1268 } else {
1269 const FileEntFmt = struct {
1270 content_type_code: u8,
1271 form_code: u16,
1272 };
1273 {
1274 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
1275 const directory_entry_format_count = try fbr.readByte();
1276 if (directory_entry_format_count > dir_ent_fmt_buf.len) return badDwarf();
1277 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
1278 ent_fmt.* = .{
1279 .content_type_code = try fbr.readUleb128(u8),
1280 .form_code = try fbr.readUleb128(u16),
1281 };
1282 }
1283
1284 const directories_count = try fbr.readUleb128(usize);
1285 try include_directories.ensureUnusedCapacity(directories_count);
1286 {
1287 var i: usize = 0;
1288 while (i < directories_count) : (i += 1) {
1289 var e: FileEntry = .{ .path = &.{} };
1290 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
1291 const form_value = try parseFormValue(
1292 &fbr,
1293 ent_fmt.form_code,
1294 unit_header.format,
1295 null,
1296 );
1297 switch (ent_fmt.content_type_code) {
1298 LNCT.path => e.path = try form_value.getString(di.*),
1299 LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
1300 LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
1301 LNCT.size => e.size = try form_value.getUInt(u64),
1302 LNCT.MD5 => e.md5 = switch (form_value) {
1303 .data16 => |data16| data16.*,
1304 else => return badDwarf(),
1305 },
1306 else => continue,
1307 }
1308 }
1309 include_directories.appendAssumeCapacity(e);
1310 }
1311 }
1312 }
1313
1314 var file_ent_fmt_buf: [10]FileEntFmt = undefined;
1315 const file_name_entry_format_count = try fbr.readByte();
1316 if (file_name_entry_format_count > file_ent_fmt_buf.len) return badDwarf();
1317 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
1318 ent_fmt.* = .{
1319 .content_type_code = try fbr.readUleb128(u8),
1320 .form_code = try fbr.readUleb128(u16),
1321 };
1322 }
1323
1324 const file_names_count = try fbr.readUleb128(usize);
1325 try file_entries.ensureUnusedCapacity(file_names_count);
1326 {
1327 var i: usize = 0;
1328 while (i < file_names_count) : (i += 1) {
1329 var e: FileEntry = .{ .path = &.{} };
1330 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
1331 const form_value = try parseFormValue(
1332 &fbr,
1333 ent_fmt.form_code,
1334 unit_header.format,
1335 null,
1336 );
1337 switch (ent_fmt.content_type_code) {
1338 LNCT.path => e.path = try form_value.getString(di.*),
1339 LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
1340 LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
1341 LNCT.size => e.size = try form_value.getUInt(u64),
1342 LNCT.MD5 => e.md5 = switch (form_value) {
1343 .data16 => |data16| data16.*,
1344 else => return badDwarf(),
1345 },
1346 else => continue,
1347 }
1348 }
1349 file_entries.appendAssumeCapacity(e);
1350 }
1351 }
1352 }
1353
1354 var prog = LineNumberProgram.init(
1355 default_is_stmt,
1356 include_directories.items,
1357 target_address,
1358 version,
1359 );
1360
1361 try fbr.seekTo(prog_start_offset);
1362
1363 const next_unit_pos = line_info_offset + next_offset;
1364
1365 while (fbr.pos < next_unit_pos) {
1366 const opcode = try fbr.readByte();
1367
1368 if (opcode == LNS.extended_op) {
1369 const op_size = try fbr.readUleb128(u64);
1370 if (op_size < 1) return badDwarf();
1371 const sub_op = try fbr.readByte();
1372 switch (sub_op) {
1373 LNE.end_sequence => {
1374 prog.end_sequence = true;
1375 if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info;
1376 prog.reset();
1377 },
1378 LNE.set_address => {
1379 const addr = try fbr.readInt(usize);
1380 prog.address = addr;
1381 },
1382 LNE.define_file => {
1383 const path = try fbr.readBytesTo(0);
1384 const dir_index = try fbr.readUleb128(u32);
1385 const mtime = try fbr.readUleb128(u64);
1386 const size = try fbr.readUleb128(u64);
1387 try file_entries.append(.{
1388 .path = path,
1389 .dir_index = dir_index,
1390 .mtime = mtime,
1391 .size = size,
1392 });
1393 },
1394 else => try fbr.seekForward(op_size - 1),
1395 }
1396 } else if (opcode >= opcode_base) {
1397 // special opcodes
1398 const adjusted_opcode = opcode - opcode_base;
1399 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
1400 const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
1401 prog.line += inc_line;
1402 prog.address += inc_addr;
1403 if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info;
1404 prog.basic_block = false;
1405 } else {
1406 switch (opcode) {
1407 LNS.copy => {
1408 if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info;
1409 prog.basic_block = false;
1410 },
1411 LNS.advance_pc => {
1412 const arg = try fbr.readUleb128(usize);
1413 prog.address += arg * minimum_instruction_length;
1414 },
1415 LNS.advance_line => {
1416 const arg = try fbr.readIleb128(i64);
1417 prog.line += arg;
1418 },
1419 LNS.set_file => {
1420 const arg = try fbr.readUleb128(usize);
1421 prog.file = arg;
1422 },
1423 LNS.set_column => {
1424 const arg = try fbr.readUleb128(u64);
1425 prog.column = arg;
1426 },
1427 LNS.negate_stmt => {
1428 prog.is_stmt = !prog.is_stmt;
1429 },
1430 LNS.set_basic_block => {
1431 prog.basic_block = true;
1432 },
1433 LNS.const_add_pc => {
1434 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
1435 prog.address += inc_addr;
1436 },
1437 LNS.fixed_advance_pc => {
1438 const arg = try fbr.readInt(u16);
1439 prog.address += arg;
1440 },
1441 LNS.set_prologue_end => {},
1442 else => {
1443 if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf();
1444 try fbr.seekForward(standard_opcode_lengths[opcode - 1]);
1445 },
1446 }
1447 }
1448 }
1449
1450 return missingDwarf();
1451 }
1452
1453 fn getString(di: DwarfInfo, offset: u64) ![:0]const u8 {
1454 return getStringGeneric(di.section(.debug_str), offset);
1455 }
1456
1457 fn getLineString(di: DwarfInfo, offset: u64) ![:0]const u8 {
1458 return getStringGeneric(di.section(.debug_line_str), offset);
1459 }
1460
1461 fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 {
1462 const debug_addr = di.section(.debug_addr) orelse return badDwarf();
1463
1464 // addr_base points to the first item after the header, however we
1465 // need to read the header to know the size of each item. Empirically,
1466 // it may disagree with is_64 on the compile unit.
1467 // The header is 8 or 12 bytes depending on is_64.
1468 if (compile_unit.addr_base < 8) return badDwarf();
1469
1470 const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);
1471 if (version != 5) return badDwarf();
1472
1473 const addr_size = debug_addr[compile_unit.addr_base - 2];
1474 const seg_size = debug_addr[compile_unit.addr_base - 1];
1475
1476 const byte_offset = @as(usize, @intCast(compile_unit.addr_base + (addr_size + seg_size) * index));
1477 if (byte_offset + addr_size > debug_addr.len) return badDwarf();
1478 return switch (addr_size) {
1479 1 => debug_addr[byte_offset],
1480 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
1481 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
1482 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
1483 else => badDwarf(),
1484 };
1485 }
1486
1487 /// If .eh_frame_hdr is present, then only the header needs to be parsed.
1488 ///
1489 /// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list
1490 /// of FDEs is built for binary searching during unwinding.
1491 pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, base_address: usize) !void {
1492 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
1493 var fbr: FixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian };
1494
1495 const version = try fbr.readByte();
1496 if (version != 1) break :blk;
1497
1498 const eh_frame_ptr_enc = try fbr.readByte();
1499 if (eh_frame_ptr_enc == EH.PE.omit) break :blk;
1500 const fde_count_enc = try fbr.readByte();
1501 if (fde_count_enc == EH.PE.omit) break :blk;
1502 const table_enc = try fbr.readByte();
1503 if (table_enc == EH.PE.omit) break :blk;
1504
1505 const eh_frame_ptr = math.cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{
1506 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
1507 .follow_indirect = true,
1508 }) orelse return badDwarf()) orelse return badDwarf();
1509
1510 const fde_count = math.cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{
1511 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
1512 .follow_indirect = true,
1513 }) orelse return badDwarf()) orelse return badDwarf();
1514
1515 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);
1516 const entries_len = fde_count * entry_size;
1517 if (entries_len > eh_frame_hdr.len - fbr.pos) return badDwarf();
1518
1519 di.eh_frame_hdr = .{
1520 .eh_frame_ptr = eh_frame_ptr,
1521 .table_enc = table_enc,
1522 .fde_count = fde_count,
1523 .entries = eh_frame_hdr[fbr.pos..][0..entries_len],
1524 };
1525
1526 // No need to scan .eh_frame, we have a binary search table already
1527 return;
1528 }
1529
1530 const frame_sections = [2]DwarfSection{ .eh_frame, .debug_frame };
1531 for (frame_sections) |frame_section| {
1532 if (di.section(frame_section)) |section_data| {
1533 var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian };
1534 while (fbr.pos < fbr.buf.len) {
1535 const entry_header = try EntryHeader.read(&fbr, null, frame_section);
1536 switch (entry_header.type) {
1537 .cie => {
1538 const cie = try CommonInformationEntry.parse(
1539 entry_header.entry_bytes,
1540 di.sectionVirtualOffset(frame_section, base_address).?,
1541 true,
1542 entry_header.format,
1543 frame_section,
1544 entry_header.length_offset,
1545 @sizeOf(usize),
1546 di.endian,
1547 );
1548 try di.cie_map.put(allocator, entry_header.length_offset, cie);
1549 },
1550 .fde => |cie_offset| {
1551 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
1552 const fde = try FrameDescriptionEntry.parse(
1553 entry_header.entry_bytes,
1554 di.sectionVirtualOffset(frame_section, base_address).?,
1555 true,
1556 cie,
1557 @sizeOf(usize),
1558 di.endian,
1559 );
1560 try di.fde_list.append(allocator, fde);
1561 },
1562 .terminator => break,
1563 }
1564 }
1565
1566 mem.sortUnstable(FrameDescriptionEntry, di.fde_list.items, {}, struct {
1567 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
1568 _ = ctx;
1569 return a.pc_begin < b.pc_begin;
1570 }
1571 }.lessThan);
1572 }
1573 }
1574 }
1575
1576 /// Unwind a stack frame using DWARF unwinding info, updating the register context.
1577 ///
1578 /// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE.
1579 /// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE.
1580 ///
1581 /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
1582 /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
1583 pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, ma: *debug.StackIterator.MemoryAccessor, explicit_fde_offset: ?usize) !usize {
1584 if (!comptime abi.supportsUnwinding(builtin.target)) return error.UnsupportedCpuArchitecture;
1585 if (context.pc == 0) return 0;
1586
1587 // Find the FDE and CIE
1588 var cie: CommonInformationEntry = undefined;
1589 var fde: FrameDescriptionEntry = undefined;
1590
1591 if (explicit_fde_offset) |fde_offset| {
1592 const dwarf_section: DwarfSection = .eh_frame;
1593 const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
1594 if (fde_offset >= frame_section.len) return error.MissingFDE;
1595
1596 var fbr: FixedBufferReader = .{
1597 .buf = frame_section,
1598 .pos = fde_offset,
1599 .endian = di.endian,
1600 };
1601
1602 const fde_entry_header = try EntryHeader.read(&fbr, null, dwarf_section);
1603 if (fde_entry_header.type != .fde) return error.MissingFDE;
1604
1605 const cie_offset = fde_entry_header.type.fde;
1606 try fbr.seekTo(cie_offset);
1607
1608 fbr.endian = native_endian;
1609 const cie_entry_header = try EntryHeader.read(&fbr, null, dwarf_section);
1610 if (cie_entry_header.type != .cie) return badDwarf();
1611
1612 cie = try CommonInformationEntry.parse(
1613 cie_entry_header.entry_bytes,
1614 0,
1615 true,
1616 cie_entry_header.format,
1617 dwarf_section,
1618 cie_entry_header.length_offset,
1619 @sizeOf(usize),
1620 native_endian,
1621 );
1622
1623 fde = try FrameDescriptionEntry.parse(
1624 fde_entry_header.entry_bytes,
1625 0,
1626 true,
1627 cie,
1628 @sizeOf(usize),
1629 native_endian,
1630 );
1631 } else if (di.eh_frame_hdr) |header| {
1632 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
1633 try header.findEntry(
1634 ma,
1635 eh_frame_len,
1636 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1637 context.pc,
1638 &cie,
1639 &fde,
1640 );
1641 } else {
1642 const index = std.sort.binarySearch(FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct {
1643 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) math.Order {
1644 if (pc < mid_item.pc_begin) return .lt;
1645
1646 const range_end = mid_item.pc_begin + mid_item.pc_range;
1647 if (pc < range_end) return .eq;
1648
1649 return .gt;
1650 }
1651 }.compareFn);
1652
1653 fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
1654 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
1655 }
1656
1657 var expression_context: expressions.ExpressionContext = .{
1658 .format = cie.format,
1659 .memory_accessor = ma,
1660 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1661 .thread_context = context.thread_context,
1662 .reg_context = context.reg_context,
1663 .cfa = context.cfa,
1664 };
1665
1666 context.vm.reset();
1667 context.reg_context.eh_frame = cie.version != 4;
1668 context.reg_context.is_macho = di.is_macho;
1669
1670 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
1671 context.cfa = switch (row.cfa.rule) {
1672 .val_offset => |offset| blk: {
1673 const register = row.cfa.register orelse return error.InvalidCFARule;
1674 const value = mem.readInt(usize, (try abi.regBytes(context.thread_context, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
1675 break :blk try call_frame.applyOffset(value, offset);
1676 },
1677 .expression => |expression| blk: {
1678 context.stack_machine.reset();
1679 const value = try context.stack_machine.run(
1680 expression,
1681 context.allocator,
1682 expression_context,
1683 context.cfa,
1684 );
1685
1686 if (value) |v| {
1687 if (v != .generic) return error.InvalidExpressionValue;
1688 break :blk v.generic;
1689 } else return error.NoExpressionValue;
1690 },
1691 else => return error.InvalidCFARule,
1692 };
1693
1694 if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA;
1695 expression_context.cfa = context.cfa;
1696
1697 // Buffering the modifications is done because copying the thread context is not portable,
1698 // some implementations (ie. darwin) use internal pointers to the mcontext.
1699 var arena = std.heap.ArenaAllocator.init(context.allocator);
1700 defer arena.deinit();
1701 const update_allocator = arena.allocator();
1702
1703 const RegisterUpdate = struct {
1704 // Backed by thread_context
1705 dest: []u8,
1706 // Backed by arena
1707 src: []const u8,
1708 prev: ?*@This(),
1709 };
1710
1711 var update_tail: ?*RegisterUpdate = null;
1712 var has_return_address = true;
1713 for (context.vm.rowColumns(row)) |column| {
1714 if (column.register) |register| {
1715 if (register == cie.return_address_register) {
1716 has_return_address = column.rule != .undefined;
1717 }
1718
1719 const dest = try abi.regBytes(context.thread_context, register, context.reg_context);
1720 const src = try update_allocator.alloc(u8, dest.len);
1721
1722 const prev = update_tail;
1723 update_tail = try update_allocator.create(RegisterUpdate);
1724 update_tail.?.* = .{
1725 .dest = dest,
1726 .src = src,
1727 .prev = prev,
1728 };
1729
1730 try column.resolveValue(
1731 context,
1732 expression_context,
1733 ma,
1734 src,
1735 );
1736 }
1737 }
1738
1739 // On all implemented architectures, the CFA is defined as being the previous frame's SP
1740 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?;
1741
1742 while (update_tail) |tail| {
1743 @memcpy(tail.dest, tail.src);
1744 update_tail = tail.prev;
1745 }
1746
1747 if (has_return_address) {
1748 context.pc = abi.stripInstructionPtrAuthCode(mem.readInt(usize, (try abi.regBytes(
1749 context.thread_context,
1750 cie.return_address_register,
1751 context.reg_context,
1752 ))[0..@sizeOf(usize)], native_endian));
1753 } else {
1754 context.pc = 0;
1755 }
1756
1757 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
1758
1759 // The call instruction will have pushed the address of the instruction that follows the call as the return address.
1760 // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
1761 // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
1762 // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
1763 // we subtract one so that the next lookup is guaranteed to land inside the
1764 //
1765 // The exception to this rule is signal frames, where we return execution would be returned to the instruction
1766 // that triggered the handler.
1767 const return_address = context.pc;
1768 if (context.pc > 0 and !cie.isSignalFrame()) context.pc -= 1;
1769
1770 return return_address;
1771 }
1772};
1773
1774/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
1775fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
1776 return switch (unwind_reg_number) {
1777 1 => 3, // RBX
1778 2 => 12, // R12
1779 3 => 13, // R13
1780 4 => 14, // R14
1781 5 => 15, // R15
1782 6 => 6, // RBP
1783 else => error.InvalidUnwindRegisterNumber,
1784 };
1785}
1786
1787const macho = std.macho;
1788
1789/// Unwind a frame using MachO compact unwind info (from __unwind_info).
1790/// If the compact encoding can't encode a way to unwind a frame, it will
1791/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
1792pub fn unwindFrameMachO(
1793 context: *UnwindContext,
1794 ma: *debug.StackIterator.MemoryAccessor,
1795 unwind_info: []const u8,
1796 eh_frame: ?[]const u8,
1797 module_base_address: usize,
1798) !usize {
1799 const header = mem.bytesAsValue(
1800 macho.unwind_info_section_header,
1801 unwind_info[0..@sizeOf(macho.unwind_info_section_header)],
1802 );
1803 const indices = mem.bytesAsSlice(
1804 macho.unwind_info_section_header_index_entry,
1805 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry)],
1806 );
1807 if (indices.len == 0) return error.MissingUnwindInfo;
1808
1809 const mapped_pc = context.pc - module_base_address;
1810 const second_level_index = blk: {
1811 var left: usize = 0;
1812 var len: usize = indices.len;
1813
1814 while (len > 1) {
1815 const mid = left + len / 2;
1816 const offset = indices[mid].functionOffset;
1817 if (mapped_pc < offset) {
1818 len /= 2;
1819 } else {
1820 left = mid;
1821 if (mapped_pc == offset) break;
1822 len -= len / 2;
1823 }
1824 }
1825
1826 // Last index is a sentinel containing the highest address as its functionOffset
1827 if (indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
1828 break :blk &indices[left];
1829 };
1830
1831 const common_encodings = mem.bytesAsSlice(
1832 macho.compact_unwind_encoding_t,
1833 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t)],
1834 );
1835
1836 const start_offset = second_level_index.secondLevelPagesSectionOffset;
1837 const kind = mem.bytesAsValue(
1838 macho.UNWIND_SECOND_LEVEL,
1839 unwind_info[start_offset..][0..@sizeOf(macho.UNWIND_SECOND_LEVEL)],
1840 );
1841
1842 const entry: struct {
1843 function_offset: usize,
1844 raw_encoding: u32,
1845 } = switch (kind.*) {
1846 .REGULAR => blk: {
1847 const page_header = mem.bytesAsValue(
1848 macho.unwind_info_regular_second_level_page_header,
1849 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_regular_second_level_page_header)],
1850 );
1851
1852 const entries = mem.bytesAsSlice(
1853 macho.unwind_info_regular_second_level_entry,
1854 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry)],
1855 );
1856 if (entries.len == 0) return error.InvalidUnwindInfo;
1857
1858 var left: usize = 0;
1859 var len: usize = entries.len;
1860 while (len > 1) {
1861 const mid = left + len / 2;
1862 const offset = entries[mid].functionOffset;
1863 if (mapped_pc < offset) {
1864 len /= 2;
1865 } else {
1866 left = mid;
1867 if (mapped_pc == offset) break;
1868 len -= len / 2;
1869 }
1870 }
1871
1872 break :blk .{
1873 .function_offset = entries[left].functionOffset,
1874 .raw_encoding = entries[left].encoding,
1875 };
1876 },
1877 .COMPRESSED => blk: {
1878 const page_header = mem.bytesAsValue(
1879 macho.unwind_info_compressed_second_level_page_header,
1880 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_compressed_second_level_page_header)],
1881 );
1882
1883 const entries = mem.bytesAsSlice(
1884 macho.UnwindInfoCompressedEntry,
1885 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry)],
1886 );
1887 if (entries.len == 0) return error.InvalidUnwindInfo;
1888
1889 var left: usize = 0;
1890 var len: usize = entries.len;
1891 while (len > 1) {
1892 const mid = left + len / 2;
1893 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
1894 if (mapped_pc < offset) {
1895 len /= 2;
1896 } else {
1897 left = mid;
1898 if (mapped_pc == offset) break;
1899 len -= len / 2;
1900 }
1901 }
1902
1903 const entry = entries[left];
1904 const function_offset = second_level_index.functionOffset + entry.funcOffset;
1905 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
1906 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
1907 break :blk .{
1908 .function_offset = function_offset,
1909 .raw_encoding = common_encodings[entry.encodingIndex],
1910 };
1911 } else {
1912 const local_index = try math.sub(
1913 u8,
1914 entry.encodingIndex,
1915 math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
1916 );
1917 const local_encodings = mem.bytesAsSlice(
1918 macho.compact_unwind_encoding_t,
1919 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t)],
1920 );
1921 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
1922 break :blk .{
1923 .function_offset = function_offset,
1924 .raw_encoding = local_encodings[local_index],
1925 };
1926 }
1927 },
1928 else => return error.InvalidUnwindInfo,
1929 };
1930
1931 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
1932 const reg_context = abi.RegisterContext{
1933 .eh_frame = false,
1934 .is_macho = true,
1935 };
1936
1937 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
1938 const new_ip = switch (builtin.cpu.arch) {
1939 .x86_64 => switch (encoding.mode.x86_64) {
1940 .OLD => return error.UnimplementedUnwindEncoding,
1941 .RBP_FRAME => blk: {
1942 const regs: [5]u3 = .{
1943 encoding.value.x86_64.frame.reg0,
1944 encoding.value.x86_64.frame.reg1,
1945 encoding.value.x86_64.frame.reg2,
1946 encoding.value.x86_64.frame.reg3,
1947 encoding.value.x86_64.frame.reg4,
1948 };
1949
1950 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
1951 var max_reg: usize = 0;
1952 inline for (regs, 0..) |reg, i| {
1953 if (reg > 0) max_reg = i;
1954 }
1955
1956 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
1957 const new_sp = fp + 2 * @sizeOf(usize);
1958
1959 // Verify the stack range we're about to read register values from
1960 if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo;
1961
1962 const ip_ptr = fp + @sizeOf(usize);
1963 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
1964 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
1965
1966 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
1967 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
1968 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
1969
1970 for (regs, 0..) |reg, i| {
1971 if (reg == 0) continue;
1972 const addr = fp - frame_offset + i * @sizeOf(usize);
1973 const reg_number = try compactUnwindToDwarfRegNumber(reg);
1974 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
1975 }
1976
1977 break :blk new_ip;
1978 },
1979 .STACK_IMMD,
1980 .STACK_IND,
1981 => blk: {
1982 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
1983 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
1984 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
1985 else stack_size: {
1986 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
1987 const sub_offset_addr =
1988 module_base_address +
1989 entry.function_offset +
1990 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
1991 if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
1992
1993 // `sub_offset_addr` points to the offset of the literal within the instruction
1994 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
1995 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
1996 };
1997
1998 // Decode the Lehmer-coded sequence of registers.
1999 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
2000
2001 // Decode the variable-based permutation number into its digits. Each digit represents
2002 // an index into the list of register numbers that weren't yet used in the sequence at
2003 // the time the digit was added.
2004 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
2005 const ip_ptr = if (reg_count > 0) reg_blk: {
2006 var digits: [6]u3 = undefined;
2007 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
2008 var base: usize = 2;
2009 for (0..reg_count) |i| {
2010 const div = accumulator / base;
2011 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
2012 accumulator = div;
2013 base += 1;
2014 }
2015
2016 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
2017 var registers: [reg_numbers.len]u3 = undefined;
2018 var used_indices = [_]bool{false} ** reg_numbers.len;
2019 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
2020 var unused_count: u8 = 0;
2021 const unused_index = for (used_indices, 0..) |used, index| {
2022 if (!used) {
2023 if (target_unused_index == unused_count) break index;
2024 unused_count += 1;
2025 }
2026 } else unreachable;
2027
2028 registers[i] = reg_numbers[unused_index];
2029 used_indices[unused_index] = true;
2030 }
2031
2032 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
2033 if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo;
2034 for (0..reg_count) |i| {
2035 const reg_number = try compactUnwindToDwarfRegNumber(registers[i]);
2036 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2037 reg_addr += @sizeOf(usize);
2038 }
2039
2040 break :reg_blk reg_addr;
2041 } else sp + stack_size - @sizeOf(usize);
2042
2043 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2044 const new_sp = ip_ptr + @sizeOf(usize);
2045 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
2046
2047 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2048 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2049
2050 break :blk new_ip;
2051 },
2052 .DWARF => {
2053 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
2054 },
2055 },
2056 .aarch64 => switch (encoding.mode.arm64) {
2057 .OLD => return error.UnimplementedUnwindEncoding,
2058 .FRAMELESS => blk: {
2059 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2060 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
2061 const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*;
2062 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
2063 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2064 break :blk new_ip;
2065 },
2066 .DWARF => {
2067 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
2068 },
2069 .FRAME => blk: {
2070 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2071 const new_sp = fp + 16;
2072 const ip_ptr = fp + @sizeOf(usize);
2073
2074 const num_restored_pairs: usize =
2075 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
2076 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
2077 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
2078
2079 if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo;
2080
2081 var reg_addr = fp - @sizeOf(usize);
2082 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
2083 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
2084 (try abi.regValueNative(usize, context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2085 reg_addr += @sizeOf(usize);
2086 (try abi.regValueNative(usize, context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2087 reg_addr += @sizeOf(usize);
2088 }
2089 }
2090
2091 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
2092 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
2093 // Only the lower half of the 128-bit V registers are restored during unwinding
2094 @memcpy(
2095 try abi.regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
2096 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2097 );
2098 reg_addr += @sizeOf(usize);
2099 @memcpy(
2100 try abi.regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
2101 mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2102 );
2103 reg_addr += @sizeOf(usize);
2104 }
2105 }
2106
2107 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2108 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2109
2110 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2111 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2112
2113 break :blk new_ip;
2114 },
2115 },
2116 else => return error.UnimplementedArch,
2117 };
2118
2119 context.pc = abi.stripInstructionPtrAuthCode(new_ip);
2120 if (context.pc > 0) context.pc -= 1;
2121 return new_ip;
2122}
2123
2124fn unwindFrameMachODwarf(context: *UnwindContext, ma: *debug.StackIterator.MemoryAccessor, eh_frame: []const u8, fde_offset: usize) !usize {
2125 var di = DwarfInfo{
2126 .endian = native_endian,
2127 .is_macho = true,
2128 };
2129 defer di.deinit(context.allocator);
2130
2131 di.sections[@intFromEnum(DwarfSection.eh_frame)] = .{
2132 .data = eh_frame,
2133 .owned = false,
2134 };
2135
2136 return di.unwindFrame(context, ma, fde_offset);
2137}
2138
2139pub const UnwindContext = struct {
2140 allocator: mem.Allocator,
2141 cfa: ?usize,
2142 pc: usize,
2143 thread_context: *debug.ThreadContext,
2144 reg_context: abi.RegisterContext,
2145 vm: call_frame.VirtualMachine,
2146 stack_machine: expressions.StackMachine(.{ .call_frame_context = true }),
2147
2148 pub fn init(
2149 allocator: mem.Allocator,
2150 thread_context: *const debug.ThreadContext,
2151 ) !UnwindContext {
2152 const pc = abi.stripInstructionPtrAuthCode(
2153 (try abi.regValueNative(
2154 usize,
2155 thread_context,
2156 abi.ipRegNum(),
2157 null,
2158 )).*,
2159 );
2160
2161 const context_copy = try allocator.create(debug.ThreadContext);
2162 debug.copyContext(thread_context, context_copy);
2163
2164 return .{
2165 .allocator = allocator,
2166 .cfa = null,
2167 .pc = pc,
2168 .thread_context = context_copy,
2169 .reg_context = undefined,
2170 .vm = .{},
2171 .stack_machine = .{},
2172 };
2173 }
2174
2175 pub fn deinit(self: *UnwindContext) void {
2176 self.vm.deinit(self.allocator);
2177 self.stack_machine.deinit(self.allocator);
2178 self.allocator.destroy(self.thread_context);
2179 self.* = undefined;
2180 }
2181
2182 pub fn getFp(self: *const UnwindContext) !usize {
2183 return (try abi.regValueNative(usize, self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context)).*;
2184 }
2185};
2186
2187/// Initialize DWARF info. The caller has the responsibility to initialize most
2188/// the DwarfInfo fields before calling. `binary_mem` is the raw bytes of the
2189/// main binary file (not the secondary debug info file).
2190pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
2191 try di.scanAllFunctions(allocator);
2192 try di.scanAllCompileUnits(allocator);
2193}
2194
2195/// This function is to make it handy to comment out the return and make it
2196/// into a crash when working on this file.
2197fn badDwarf() error{InvalidDebugInfo} {
2198 //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file
2199 return error.InvalidDebugInfo;
2200}
2201
2202fn missingDwarf() error{MissingDebugInfo} {
2203 //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file
2204 return error.MissingDebugInfo;
2205}
2206
2207fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
2208 const str = opt_str orelse return badDwarf();
2209 if (offset > str.len) return badDwarf();
2210 const casted_offset = math.cast(usize, offset) orelse return badDwarf();
2211 // Valid strings always have a terminating zero byte
2212 const last = mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf();
2213 return str[casted_offset..last :0];
2214}
2215
2216const EhPointerContext = struct {
2217 // The address of the pointer field itself
2218 pc_rel_base: u64,
2219
2220 // Whether or not to follow indirect pointers. This should only be
2221 // used when decoding pointers at runtime using the current process's
2222 // debug info
2223 follow_indirect: bool,
2224
2225 // These relative addressing modes are only used in specific cases, and
2226 // might not be available / required in all parsing contexts
2227 data_rel_base: ?u64 = null,
2228 text_rel_base: ?u64 = null,
2229 function_rel_base: ?u64 = null,
2230};
2231fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext) !?u64 {
2232 if (enc == EH.PE.omit) return null;
2233
2234 const value: union(enum) {
2235 signed: i64,
2236 unsigned: u64,
2237 } = switch (enc & EH.PE.type_mask) {
2238 EH.PE.absptr => .{
2239 .unsigned = switch (addr_size_bytes) {
2240 2 => try fbr.readInt(u16),
2241 4 => try fbr.readInt(u32),
2242 8 => try fbr.readInt(u64),
2243 else => return error.InvalidAddrSize,
2244 },
2245 },
2246 EH.PE.uleb128 => .{ .unsigned = try fbr.readUleb128(u64) },
2247 EH.PE.udata2 => .{ .unsigned = try fbr.readInt(u16) },
2248 EH.PE.udata4 => .{ .unsigned = try fbr.readInt(u32) },
2249 EH.PE.udata8 => .{ .unsigned = try fbr.readInt(u64) },
2250 EH.PE.sleb128 => .{ .signed = try fbr.readIleb128(i64) },
2251 EH.PE.sdata2 => .{ .signed = try fbr.readInt(i16) },
2252 EH.PE.sdata4 => .{ .signed = try fbr.readInt(i32) },
2253 EH.PE.sdata8 => .{ .signed = try fbr.readInt(i64) },
2254 else => return badDwarf(),
2255 };
2256
2257 const base = switch (enc & EH.PE.rel_mask) {
2258 EH.PE.pcrel => ctx.pc_rel_base,
2259 EH.PE.textrel => ctx.text_rel_base orelse return error.PointerBaseNotSpecified,
2260 EH.PE.datarel => ctx.data_rel_base orelse return error.PointerBaseNotSpecified,
2261 EH.PE.funcrel => ctx.function_rel_base orelse return error.PointerBaseNotSpecified,
2262 else => null,
2263 };
2264
2265 const ptr: u64 = if (base) |b| switch (value) {
2266 .signed => |s| @intCast(try math.add(i64, s, @as(i64, @intCast(b)))),
2267 // absptr can actually contain signed values in some cases (aarch64 MachO)
2268 .unsigned => |u| u +% b,
2269 } else switch (value) {
2270 .signed => |s| @as(u64, @intCast(s)),
2271 .unsigned => |u| u,
2272 };
2273
2274 if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {
2275 if (@sizeOf(usize) != addr_size_bytes) {
2276 // See the documentation for `follow_indirect`
2277 return error.NonNativeIndirection;
2278 }
2279
2280 const native_ptr = math.cast(usize, ptr) orelse return error.PointerOverflow;
2281 return switch (addr_size_bytes) {
2282 2, 4, 8 => return @as(*const usize, @ptrFromInt(native_ptr)).*,
2283 else => return error.UnsupportedAddrSize,
2284 };
2285 } else {
2286 return ptr;
2287 }
2288}
2289
2290/// This represents the decoded .eh_frame_hdr header
2291pub const ExceptionFrameHeader = struct {
2292 eh_frame_ptr: usize,
2293 table_enc: u8,
2294 fde_count: usize,
2295 entries: []const u8,
2296
2297 pub fn entrySize(table_enc: u8) !u8 {
2298 return switch (table_enc & EH.PE.type_mask) {
2299 EH.PE.udata2,
2300 EH.PE.sdata2,
2301 => 4,
2302 EH.PE.udata4,
2303 EH.PE.sdata4,
2304 => 8,
2305 EH.PE.udata8,
2306 EH.PE.sdata8,
2307 => 16,
2308 // This is a binary search table, so all entries must be the same length
2309 else => return badDwarf(),
2310 };
2311 }
2312
2313 fn isValidPtr(
2314 self: ExceptionFrameHeader,
2315 comptime T: type,
2316 ptr: usize,
2317 ma: *debug.StackIterator.MemoryAccessor,
2318 eh_frame_len: ?usize,
2319 ) bool {
2320 if (eh_frame_len) |len| {
2321 return ptr >= self.eh_frame_ptr and ptr <= self.eh_frame_ptr + len - @sizeOf(T);
2322 } else {
2323 return ma.load(T, ptr) != null;
2324 }
2325 }
2326
2327 /// Find an entry by binary searching the eh_frame_hdr section.
2328 ///
2329 /// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller,
2330 /// MemoryAccessor will be used to verify readability of the header entries.
2331 /// If `eh_frame_len` is provided, then these checks can be skipped.
2332 pub fn findEntry(
2333 self: ExceptionFrameHeader,
2334 ma: *debug.StackIterator.MemoryAccessor,
2335 eh_frame_len: ?usize,
2336 eh_frame_hdr_ptr: usize,
2337 pc: usize,
2338 cie: *CommonInformationEntry,
2339 fde: *FrameDescriptionEntry,
2340 ) !void {
2341 const entry_size = try entrySize(self.table_enc);
2342
2343 var left: usize = 0;
2344 var len: usize = self.fde_count;
2345
2346 var fbr: FixedBufferReader = .{ .buf = self.entries, .endian = native_endian };
2347
2348 while (len > 1) {
2349 const mid = left + len / 2;
2350
2351 fbr.pos = mid * entry_size;
2352 const pc_begin = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
2353 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
2354 .follow_indirect = true,
2355 .data_rel_base = eh_frame_hdr_ptr,
2356 }) orelse return badDwarf();
2357
2358 if (pc < pc_begin) {
2359 len /= 2;
2360 } else {
2361 left = mid;
2362 if (pc == pc_begin) break;
2363 len -= len / 2;
2364 }
2365 }
2366
2367 if (len == 0) return badDwarf();
2368 fbr.pos = left * entry_size;
2369
2370 // Read past the pc_begin field of the entry
2371 _ = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
2372 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
2373 .follow_indirect = true,
2374 .data_rel_base = eh_frame_hdr_ptr,
2375 }) orelse return badDwarf();
2376
2377 const fde_ptr = math.cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
2378 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
2379 .follow_indirect = true,
2380 .data_rel_base = eh_frame_hdr_ptr,
2381 }) orelse return badDwarf()) orelse return badDwarf();
2382
2383 if (fde_ptr < self.eh_frame_ptr) return badDwarf();
2384
2385 // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor
2386 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse math.maxInt(u32)];
2387
2388 const fde_offset = fde_ptr - self.eh_frame_ptr;
2389 var eh_frame_fbr: FixedBufferReader = .{
2390 .buf = eh_frame,
2391 .pos = fde_offset,
2392 .endian = native_endian,
2393 };
2394
2395 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
2396 if (!self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf();
2397 if (fde_entry_header.type != .fde) return badDwarf();
2398
2399 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
2400 const cie_offset = fde_entry_header.type.fde;
2401 try eh_frame_fbr.seekTo(cie_offset);
2402 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
2403 if (!self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf();
2404 if (cie_entry_header.type != .cie) return badDwarf();
2405
2406 cie.* = try CommonInformationEntry.parse(
2407 cie_entry_header.entry_bytes,
2408 0,
2409 true,
2410 cie_entry_header.format,
2411 .eh_frame,
2412 cie_entry_header.length_offset,
2413 @sizeOf(usize),
2414 native_endian,
2415 );
2416
2417 fde.* = try FrameDescriptionEntry.parse(
2418 fde_entry_header.entry_bytes,
2419 0,
2420 true,
2421 cie.*,
2422 @sizeOf(usize),
2423 native_endian,
2424 );
2425 }
2426};
2427
2428pub const EntryHeader = struct {
2429 /// Offset of the length field in the backing buffer
2430 length_offset: usize,
2431 format: Format,
2432 type: union(enum) {
2433 cie,
2434 /// Value is the offset of the corresponding CIE
2435 fde: u64,
2436 terminator,
2437 },
2438 /// The entry's contents, not including the ID field
2439 entry_bytes: []const u8,
2440
2441 /// The length of the entry including the ID field, but not the length field itself
2442 pub fn entryLength(self: EntryHeader) usize {
2443 return self.entry_bytes.len + @as(u8, if (self.format == .@"64") 8 else 4);
2444 }
2445
2446 /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure.
2447 /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections.
2448 pub fn read(
2449 fbr: *FixedBufferReader,
2450 opt_ma: ?*debug.StackIterator.MemoryAccessor,
2451 dwarf_section: DwarfSection,
2452 ) !EntryHeader {
2453 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
2454
2455 const length_offset = fbr.pos;
2456 const unit_header = try readUnitHeader(fbr, opt_ma);
2457 const unit_length = math.cast(usize, unit_header.unit_length) orelse return badDwarf();
2458 if (unit_length == 0) return .{
2459 .length_offset = length_offset,
2460 .format = unit_header.format,
2461 .type = .terminator,
2462 .entry_bytes = &.{},
2463 };
2464 const start_offset = fbr.pos;
2465 const end_offset = start_offset + unit_length;
2466 defer fbr.pos = end_offset;
2467
2468 const id = try if (opt_ma) |ma|
2469 fbr.readAddressChecked(unit_header.format, ma)
2470 else
2471 fbr.readAddress(unit_header.format);
2472 const entry_bytes = fbr.buf[fbr.pos..end_offset];
2473 const cie_id: u64 = switch (dwarf_section) {
2474 .eh_frame => CommonInformationEntry.eh_id,
2475 .debug_frame => switch (unit_header.format) {
2476 .@"32" => CommonInformationEntry.dwarf32_id,
2477 .@"64" => CommonInformationEntry.dwarf64_id,
2478 },
2479 else => unreachable,
2480 };
2481
2482 return .{
2483 .length_offset = length_offset,
2484 .format = unit_header.format,
2485 .type = if (id == cie_id) .cie else .{ .fde = switch (dwarf_section) {
2486 .eh_frame => try math.sub(u64, start_offset, id),
2487 .debug_frame => id,
2488 else => unreachable,
2489 } },
2490 .entry_bytes = entry_bytes,
2491 };
2492 }
2493};
2494
2495pub const CommonInformationEntry = struct {
2496 // Used in .eh_frame
2497 pub const eh_id = 0;
2498
2499 // Used in .debug_frame (DWARF32)
2500 pub const dwarf32_id = math.maxInt(u32);
2501
2502 // Used in .debug_frame (DWARF64)
2503 pub const dwarf64_id = math.maxInt(u64);
2504
2505 // Offset of the length field of this entry in the eh_frame section.
2506 // This is the key that FDEs use to reference CIEs.
2507 length_offset: u64,
2508 version: u8,
2509 address_size: u8,
2510 format: Format,
2511
2512 // Only present in version 4
2513 segment_selector_size: ?u8,
2514
2515 code_alignment_factor: u32,
2516 data_alignment_factor: i32,
2517 return_address_register: u8,
2518
2519 aug_str: []const u8,
2520 aug_data: []const u8,
2521 lsda_pointer_enc: u8,
2522 personality_enc: ?u8,
2523 personality_routine_pointer: ?u64,
2524 fde_pointer_enc: u8,
2525 initial_instructions: []const u8,
2526
2527 pub fn isSignalFrame(self: CommonInformationEntry) bool {
2528 for (self.aug_str) |c| if (c == 'S') return true;
2529 return false;
2530 }
2531
2532 pub fn addressesSignedWithBKey(self: CommonInformationEntry) bool {
2533 for (self.aug_str) |c| if (c == 'B') return true;
2534 return false;
2535 }
2536
2537 pub fn mteTaggedFrame(self: CommonInformationEntry) bool {
2538 for (self.aug_str) |c| if (c == 'G') return true;
2539 return false;
2540 }
2541
2542 /// This function expects to read the CIE starting with the version field.
2543 /// The returned struct references memory backed by cie_bytes.
2544 ///
2545 /// See the FrameDescriptionEntry.parse documentation for the description
2546 /// of `pc_rel_offset` and `is_runtime`.
2547 ///
2548 /// `length_offset` specifies the offset of this CIE's length field in the
2549 /// .eh_frame / .debug_frame section.
2550 pub fn parse(
2551 cie_bytes: []const u8,
2552 pc_rel_offset: i64,
2553 is_runtime: bool,
2554 format: Format,
2555 dwarf_section: DwarfSection,
2556 length_offset: u64,
2557 addr_size_bytes: u8,
2558 endian: std.builtin.Endian,
2559 ) !CommonInformationEntry {
2560 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;
2561
2562 var fbr: FixedBufferReader = .{ .buf = cie_bytes, .endian = endian };
2563
2564 const version = try fbr.readByte();
2565 switch (dwarf_section) {
2566 .eh_frame => if (version != 1 and version != 3) return error.UnsupportedDwarfVersion,
2567 .debug_frame => if (version != 4) return error.UnsupportedDwarfVersion,
2568 else => return error.UnsupportedDwarfSection,
2569 }
2570
2571 var has_eh_data = false;
2572 var has_aug_data = false;
2573
2574 var aug_str_len: usize = 0;
2575 const aug_str_start = fbr.pos;
2576 var aug_byte = try fbr.readByte();
2577 while (aug_byte != 0) : (aug_byte = try fbr.readByte()) {
2578 switch (aug_byte) {
2579 'z' => {
2580 if (aug_str_len != 0) return badDwarf();
2581 has_aug_data = true;
2582 },
2583 'e' => {
2584 if (has_aug_data or aug_str_len != 0) return badDwarf();
2585 if (try fbr.readByte() != 'h') return badDwarf();
2586 has_eh_data = true;
2587 },
2588 else => if (has_eh_data) return badDwarf(),
2589 }
2590
2591 aug_str_len += 1;
2592 }
2593
2594 if (has_eh_data) {
2595 // legacy data created by older versions of gcc - unsupported here
2596 for (0..addr_size_bytes) |_| _ = try fbr.readByte();
2597 }
2598
2599 const address_size = if (version == 4) try fbr.readByte() else addr_size_bytes;
2600 const segment_selector_size = if (version == 4) try fbr.readByte() else null;
2601
2602 const code_alignment_factor = try fbr.readUleb128(u32);
2603 const data_alignment_factor = try fbr.readIleb128(i32);
2604 const return_address_register = if (version == 1) try fbr.readByte() else try fbr.readUleb128(u8);
2605
2606 var lsda_pointer_enc: u8 = EH.PE.omit;
2607 var personality_enc: ?u8 = null;
2608 var personality_routine_pointer: ?u64 = null;
2609 var fde_pointer_enc: u8 = EH.PE.absptr;
2610
2611 var aug_data: []const u8 = &[_]u8{};
2612 const aug_str = if (has_aug_data) blk: {
2613 const aug_data_len = try fbr.readUleb128(usize);
2614 const aug_data_start = fbr.pos;
2615 aug_data = cie_bytes[aug_data_start..][0..aug_data_len];
2616
2617 const aug_str = cie_bytes[aug_str_start..][0..aug_str_len];
2618 for (aug_str[1..]) |byte| {
2619 switch (byte) {
2620 'L' => {
2621 lsda_pointer_enc = try fbr.readByte();
2622 },
2623 'P' => {
2624 personality_enc = try fbr.readByte();
2625 personality_routine_pointer = try readEhPointer(&fbr, personality_enc.?, addr_size_bytes, .{
2626 .pc_rel_base = try pcRelBase(@intFromPtr(&cie_bytes[fbr.pos]), pc_rel_offset),
2627 .follow_indirect = is_runtime,
2628 });
2629 },
2630 'R' => {
2631 fde_pointer_enc = try fbr.readByte();
2632 },
2633 'S', 'B', 'G' => {},
2634 else => return badDwarf(),
2635 }
2636 }
2637
2638 // aug_data_len can include padding so the CIE ends on an address boundary
2639 fbr.pos = aug_data_start + aug_data_len;
2640 break :blk aug_str;
2641 } else &[_]u8{};
2642
2643 const initial_instructions = cie_bytes[fbr.pos..];
2644 return .{
2645 .length_offset = length_offset,
2646 .version = version,
2647 .address_size = address_size,
2648 .format = format,
2649 .segment_selector_size = segment_selector_size,
2650 .code_alignment_factor = code_alignment_factor,
2651 .data_alignment_factor = data_alignment_factor,
2652 .return_address_register = return_address_register,
2653 .aug_str = aug_str,
2654 .aug_data = aug_data,
2655 .lsda_pointer_enc = lsda_pointer_enc,
2656 .personality_enc = personality_enc,
2657 .personality_routine_pointer = personality_routine_pointer,
2658 .fde_pointer_enc = fde_pointer_enc,
2659 .initial_instructions = initial_instructions,
2660 };
2661 }
2662};
2663
2664pub const FrameDescriptionEntry = struct {
2665 // Offset into eh_frame where the CIE for this FDE is stored
2666 cie_length_offset: u64,
2667
2668 pc_begin: u64,
2669 pc_range: u64,
2670 lsda_pointer: ?u64,
2671 aug_data: []const u8,
2672 instructions: []const u8,
2673
2674 /// This function expects to read the FDE starting at the PC Begin field.
2675 /// The returned struct references memory backed by `fde_bytes`.
2676 ///
2677 /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values
2678 /// used when decoding pointers. This should be set to zero if fde_bytes is
2679 /// backed by the memory of a .eh_frame / .debug_frame section in the running executable.
2680 /// Otherwise, it should be the relative offset to translate addresses from
2681 /// where the section is currently stored in memory, to where it *would* be
2682 /// stored at runtime: section base addr - backing data base ptr.
2683 ///
2684 /// Similarly, `is_runtime` specifies this function is being called on a runtime
2685 /// section, and so indirect pointers can be followed.
2686 pub fn parse(
2687 fde_bytes: []const u8,
2688 pc_rel_offset: i64,
2689 is_runtime: bool,
2690 cie: CommonInformationEntry,
2691 addr_size_bytes: u8,
2692 endian: std.builtin.Endian,
2693 ) !FrameDescriptionEntry {
2694 if (addr_size_bytes > 8) return error.InvalidAddrSize;
2695
2696 var fbr: FixedBufferReader = .{ .buf = fde_bytes, .endian = endian };
2697
2698 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
2699 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),
2700 .follow_indirect = is_runtime,
2701 }) orelse return badDwarf();
2702
2703 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
2704 .pc_rel_base = 0,
2705 .follow_indirect = false,
2706 }) orelse return badDwarf();
2707
2708 var aug_data: []const u8 = &[_]u8{};
2709 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
2710 const aug_data_len = try fbr.readUleb128(usize);
2711 const aug_data_start = fbr.pos;
2712 aug_data = fde_bytes[aug_data_start..][0..aug_data_len];
2713
2714 const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit)
2715 try readEhPointer(&fbr, cie.lsda_pointer_enc, addr_size_bytes, .{
2716 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),
2717 .follow_indirect = is_runtime,
2718 })
2719 else
2720 null;
2721
2722 fbr.pos = aug_data_start + aug_data_len;
2723 break :blk lsda_pointer;
2724 } else null;
2725
2726 const instructions = fde_bytes[fbr.pos..];
2727 return .{
2728 .cie_length_offset = cie.length_offset,
2729 .pc_begin = pc_begin,
2730 .pc_range = pc_range,
2731 .lsda_pointer = lsda_pointer,
2732 .aug_data = aug_data,
2733 .instructions = instructions,
2734 };
2735 }
2736};
2737
2738fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
2739 if (pc_rel_offset < 0) {
2740 return math.sub(usize, field_ptr, @as(usize, @intCast(-pc_rel_offset)));
2741 } else {
2742 return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
2743 }
2744}
2745
2746// Reading debug info needs to be fast, even when compiled in debug mode,
2747// so avoid using a `std.io.FixedBufferStream` which is too slow.
2748pub const FixedBufferReader = struct {
2749 buf: []const u8,
2750 pos: usize = 0,
2751 endian: std.builtin.Endian,
2752
2753 pub const Error = error{ EndOfBuffer, Overflow, InvalidBuffer };
2754
2755 fn seekTo(fbr: *FixedBufferReader, pos: u64) Error!void {
2756 if (pos > fbr.buf.len) return error.EndOfBuffer;
2757 fbr.pos = @intCast(pos);
2758 }
2759
2760 fn seekForward(fbr: *FixedBufferReader, amount: u64) Error!void {
2761 if (fbr.buf.len - fbr.pos < amount) return error.EndOfBuffer;
2762 fbr.pos += @intCast(amount);
2763 }
2764
2765 pub inline fn readByte(fbr: *FixedBufferReader) Error!u8 {
2766 if (fbr.pos >= fbr.buf.len) return error.EndOfBuffer;
2767 defer fbr.pos += 1;
2768 return fbr.buf[fbr.pos];
2769 }
2770
2771 fn readByteSigned(fbr: *FixedBufferReader) Error!i8 {
2772 return @bitCast(try fbr.readByte());
2773 }
2774
2775 fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T {
2776 const size = @divExact(@typeInfo(T).Int.bits, 8);
2777 if (fbr.buf.len - fbr.pos < size) return error.EndOfBuffer;
2778 defer fbr.pos += size;
2779 return mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian);
2780 }
2781
2782 fn readIntChecked(
2783 fbr: *FixedBufferReader,
2784 comptime T: type,
2785 ma: *debug.StackIterator.MemoryAccessor,
2786 ) Error!T {
2787 if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null)
2788 return error.InvalidBuffer;
2789
2790 return readInt(fbr, T);
2791 }
2792
2793 fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
2794 return std.leb.readUleb128(T, fbr);
2795 }
2796
2797 fn readIleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
2798 return std.leb.readIleb128(T, fbr);
2799 }
2800
2801 fn readAddress(fbr: *FixedBufferReader, format: Format) Error!u64 {
2802 return switch (format) {
2803 .@"32" => try fbr.readInt(u32),
2804 .@"64" => try fbr.readInt(u64),
2805 };
2806 }
2807
2808 fn readAddressChecked(
2809 fbr: *FixedBufferReader,
2810 format: Format,
2811 ma: *debug.StackIterator.MemoryAccessor,
2812 ) Error!u64 {
2813 return switch (format) {
2814 .@"32" => try fbr.readIntChecked(u32, ma),
2815 .@"64" => try fbr.readIntChecked(u64, ma),
2816 };
2817 }
2818
2819 fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 {
2820 if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer;
2821 defer fbr.pos += len;
2822 return fbr.buf[fbr.pos..][0..len];
2823 }
2824
2825 fn readBytesTo(fbr: *FixedBufferReader, comptime sentinel: u8) Error![:sentinel]const u8 {
2826 const end = @call(.always_inline, mem.indexOfScalarPos, .{
2827 u8,
2828 fbr.buf,
2829 fbr.pos,
2830 sentinel,
2831 }) orelse return error.EndOfBuffer;
2832 defer fbr.pos = end + 1;
2833 return fbr.buf[fbr.pos..end :sentinel];
2834 }
2835};
2836
2837test {
2838 std.testing.refAllDecls(@This());
2839}
lib/std/dwarf/abi.zig deleted-410
......@@ -1,410 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const native_os = builtin.os.tag;
5const posix = std.posix;
6
7pub fn supportsUnwinding(target: std.Target) bool {
8 return switch (target.cpu.arch) {
9 .x86 => switch (target.os.tag) {
10 .linux, .netbsd, .solaris, .illumos => true,
11 else => false,
12 },
13 .x86_64 => switch (target.os.tag) {
14 .linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true,
15 else => false,
16 },
17 .arm => switch (target.os.tag) {
18 .linux => true,
19 else => false,
20 },
21 .aarch64 => switch (target.os.tag) {
22 .linux, .netbsd, .freebsd, .macos, .ios => true,
23 else => false,
24 },
25 else => false,
26 };
27}
28
29pub fn ipRegNum() u8 {
30 return switch (builtin.cpu.arch) {
31 .x86 => 8,
32 .x86_64 => 16,
33 .arm => 15,
34 .aarch64 => 32,
35 else => unreachable,
36 };
37}
38
39pub fn fpRegNum(reg_context: RegisterContext) u8 {
40 return switch (builtin.cpu.arch) {
41 // GCC on OS X historically did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
42 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
43 .x86_64 => 6,
44 .arm => 11,
45 .aarch64 => 29,
46 else => unreachable,
47 };
48}
49
50pub fn spRegNum(reg_context: RegisterContext) u8 {
51 return switch (builtin.cpu.arch) {
52 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
53 .x86_64 => 7,
54 .arm => 13,
55 .aarch64 => 31,
56 else => unreachable,
57 };
58}
59
60/// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature.
61/// This function clears these signature bits to make the pointer usable.
62pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
63 if (builtin.cpu.arch == .aarch64) {
64 // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it)
65 // The save / restore is because `xpaclri` operates on x30 (LR)
66 return asm (
67 \\mov x16, x30
68 \\mov x30, x15
69 \\hint 0x07
70 \\mov x15, x30
71 \\mov x30, x16
72 : [ret] "={x15}" (-> usize),
73 : [ptr] "{x15}" (ptr),
74 : "x16"
75 );
76 }
77
78 return ptr;
79}
80
81pub const RegisterContext = struct {
82 eh_frame: bool,
83 is_macho: bool,
84};
85
86pub const AbiError = error{
87 InvalidRegister,
88 UnimplementedArch,
89 UnimplementedOs,
90 RegisterContextRequired,
91 ThreadContextNotSupported,
92};
93
94fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
95 const reg_bytes_type = comptime RegBytesReturnType(ContextPtrType);
96 const info = @typeInfo(reg_bytes_type).Pointer;
97 return @Type(.{
98 .Pointer = .{
99 .size = .One,
100 .is_const = info.is_const,
101 .is_volatile = info.is_volatile,
102 .is_allowzero = info.is_allowzero,
103 .alignment = info.alignment,
104 .address_space = info.address_space,
105 .child = T,
106 .sentinel = null,
107 },
108 });
109}
110
111/// Returns a pointer to a register stored in a ThreadContext, preserving the pointer attributes of the context.
112pub fn regValueNative(
113 comptime T: type,
114 thread_context_ptr: anytype,
115 reg_number: u8,
116 reg_context: ?RegisterContext,
117) !RegValueReturnType(@TypeOf(thread_context_ptr), T) {
118 const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
119 if (@sizeOf(T) != reg_bytes.len) return error.IncompatibleRegisterSize;
120 return mem.bytesAsValue(T, reg_bytes[0..@sizeOf(T)]);
121}
122
123fn RegBytesReturnType(comptime ContextPtrType: type) type {
124 const info = @typeInfo(ContextPtrType);
125 if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
126 @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
127 }
128
129 return if (info.Pointer.is_const) return []const u8 else []u8;
130}
131
132/// Returns a slice containing the backing storage for `reg_number`.
133///
134/// `reg_context` describes in what context the register number is used, as it can have different
135/// meanings depending on the DWARF container. It is only required when getting the stack or
136/// frame pointer register on some architectures.
137pub fn regBytes(
138 thread_context_ptr: anytype,
139 reg_number: u8,
140 reg_context: ?RegisterContext,
141) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
142 if (native_os == .windows) {
143 return switch (builtin.cpu.arch) {
144 .x86 => switch (reg_number) {
145 0 => mem.asBytes(&thread_context_ptr.Eax),
146 1 => mem.asBytes(&thread_context_ptr.Ecx),
147 2 => mem.asBytes(&thread_context_ptr.Edx),
148 3 => mem.asBytes(&thread_context_ptr.Ebx),
149 4 => mem.asBytes(&thread_context_ptr.Esp),
150 5 => mem.asBytes(&thread_context_ptr.Ebp),
151 6 => mem.asBytes(&thread_context_ptr.Esi),
152 7 => mem.asBytes(&thread_context_ptr.Edi),
153 8 => mem.asBytes(&thread_context_ptr.Eip),
154 9 => mem.asBytes(&thread_context_ptr.EFlags),
155 10 => mem.asBytes(&thread_context_ptr.SegCs),
156 11 => mem.asBytes(&thread_context_ptr.SegSs),
157 12 => mem.asBytes(&thread_context_ptr.SegDs),
158 13 => mem.asBytes(&thread_context_ptr.SegEs),
159 14 => mem.asBytes(&thread_context_ptr.SegFs),
160 15 => mem.asBytes(&thread_context_ptr.SegGs),
161 else => error.InvalidRegister,
162 },
163 .x86_64 => switch (reg_number) {
164 0 => mem.asBytes(&thread_context_ptr.Rax),
165 1 => mem.asBytes(&thread_context_ptr.Rdx),
166 2 => mem.asBytes(&thread_context_ptr.Rcx),
167 3 => mem.asBytes(&thread_context_ptr.Rbx),
168 4 => mem.asBytes(&thread_context_ptr.Rsi),
169 5 => mem.asBytes(&thread_context_ptr.Rdi),
170 6 => mem.asBytes(&thread_context_ptr.Rbp),
171 7 => mem.asBytes(&thread_context_ptr.Rsp),
172 8 => mem.asBytes(&thread_context_ptr.R8),
173 9 => mem.asBytes(&thread_context_ptr.R9),
174 10 => mem.asBytes(&thread_context_ptr.R10),
175 11 => mem.asBytes(&thread_context_ptr.R11),
176 12 => mem.asBytes(&thread_context_ptr.R12),
177 13 => mem.asBytes(&thread_context_ptr.R13),
178 14 => mem.asBytes(&thread_context_ptr.R14),
179 15 => mem.asBytes(&thread_context_ptr.R15),
180 16 => mem.asBytes(&thread_context_ptr.Rip),
181 else => error.InvalidRegister,
182 },
183 .aarch64 => switch (reg_number) {
184 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
185 31 => mem.asBytes(&thread_context_ptr.Sp),
186 32 => mem.asBytes(&thread_context_ptr.Pc),
187 else => error.InvalidRegister,
188 },
189 else => error.UnimplementedArch,
190 };
191 }
192
193 if (!std.debug.have_ucontext) return error.ThreadContextNotSupported;
194
195 const ucontext_ptr = thread_context_ptr;
196 return switch (builtin.cpu.arch) {
197 .x86 => switch (native_os) {
198 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
199 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
200 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
201 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]),
202 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]),
203 4...5 => if (reg_context) |r| bytes: {
204 if (reg_number == 4) {
205 break :bytes if (r.eh_frame and r.is_macho)
206 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP])
207 else
208 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]);
209 } else {
210 break :bytes if (r.eh_frame and r.is_macho)
211 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP])
212 else
213 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]);
214 }
215 } else error.RegisterContextRequired,
216 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]),
217 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]),
218 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]),
219 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]),
220 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]),
221 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]),
222 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]),
223 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]),
224 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]),
225 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]),
226 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
227 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
228 else => error.InvalidRegister,
229 },
230 else => error.UnimplementedOs,
231 },
232 .x86_64 => switch (native_os) {
233 .linux, .solaris, .illumos => switch (reg_number) {
234 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
235 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
236 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]),
237 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]),
238 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]),
239 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]),
240 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]),
241 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]),
242 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]),
243 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]),
244 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]),
245 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]),
246 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]),
247 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]),
248 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
249 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
250 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
251 17...32 => |i| if (native_os.isSolarish())
252 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
253 else
254 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
255 else => error.InvalidRegister,
256 },
257 .freebsd => switch (reg_number) {
258 0 => mem.asBytes(&ucontext_ptr.mcontext.rax),
259 1 => mem.asBytes(&ucontext_ptr.mcontext.rdx),
260 2 => mem.asBytes(&ucontext_ptr.mcontext.rcx),
261 3 => mem.asBytes(&ucontext_ptr.mcontext.rbx),
262 4 => mem.asBytes(&ucontext_ptr.mcontext.rsi),
263 5 => mem.asBytes(&ucontext_ptr.mcontext.rdi),
264 6 => mem.asBytes(&ucontext_ptr.mcontext.rbp),
265 7 => mem.asBytes(&ucontext_ptr.mcontext.rsp),
266 8 => mem.asBytes(&ucontext_ptr.mcontext.r8),
267 9 => mem.asBytes(&ucontext_ptr.mcontext.r9),
268 10 => mem.asBytes(&ucontext_ptr.mcontext.r10),
269 11 => mem.asBytes(&ucontext_ptr.mcontext.r11),
270 12 => mem.asBytes(&ucontext_ptr.mcontext.r12),
271 13 => mem.asBytes(&ucontext_ptr.mcontext.r13),
272 14 => mem.asBytes(&ucontext_ptr.mcontext.r14),
273 15 => mem.asBytes(&ucontext_ptr.mcontext.r15),
274 16 => mem.asBytes(&ucontext_ptr.mcontext.rip),
275 // TODO: Extract xmm state from mcontext.fpstate?
276 else => error.InvalidRegister,
277 },
278 .openbsd => switch (reg_number) {
279 0 => mem.asBytes(&ucontext_ptr.sc_rax),
280 1 => mem.asBytes(&ucontext_ptr.sc_rdx),
281 2 => mem.asBytes(&ucontext_ptr.sc_rcx),
282 3 => mem.asBytes(&ucontext_ptr.sc_rbx),
283 4 => mem.asBytes(&ucontext_ptr.sc_rsi),
284 5 => mem.asBytes(&ucontext_ptr.sc_rdi),
285 6 => mem.asBytes(&ucontext_ptr.sc_rbp),
286 7 => mem.asBytes(&ucontext_ptr.sc_rsp),
287 8 => mem.asBytes(&ucontext_ptr.sc_r8),
288 9 => mem.asBytes(&ucontext_ptr.sc_r9),
289 10 => mem.asBytes(&ucontext_ptr.sc_r10),
290 11 => mem.asBytes(&ucontext_ptr.sc_r11),
291 12 => mem.asBytes(&ucontext_ptr.sc_r12),
292 13 => mem.asBytes(&ucontext_ptr.sc_r13),
293 14 => mem.asBytes(&ucontext_ptr.sc_r14),
294 15 => mem.asBytes(&ucontext_ptr.sc_r15),
295 16 => mem.asBytes(&ucontext_ptr.sc_rip),
296 // TODO: Extract xmm state from sc_fpstate?
297 else => error.InvalidRegister,
298 },
299 .macos, .ios => switch (reg_number) {
300 0 => mem.asBytes(&ucontext_ptr.mcontext.ss.rax),
301 1 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdx),
302 2 => mem.asBytes(&ucontext_ptr.mcontext.ss.rcx),
303 3 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbx),
304 4 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsi),
305 5 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdi),
306 6 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbp),
307 7 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsp),
308 8 => mem.asBytes(&ucontext_ptr.mcontext.ss.r8),
309 9 => mem.asBytes(&ucontext_ptr.mcontext.ss.r9),
310 10 => mem.asBytes(&ucontext_ptr.mcontext.ss.r10),
311 11 => mem.asBytes(&ucontext_ptr.mcontext.ss.r11),
312 12 => mem.asBytes(&ucontext_ptr.mcontext.ss.r12),
313 13 => mem.asBytes(&ucontext_ptr.mcontext.ss.r13),
314 14 => mem.asBytes(&ucontext_ptr.mcontext.ss.r14),
315 15 => mem.asBytes(&ucontext_ptr.mcontext.ss.r15),
316 16 => mem.asBytes(&ucontext_ptr.mcontext.ss.rip),
317 else => error.InvalidRegister,
318 },
319 else => error.UnimplementedOs,
320 },
321 .arm => switch (native_os) {
322 .linux => switch (reg_number) {
323 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
324 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
325 2 => mem.asBytes(&ucontext_ptr.mcontext.arm_r2),
326 3 => mem.asBytes(&ucontext_ptr.mcontext.arm_r3),
327 4 => mem.asBytes(&ucontext_ptr.mcontext.arm_r4),
328 5 => mem.asBytes(&ucontext_ptr.mcontext.arm_r5),
329 6 => mem.asBytes(&ucontext_ptr.mcontext.arm_r6),
330 7 => mem.asBytes(&ucontext_ptr.mcontext.arm_r7),
331 8 => mem.asBytes(&ucontext_ptr.mcontext.arm_r8),
332 9 => mem.asBytes(&ucontext_ptr.mcontext.arm_r9),
333 10 => mem.asBytes(&ucontext_ptr.mcontext.arm_r10),
334 11 => mem.asBytes(&ucontext_ptr.mcontext.arm_fp),
335 12 => mem.asBytes(&ucontext_ptr.mcontext.arm_ip),
336 13 => mem.asBytes(&ucontext_ptr.mcontext.arm_sp),
337 14 => mem.asBytes(&ucontext_ptr.mcontext.arm_lr),
338 15 => mem.asBytes(&ucontext_ptr.mcontext.arm_pc),
339 // CPSR is not allocated a register number (See: https://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst, Section 4.1)
340 else => error.InvalidRegister,
341 },
342 else => error.UnimplementedOs,
343 },
344 .aarch64 => switch (native_os) {
345 .macos, .ios => switch (reg_number) {
346 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
347 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
348 30 => mem.asBytes(&ucontext_ptr.mcontext.ss.lr),
349 31 => mem.asBytes(&ucontext_ptr.mcontext.ss.sp),
350 32 => mem.asBytes(&ucontext_ptr.mcontext.ss.pc),
351
352 // TODO: Find storage for this state
353 //34 => mem.asBytes(&ucontext_ptr.ra_sign_state),
354
355 // V0-V31
356 64...95 => mem.asBytes(&ucontext_ptr.mcontext.ns.q[reg_number - 64]),
357 else => error.InvalidRegister,
358 },
359 .netbsd => switch (reg_number) {
360 0...34 => mem.asBytes(&ucontext_ptr.mcontext.gregs[reg_number]),
361 else => error.InvalidRegister,
362 },
363 .freebsd => switch (reg_number) {
364 0...29 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.x[reg_number]),
365 30 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.lr),
366 31 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.sp),
367
368 // TODO: This seems wrong, but it was in the previous debug.zig code for mapping PC, check this
369 32 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.elr),
370
371 else => error.InvalidRegister,
372 },
373 .openbsd => switch (reg_number) {
374 0...30 => mem.asBytes(&ucontext_ptr.sc_x[reg_number]),
375 31 => mem.asBytes(&ucontext_ptr.sc_sp),
376 32 => mem.asBytes(&ucontext_ptr.sc_lr),
377 33 => mem.asBytes(&ucontext_ptr.sc_elr),
378 34 => mem.asBytes(&ucontext_ptr.sc_spsr),
379 else => error.InvalidRegister,
380 },
381 else => switch (reg_number) {
382 0...30 => mem.asBytes(&ucontext_ptr.mcontext.regs[reg_number]),
383 31 => mem.asBytes(&ucontext_ptr.mcontext.sp),
384 32 => mem.asBytes(&ucontext_ptr.mcontext.pc),
385 else => error.InvalidRegister,
386 },
387 },
388 else => error.UnimplementedArch,
389 };
390}
391
392/// Returns the ABI-defined default value this register has in the unwinding table
393/// before running any of the CIE instructions. The DWARF spec defines these as having
394/// the .undefined rule by default, but allows ABI authors to override that.
395pub fn getRegDefaultValue(reg_number: u8, context: *std.dwarf.UnwindContext, out: []u8) !void {
396 switch (builtin.cpu.arch) {
397 .aarch64 => {
398 // Callee-saved registers are initialized as if they had the .same_value rule
399 if (reg_number >= 19 and reg_number <= 28) {
400 const src = try regBytes(context.thread_context, reg_number, context.reg_context);
401 if (src.len != out.len) return error.RegisterSizeMismatch;
402 @memcpy(out, src);
403 return;
404 }
405 },
406 else => {},
407 }
408
409 @memset(out, undefined);
410}
lib/std/dwarf/call_frame.zig deleted-687
......@@ -1,687 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const debug = std.debug;
5const leb = std.leb;
6const dwarf = std.dwarf;
7const abi = dwarf.abi;
8const expressions = dwarf.expressions;
9const assert = std.debug.assert;
10const native_endian = builtin.cpu.arch.endian();
11
12const Opcode = enum(u8) {
13 advance_loc = 0x1 << 6,
14 offset = 0x2 << 6,
15 restore = 0x3 << 6,
16
17 nop = 0x00,
18 set_loc = 0x01,
19 advance_loc1 = 0x02,
20 advance_loc2 = 0x03,
21 advance_loc4 = 0x04,
22 offset_extended = 0x05,
23 restore_extended = 0x06,
24 undefined = 0x07,
25 same_value = 0x08,
26 register = 0x09,
27 remember_state = 0x0a,
28 restore_state = 0x0b,
29 def_cfa = 0x0c,
30 def_cfa_register = 0x0d,
31 def_cfa_offset = 0x0e,
32 def_cfa_expression = 0x0f,
33 expression = 0x10,
34 offset_extended_sf = 0x11,
35 def_cfa_sf = 0x12,
36 def_cfa_offset_sf = 0x13,
37 val_offset = 0x14,
38 val_offset_sf = 0x15,
39 val_expression = 0x16,
40
41 // These opcodes encode an operand in the lower 6 bits of the opcode itself
42 pub const lo_inline = @intFromEnum(Opcode.advance_loc);
43 pub const hi_inline = @intFromEnum(Opcode.restore) | 0b111111;
44
45 // These opcodes are trailed by zero or more operands
46 pub const lo_reserved = @intFromEnum(Opcode.nop);
47 pub const hi_reserved = @intFromEnum(Opcode.val_expression);
48
49 // Vendor-specific opcodes
50 pub const lo_user = 0x1c;
51 pub const hi_user = 0x3f;
52};
53
54fn readBlock(stream: *std.io.FixedBufferStream([]const u8)) ![]const u8 {
55 const reader = stream.reader();
56 const block_len = try leb.readUleb128(usize, reader);
57 if (stream.pos + block_len > stream.buffer.len) return error.InvalidOperand;
58
59 const block = stream.buffer[stream.pos..][0..block_len];
60 reader.context.pos += block_len;
61
62 return block;
63}
64
65pub const Instruction = union(Opcode) {
66 advance_loc: struct {
67 delta: u8,
68 },
69 offset: struct {
70 register: u8,
71 offset: u64,
72 },
73 restore: struct {
74 register: u8,
75 },
76 nop: void,
77 set_loc: struct {
78 address: u64,
79 },
80 advance_loc1: struct {
81 delta: u8,
82 },
83 advance_loc2: struct {
84 delta: u16,
85 },
86 advance_loc4: struct {
87 delta: u32,
88 },
89 offset_extended: struct {
90 register: u8,
91 offset: u64,
92 },
93 restore_extended: struct {
94 register: u8,
95 },
96 undefined: struct {
97 register: u8,
98 },
99 same_value: struct {
100 register: u8,
101 },
102 register: struct {
103 register: u8,
104 target_register: u8,
105 },
106 remember_state: void,
107 restore_state: void,
108 def_cfa: struct {
109 register: u8,
110 offset: u64,
111 },
112 def_cfa_register: struct {
113 register: u8,
114 },
115 def_cfa_offset: struct {
116 offset: u64,
117 },
118 def_cfa_expression: struct {
119 block: []const u8,
120 },
121 expression: struct {
122 register: u8,
123 block: []const u8,
124 },
125 offset_extended_sf: struct {
126 register: u8,
127 offset: i64,
128 },
129 def_cfa_sf: struct {
130 register: u8,
131 offset: i64,
132 },
133 def_cfa_offset_sf: struct {
134 offset: i64,
135 },
136 val_offset: struct {
137 register: u8,
138 offset: u64,
139 },
140 val_offset_sf: struct {
141 register: u8,
142 offset: i64,
143 },
144 val_expression: struct {
145 register: u8,
146 block: []const u8,
147 },
148
149 pub fn read(
150 stream: *std.io.FixedBufferStream([]const u8),
151 addr_size_bytes: u8,
152 endian: std.builtin.Endian,
153 ) !Instruction {
154 const reader = stream.reader();
155 switch (try reader.readByte()) {
156 Opcode.lo_inline...Opcode.hi_inline => |opcode| {
157 const e: Opcode = @enumFromInt(opcode & 0b11000000);
158 const value: u6 = @intCast(opcode & 0b111111);
159 return switch (e) {
160 .advance_loc => .{
161 .advance_loc = .{ .delta = value },
162 },
163 .offset => .{
164 .offset = .{
165 .register = value,
166 .offset = try leb.readUleb128(u64, reader),
167 },
168 },
169 .restore => .{
170 .restore = .{ .register = value },
171 },
172 else => unreachable,
173 };
174 },
175 Opcode.lo_reserved...Opcode.hi_reserved => |opcode| {
176 const e: Opcode = @enumFromInt(opcode);
177 return switch (e) {
178 .advance_loc,
179 .offset,
180 .restore,
181 => unreachable,
182 .nop => .{ .nop = {} },
183 .set_loc => .{
184 .set_loc = .{
185 .address = switch (addr_size_bytes) {
186 2 => try reader.readInt(u16, endian),
187 4 => try reader.readInt(u32, endian),
188 8 => try reader.readInt(u64, endian),
189 else => return error.InvalidAddrSize,
190 },
191 },
192 },
193 .advance_loc1 => .{
194 .advance_loc1 = .{ .delta = try reader.readByte() },
195 },
196 .advance_loc2 => .{
197 .advance_loc2 = .{ .delta = try reader.readInt(u16, endian) },
198 },
199 .advance_loc4 => .{
200 .advance_loc4 = .{ .delta = try reader.readInt(u32, endian) },
201 },
202 .offset_extended => .{
203 .offset_extended = .{
204 .register = try leb.readUleb128(u8, reader),
205 .offset = try leb.readUleb128(u64, reader),
206 },
207 },
208 .restore_extended => .{
209 .restore_extended = .{
210 .register = try leb.readUleb128(u8, reader),
211 },
212 },
213 .undefined => .{
214 .undefined = .{
215 .register = try leb.readUleb128(u8, reader),
216 },
217 },
218 .same_value => .{
219 .same_value = .{
220 .register = try leb.readUleb128(u8, reader),
221 },
222 },
223 .register => .{
224 .register = .{
225 .register = try leb.readUleb128(u8, reader),
226 .target_register = try leb.readUleb128(u8, reader),
227 },
228 },
229 .remember_state => .{ .remember_state = {} },
230 .restore_state => .{ .restore_state = {} },
231 .def_cfa => .{
232 .def_cfa = .{
233 .register = try leb.readUleb128(u8, reader),
234 .offset = try leb.readUleb128(u64, reader),
235 },
236 },
237 .def_cfa_register => .{
238 .def_cfa_register = .{
239 .register = try leb.readUleb128(u8, reader),
240 },
241 },
242 .def_cfa_offset => .{
243 .def_cfa_offset = .{
244 .offset = try leb.readUleb128(u64, reader),
245 },
246 },
247 .def_cfa_expression => .{
248 .def_cfa_expression = .{
249 .block = try readBlock(stream),
250 },
251 },
252 .expression => .{
253 .expression = .{
254 .register = try leb.readUleb128(u8, reader),
255 .block = try readBlock(stream),
256 },
257 },
258 .offset_extended_sf => .{
259 .offset_extended_sf = .{
260 .register = try leb.readUleb128(u8, reader),
261 .offset = try leb.readIleb128(i64, reader),
262 },
263 },
264 .def_cfa_sf => .{
265 .def_cfa_sf = .{
266 .register = try leb.readUleb128(u8, reader),
267 .offset = try leb.readIleb128(i64, reader),
268 },
269 },
270 .def_cfa_offset_sf => .{
271 .def_cfa_offset_sf = .{
272 .offset = try leb.readIleb128(i64, reader),
273 },
274 },
275 .val_offset => .{
276 .val_offset = .{
277 .register = try leb.readUleb128(u8, reader),
278 .offset = try leb.readUleb128(u64, reader),
279 },
280 },
281 .val_offset_sf => .{
282 .val_offset_sf = .{
283 .register = try leb.readUleb128(u8, reader),
284 .offset = try leb.readIleb128(i64, reader),
285 },
286 },
287 .val_expression => .{
288 .val_expression = .{
289 .register = try leb.readUleb128(u8, reader),
290 .block = try readBlock(stream),
291 },
292 },
293 };
294 },
295 Opcode.lo_user...Opcode.hi_user => return error.UnimplementedUserOpcode,
296 else => return error.InvalidOpcode,
297 }
298 }
299};
300
301/// Since register rules are applied (usually) during a panic,
302/// checked addition / subtraction is used so that we can return
303/// an error and fall back to FP-based unwinding.
304pub fn applyOffset(base: usize, offset: i64) !usize {
305 return if (offset >= 0)
306 try std.math.add(usize, base, @as(usize, @intCast(offset)))
307 else
308 try std.math.sub(usize, base, @as(usize, @intCast(-offset)));
309}
310
311/// This is a virtual machine that runs DWARF call frame instructions.
312pub const VirtualMachine = struct {
313 /// See section 6.4.1 of the DWARF5 specification for details on each
314 const RegisterRule = union(enum) {
315 // The spec says that the default rule for each column is the undefined rule.
316 // However, it also allows ABI / compiler authors to specify alternate defaults, so
317 // there is a distinction made here.
318 default: void,
319
320 undefined: void,
321 same_value: void,
322
323 // offset(N)
324 offset: i64,
325
326 // val_offset(N)
327 val_offset: i64,
328
329 // register(R)
330 register: u8,
331
332 // expression(E)
333 expression: []const u8,
334
335 // val_expression(E)
336 val_expression: []const u8,
337
338 // Augmenter-defined rule
339 architectural: void,
340 };
341
342 /// Each row contains unwinding rules for a set of registers.
343 pub const Row = struct {
344 /// Offset from `FrameDescriptionEntry.pc_begin`
345 offset: u64 = 0,
346
347 /// Special-case column that defines the CFA (Canonical Frame Address) rule.
348 /// The register field of this column defines the register that CFA is derived from.
349 cfa: Column = .{},
350
351 /// The register fields in these columns define the register the rule applies to.
352 columns: ColumnRange = .{},
353
354 /// Indicates that the next write to any column in this row needs to copy
355 /// the backing column storage first, as it may be referenced by previous rows.
356 copy_on_write: bool = false,
357 };
358
359 pub const Column = struct {
360 register: ?u8 = null,
361 rule: RegisterRule = .{ .default = {} },
362
363 /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
364 pub fn resolveValue(
365 self: Column,
366 context: *dwarf.UnwindContext,
367 expression_context: dwarf.expressions.ExpressionContext,
368 ma: *debug.StackIterator.MemoryAccessor,
369 out: []u8,
370 ) !void {
371 switch (self.rule) {
372 .default => {
373 const register = self.register orelse return error.InvalidRegister;
374 try abi.getRegDefaultValue(register, context, out);
375 },
376 .undefined => {
377 @memset(out, undefined);
378 },
379 .same_value => {
380 // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
381 const register = self.register orelse return error.InvalidRegister;
382 const src = try abi.regBytes(context.thread_context, register, context.reg_context);
383 if (src.len != out.len) return error.RegisterSizeMismatch;
384 @memcpy(out, src);
385 },
386 .offset => |offset| {
387 if (context.cfa) |cfa| {
388 const addr = try applyOffset(cfa, offset);
389 if (ma.load(usize, addr) == null) return error.InvalidAddress;
390 const ptr: *const usize = @ptrFromInt(addr);
391 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
392 } else return error.InvalidCFA;
393 },
394 .val_offset => |offset| {
395 if (context.cfa) |cfa| {
396 mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
397 } else return error.InvalidCFA;
398 },
399 .register => |register| {
400 const src = try abi.regBytes(context.thread_context, register, context.reg_context);
401 if (src.len != out.len) return error.RegisterSizeMismatch;
402 @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context));
403 },
404 .expression => |expression| {
405 context.stack_machine.reset();
406 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
407 const addr = if (value) |v| blk: {
408 if (v != .generic) return error.InvalidExpressionValue;
409 break :blk v.generic;
410 } else return error.NoExpressionValue;
411
412 if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress;
413 const ptr: *usize = @ptrFromInt(addr);
414 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
415 },
416 .val_expression => |expression| {
417 context.stack_machine.reset();
418 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
419 if (value) |v| {
420 if (v != .generic) return error.InvalidExpressionValue;
421 mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian);
422 } else return error.NoExpressionValue;
423 },
424 .architectural => return error.UnimplementedRegisterRule,
425 }
426 }
427 };
428
429 const ColumnRange = struct {
430 /// Index into `columns` of the first column in this row.
431 start: usize = undefined,
432 len: u8 = 0,
433 };
434
435 columns: std.ArrayListUnmanaged(Column) = .{},
436 stack: std.ArrayListUnmanaged(ColumnRange) = .{},
437 current_row: Row = .{},
438
439 /// The result of executing the CIE's initial_instructions
440 cie_row: ?Row = null,
441
442 pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
443 self.stack.deinit(allocator);
444 self.columns.deinit(allocator);
445 self.* = undefined;
446 }
447
448 pub fn reset(self: *VirtualMachine) void {
449 self.stack.clearRetainingCapacity();
450 self.columns.clearRetainingCapacity();
451 self.current_row = .{};
452 self.cie_row = null;
453 }
454
455 /// Return a slice backed by the row's non-CFA columns
456 pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
457 if (row.columns.len == 0) return &.{};
458 return self.columns.items[row.columns.start..][0..row.columns.len];
459 }
460
461 /// Either retrieves or adds a column for `register` (non-CFA) in the current row.
462 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
463 for (self.rowColumns(self.current_row)) |*c| {
464 if (c.register == register) return c;
465 }
466
467 if (self.current_row.columns.len == 0) {
468 self.current_row.columns.start = self.columns.items.len;
469 }
470 self.current_row.columns.len += 1;
471
472 const column = try self.columns.addOne(allocator);
473 column.* = .{
474 .register = register,
475 };
476
477 return column;
478 }
479
480 /// Runs the CIE instructions, then the FDE instructions. Execution halts
481 /// once the row that corresponds to `pc` is known, and the row is returned.
482 pub fn runTo(
483 self: *VirtualMachine,
484 allocator: std.mem.Allocator,
485 pc: u64,
486 cie: dwarf.CommonInformationEntry,
487 fde: dwarf.FrameDescriptionEntry,
488 addr_size_bytes: u8,
489 endian: std.builtin.Endian,
490 ) !Row {
491 assert(self.cie_row == null);
492 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
493
494 var prev_row: Row = self.current_row;
495
496 var cie_stream = std.io.fixedBufferStream(cie.initial_instructions);
497 var fde_stream = std.io.fixedBufferStream(fde.instructions);
498 var streams = [_]*std.io.FixedBufferStream([]const u8){
499 &cie_stream,
500 &fde_stream,
501 };
502
503 for (&streams, 0..) |stream, i| {
504 while (stream.pos < stream.buffer.len) {
505 const instruction = try dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
506 prev_row = try self.step(allocator, cie, i == 0, instruction);
507 if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
508 }
509 }
510
511 return self.current_row;
512 }
513
514 pub fn runToNative(
515 self: *VirtualMachine,
516 allocator: std.mem.Allocator,
517 pc: u64,
518 cie: dwarf.CommonInformationEntry,
519 fde: dwarf.FrameDescriptionEntry,
520 ) !Row {
521 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());
522 }
523
524 fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {
525 if (!self.current_row.copy_on_write) return;
526
527 const new_start = self.columns.items.len;
528 if (self.current_row.columns.len > 0) {
529 try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);
530 self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
531 self.current_row.columns.start = new_start;
532 }
533 }
534
535 /// Executes a single instruction.
536 /// If this instruction is from the CIE, `is_initial` should be set.
537 /// Returns the value of `current_row` before executing this instruction.
538 pub fn step(
539 self: *VirtualMachine,
540 allocator: std.mem.Allocator,
541 cie: dwarf.CommonInformationEntry,
542 is_initial: bool,
543 instruction: Instruction,
544 ) !Row {
545 // CIE instructions must be run before FDE instructions
546 assert(!is_initial or self.cie_row == null);
547 if (!is_initial and self.cie_row == null) {
548 self.cie_row = self.current_row;
549 self.current_row.copy_on_write = true;
550 }
551
552 const prev_row = self.current_row;
553 switch (instruction) {
554 .set_loc => |i| {
555 if (i.address <= self.current_row.offset) return error.InvalidOperation;
556 // TODO: Check cie.segment_selector_size != 0 for DWARFV4
557 self.current_row.offset = i.address;
558 },
559 inline .advance_loc,
560 .advance_loc1,
561 .advance_loc2,
562 .advance_loc4,
563 => |i| {
564 self.current_row.offset += i.delta * cie.code_alignment_factor;
565 self.current_row.copy_on_write = true;
566 },
567 inline .offset,
568 .offset_extended,
569 .offset_extended_sf,
570 => |i| {
571 try self.resolveCopyOnWrite(allocator);
572 const column = try self.getOrAddColumn(allocator, i.register);
573 column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
574 },
575 inline .restore,
576 .restore_extended,
577 => |i| {
578 try self.resolveCopyOnWrite(allocator);
579 if (self.cie_row) |cie_row| {
580 const column = try self.getOrAddColumn(allocator, i.register);
581 column.rule = for (self.rowColumns(cie_row)) |cie_column| {
582 if (cie_column.register == i.register) break cie_column.rule;
583 } else .{ .default = {} };
584 } else return error.InvalidOperation;
585 },
586 .nop => {},
587 .undefined => |i| {
588 try self.resolveCopyOnWrite(allocator);
589 const column = try self.getOrAddColumn(allocator, i.register);
590 column.rule = .{ .undefined = {} };
591 },
592 .same_value => |i| {
593 try self.resolveCopyOnWrite(allocator);
594 const column = try self.getOrAddColumn(allocator, i.register);
595 column.rule = .{ .same_value = {} };
596 },
597 .register => |i| {
598 try self.resolveCopyOnWrite(allocator);
599 const column = try self.getOrAddColumn(allocator, i.register);
600 column.rule = .{ .register = i.target_register };
601 },
602 .remember_state => {
603 try self.stack.append(allocator, self.current_row.columns);
604 self.current_row.copy_on_write = true;
605 },
606 .restore_state => {
607 const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
608 self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
609 try self.columns.ensureUnusedCapacity(allocator, restored_columns.len);
610
611 self.current_row.columns.start = self.columns.items.len;
612 self.current_row.columns.len = restored_columns.len;
613 self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
614 },
615 .def_cfa => |i| {
616 try self.resolveCopyOnWrite(allocator);
617 self.current_row.cfa = .{
618 .register = i.register,
619 .rule = .{ .val_offset = @intCast(i.offset) },
620 };
621 },
622 .def_cfa_sf => |i| {
623 try self.resolveCopyOnWrite(allocator);
624 self.current_row.cfa = .{
625 .register = i.register,
626 .rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
627 };
628 },
629 .def_cfa_register => |i| {
630 try self.resolveCopyOnWrite(allocator);
631 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
632 self.current_row.cfa.register = i.register;
633 },
634 .def_cfa_offset => |i| {
635 try self.resolveCopyOnWrite(allocator);
636 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
637 self.current_row.cfa.rule = .{
638 .val_offset = @intCast(i.offset),
639 };
640 },
641 .def_cfa_offset_sf => |i| {
642 try self.resolveCopyOnWrite(allocator);
643 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
644 self.current_row.cfa.rule = .{
645 .val_offset = i.offset * cie.data_alignment_factor,
646 };
647 },
648 .def_cfa_expression => |i| {
649 try self.resolveCopyOnWrite(allocator);
650 self.current_row.cfa.register = undefined;
651 self.current_row.cfa.rule = .{
652 .expression = i.block,
653 };
654 },
655 .expression => |i| {
656 try self.resolveCopyOnWrite(allocator);
657 const column = try self.getOrAddColumn(allocator, i.register);
658 column.rule = .{
659 .expression = i.block,
660 };
661 },
662 .val_offset => |i| {
663 try self.resolveCopyOnWrite(allocator);
664 const column = try self.getOrAddColumn(allocator, i.register);
665 column.rule = .{
666 .val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
667 };
668 },
669 .val_offset_sf => |i| {
670 try self.resolveCopyOnWrite(allocator);
671 const column = try self.getOrAddColumn(allocator, i.register);
672 column.rule = .{
673 .val_offset = i.offset * cie.data_alignment_factor,
674 };
675 },
676 .val_expression => |i| {
677 try self.resolveCopyOnWrite(allocator);
678 const column = try self.getOrAddColumn(allocator, i.register);
679 column.rule = .{
680 .val_expression = i.block,
681 };
682 },
683 }
684
685 return prev_row;
686 }
687};
lib/std/dwarf/expressions.zig deleted-1648
......@@ -1,1648 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const OP = @import("OP.zig");
4const leb = std.leb;
5const dwarf = std.dwarf;
6const abi = dwarf.abi;
7const mem = std.mem;
8const assert = std.debug.assert;
9const native_endian = builtin.cpu.arch.endian();
10
11/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
12/// Callers should specify all the fields relevant to their context. If a field is required
13/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
14pub const ExpressionContext = struct {
15 /// The dwarf format of the section this expression is in
16 format: dwarf.Format = .@"32",
17
18 /// If specified, any addresses will pass through before being accessed
19 memory_accessor: ?*std.debug.StackIterator.MemoryAccessor = null,
20
21 /// The compilation unit this expression relates to, if any
22 compile_unit: ?*const dwarf.CompileUnit = null,
23
24 /// When evaluating a user-presented expression, this is the address of the object being evaluated
25 object_address: ?*const anyopaque = null,
26
27 /// .debug_addr section
28 debug_addr: ?[]const u8 = null,
29
30 /// Thread context
31 thread_context: ?*std.debug.ThreadContext = null,
32 reg_context: ?abi.RegisterContext = null,
33
34 /// Call frame address, if in a CFI context
35 cfa: ?usize = null,
36
37 /// This expression is a sub-expression from an OP.entry_value instruction
38 entry_value_context: bool = false,
39};
40
41pub const ExpressionOptions = struct {
42 /// The address size of the target architecture
43 addr_size: u8 = @sizeOf(usize),
44
45 /// Endianness of the target architecture
46 endian: std.builtin.Endian = builtin.target.cpu.arch.endian(),
47
48 /// Restrict the stack machine to a subset of opcodes used in call frame instructions
49 call_frame_context: bool = false,
50};
51
52// Explicitly defined to support executing sub-expressions
53pub const ExpressionError = error{
54 UnimplementedExpressionCall,
55 UnimplementedOpcode,
56 UnimplementedUserOpcode,
57 UnimplementedTypedComparison,
58 UnimplementedTypeConversion,
59
60 UnknownExpressionOpcode,
61
62 IncompleteExpressionContext,
63
64 InvalidCFAOpcode,
65 InvalidExpression,
66 InvalidFrameBase,
67 InvalidIntegralTypeSize,
68 InvalidRegister,
69 InvalidSubExpression,
70 InvalidTypeLength,
71
72 TruncatedIntegralType,
73} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
74
75/// A stack machine that can decode and run DWARF expressions.
76/// Expressions can be decoded for non-native address size and endianness,
77/// but can only be executed if the current target matches the configuration.
78pub fn StackMachine(comptime options: ExpressionOptions) type {
79 const addr_type = switch (options.addr_size) {
80 2 => u16,
81 4 => u32,
82 8 => u64,
83 else => @compileError("Unsupported address size of " ++ options.addr_size),
84 };
85
86 const addr_type_signed = switch (options.addr_size) {
87 2 => i16,
88 4 => i32,
89 8 => i64,
90 else => @compileError("Unsupported address size of " ++ options.addr_size),
91 };
92
93 return struct {
94 const Self = @This();
95
96 const Operand = union(enum) {
97 generic: addr_type,
98 register: u8,
99 type_size: u8,
100 branch_offset: i16,
101 base_register: struct {
102 base_register: u8,
103 offset: i64,
104 },
105 composite_location: struct {
106 size: u64,
107 offset: i64,
108 },
109 block: []const u8,
110 register_type: struct {
111 register: u8,
112 type_offset: addr_type,
113 },
114 const_type: struct {
115 type_offset: addr_type,
116 value_bytes: []const u8,
117 },
118 deref_type: struct {
119 size: u8,
120 type_offset: addr_type,
121 },
122 };
123
124 const Value = union(enum) {
125 generic: addr_type,
126
127 // Typed value with a maximum size of a register
128 regval_type: struct {
129 // Offset of DW_TAG_base_type DIE
130 type_offset: addr_type,
131 type_size: u8,
132 value: addr_type,
133 },
134
135 // Typed value specified directly in the instruction stream
136 const_type: struct {
137 // Offset of DW_TAG_base_type DIE
138 type_offset: addr_type,
139 // Backed by the instruction stream
140 value_bytes: []const u8,
141 },
142
143 pub fn asIntegral(self: Value) !addr_type {
144 return switch (self) {
145 .generic => |v| v,
146
147 // TODO: For these two prongs, look up the type and assert it's integral?
148 .regval_type => |regval_type| regval_type.value,
149 .const_type => |const_type| {
150 const value: u64 = switch (const_type.value_bytes.len) {
151 1 => mem.readInt(u8, const_type.value_bytes[0..1], native_endian),
152 2 => mem.readInt(u16, const_type.value_bytes[0..2], native_endian),
153 4 => mem.readInt(u32, const_type.value_bytes[0..4], native_endian),
154 8 => mem.readInt(u64, const_type.value_bytes[0..8], native_endian),
155 else => return error.InvalidIntegralTypeSize,
156 };
157
158 return std.math.cast(addr_type, value) orelse error.TruncatedIntegralType;
159 },
160 };
161 }
162 };
163
164 stack: std.ArrayListUnmanaged(Value) = .{},
165
166 pub fn reset(self: *Self) void {
167 self.stack.clearRetainingCapacity();
168 }
169
170 pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
171 self.stack.deinit(allocator);
172 }
173
174 fn generic(value: anytype) Operand {
175 const int_info = @typeInfo(@TypeOf(value)).Int;
176 if (@sizeOf(@TypeOf(value)) > options.addr_size) {
177 return .{ .generic = switch (int_info.signedness) {
178 .signed => @bitCast(@as(addr_type_signed, @truncate(value))),
179 .unsigned => @truncate(value),
180 } };
181 } else {
182 return .{ .generic = switch (int_info.signedness) {
183 .signed => @bitCast(@as(addr_type_signed, @intCast(value))),
184 .unsigned => @intCast(value),
185 } };
186 }
187 }
188
189 pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8, context: ExpressionContext) !?Operand {
190 const reader = stream.reader();
191 return switch (opcode) {
192 OP.addr => generic(try reader.readInt(addr_type, options.endian)),
193 OP.call_ref => switch (context.format) {
194 .@"32" => generic(try reader.readInt(u32, options.endian)),
195 .@"64" => generic(try reader.readInt(u64, options.endian)),
196 },
197 OP.const1u,
198 OP.pick,
199 => generic(try reader.readByte()),
200 OP.deref_size,
201 OP.xderef_size,
202 => .{ .type_size = try reader.readByte() },
203 OP.const1s => generic(try reader.readByteSigned()),
204 OP.const2u,
205 OP.call2,
206 => generic(try reader.readInt(u16, options.endian)),
207 OP.call4 => generic(try reader.readInt(u32, options.endian)),
208 OP.const2s => generic(try reader.readInt(i16, options.endian)),
209 OP.bra,
210 OP.skip,
211 => .{ .branch_offset = try reader.readInt(i16, options.endian) },
212 OP.const4u => generic(try reader.readInt(u32, options.endian)),
213 OP.const4s => generic(try reader.readInt(i32, options.endian)),
214 OP.const8u => generic(try reader.readInt(u64, options.endian)),
215 OP.const8s => generic(try reader.readInt(i64, options.endian)),
216 OP.constu,
217 OP.plus_uconst,
218 OP.addrx,
219 OP.constx,
220 OP.convert,
221 OP.reinterpret,
222 => generic(try leb.readUleb128(u64, reader)),
223 OP.consts,
224 OP.fbreg,
225 => generic(try leb.readIleb128(i64, reader)),
226 OP.lit0...OP.lit31 => |n| generic(n - OP.lit0),
227 OP.reg0...OP.reg31 => |n| .{ .register = n - OP.reg0 },
228 OP.breg0...OP.breg31 => |n| .{ .base_register = .{
229 .base_register = n - OP.breg0,
230 .offset = try leb.readIleb128(i64, reader),
231 } },
232 OP.regx => .{ .register = try leb.readUleb128(u8, reader) },
233 OP.bregx => blk: {
234 const base_register = try leb.readUleb128(u8, reader);
235 const offset = try leb.readIleb128(i64, reader);
236 break :blk .{ .base_register = .{
237 .base_register = base_register,
238 .offset = offset,
239 } };
240 },
241 OP.regval_type => blk: {
242 const register = try leb.readUleb128(u8, reader);
243 const type_offset = try leb.readUleb128(addr_type, reader);
244 break :blk .{ .register_type = .{
245 .register = register,
246 .type_offset = type_offset,
247 } };
248 },
249 OP.piece => .{
250 .composite_location = .{
251 .size = try leb.readUleb128(u8, reader),
252 .offset = 0,
253 },
254 },
255 OP.bit_piece => blk: {
256 const size = try leb.readUleb128(u8, reader);
257 const offset = try leb.readIleb128(i64, reader);
258 break :blk .{ .composite_location = .{
259 .size = size,
260 .offset = offset,
261 } };
262 },
263 OP.implicit_value, OP.entry_value => blk: {
264 const size = try leb.readUleb128(u8, reader);
265 if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
266 const block = stream.buffer[stream.pos..][0..size];
267 stream.pos += size;
268 break :blk .{
269 .block = block,
270 };
271 },
272 OP.const_type => blk: {
273 const type_offset = try leb.readUleb128(addr_type, reader);
274 const size = try reader.readByte();
275 if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
276 const value_bytes = stream.buffer[stream.pos..][0..size];
277 stream.pos += size;
278 break :blk .{ .const_type = .{
279 .type_offset = type_offset,
280 .value_bytes = value_bytes,
281 } };
282 },
283 OP.deref_type,
284 OP.xderef_type,
285 => .{
286 .deref_type = .{
287 .size = try reader.readByte(),
288 .type_offset = try leb.readUleb128(addr_type, reader),
289 },
290 },
291 OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,
292 else => null,
293 };
294 }
295
296 pub fn run(
297 self: *Self,
298 expression: []const u8,
299 allocator: std.mem.Allocator,
300 context: ExpressionContext,
301 initial_value: ?usize,
302 ) ExpressionError!?Value {
303 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
304 var stream = std.io.fixedBufferStream(expression);
305 while (try self.step(&stream, allocator, context)) {}
306 if (self.stack.items.len == 0) return null;
307 return self.stack.items[self.stack.items.len - 1];
308 }
309
310 /// Reads an opcode and its operands from `stream`, then executes it
311 pub fn step(
312 self: *Self,
313 stream: *std.io.FixedBufferStream([]const u8),
314 allocator: std.mem.Allocator,
315 context: ExpressionContext,
316 ) ExpressionError!bool {
317 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
318 @compileError("Execution of non-native address sizes / endianness is not supported");
319
320 const opcode = try stream.reader().readByte();
321 if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
322 const operand = try readOperand(stream, opcode, context);
323 switch (opcode) {
324
325 // 2.5.1.1: Literal Encodings
326 OP.lit0...OP.lit31,
327 OP.addr,
328 OP.const1u,
329 OP.const2u,
330 OP.const4u,
331 OP.const8u,
332 OP.const1s,
333 OP.const2s,
334 OP.const4s,
335 OP.const8s,
336 OP.constu,
337 OP.consts,
338 => try self.stack.append(allocator, .{ .generic = operand.?.generic }),
339
340 OP.const_type => {
341 const const_type = operand.?.const_type;
342 try self.stack.append(allocator, .{ .const_type = .{
343 .type_offset = const_type.type_offset,
344 .value_bytes = const_type.value_bytes,
345 } });
346 },
347
348 OP.addrx,
349 OP.constx,
350 => {
351 if (context.compile_unit == null) return error.IncompleteExpressionContext;
352 if (context.debug_addr == null) return error.IncompleteExpressionContext;
353 const debug_addr_index = operand.?.generic;
354 const offset = context.compile_unit.?.addr_base + debug_addr_index;
355 if (offset >= context.debug_addr.?.len) return error.InvalidExpression;
356 const value = mem.readInt(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)], native_endian);
357 try self.stack.append(allocator, .{ .generic = value });
358 },
359
360 // 2.5.1.2: Register Values
361 OP.fbreg => {
362 if (context.compile_unit == null) return error.IncompleteExpressionContext;
363 if (context.compile_unit.?.frame_base == null) return error.IncompleteExpressionContext;
364
365 const offset: i64 = @intCast(operand.?.generic);
366 _ = offset;
367
368 switch (context.compile_unit.?.frame_base.?.*) {
369 .exprloc => {
370 // TODO: Run this expression in a nested stack machine
371 return error.UnimplementedOpcode;
372 },
373 .loclistx => {
374 // TODO: Read value from .debug_loclists
375 return error.UnimplementedOpcode;
376 },
377 .sec_offset => {
378 // TODO: Read value from .debug_loclists
379 return error.UnimplementedOpcode;
380 },
381 else => return error.InvalidFrameBase,
382 }
383 },
384 OP.breg0...OP.breg31,
385 OP.bregx,
386 => {
387 if (context.thread_context == null) return error.IncompleteExpressionContext;
388
389 const base_register = operand.?.base_register;
390 var value: i64 = @intCast(mem.readInt(usize, (try abi.regBytes(
391 context.thread_context.?,
392 base_register.base_register,
393 context.reg_context,
394 ))[0..@sizeOf(usize)], native_endian));
395 value += base_register.offset;
396 try self.stack.append(allocator, .{ .generic = @intCast(value) });
397 },
398 OP.regval_type => {
399 const register_type = operand.?.register_type;
400 const value = mem.readInt(usize, (try abi.regBytes(
401 context.thread_context.?,
402 register_type.register,
403 context.reg_context,
404 ))[0..@sizeOf(usize)], native_endian);
405 try self.stack.append(allocator, .{
406 .regval_type = .{
407 .type_offset = register_type.type_offset,
408 .type_size = @sizeOf(addr_type),
409 .value = value,
410 },
411 });
412 },
413
414 // 2.5.1.3: Stack Operations
415 OP.dup => {
416 if (self.stack.items.len == 0) return error.InvalidExpression;
417 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1]);
418 },
419 OP.drop => {
420 _ = self.stack.pop();
421 },
422 OP.pick, OP.over => {
423 const stack_index = if (opcode == OP.over) 1 else operand.?.generic;
424 if (stack_index >= self.stack.items.len) return error.InvalidExpression;
425 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
426 },
427 OP.swap => {
428 if (self.stack.items.len < 2) return error.InvalidExpression;
429 mem.swap(Value, &self.stack.items[self.stack.items.len - 1], &self.stack.items[self.stack.items.len - 2]);
430 },
431 OP.rot => {
432 if (self.stack.items.len < 3) return error.InvalidExpression;
433 const first = self.stack.items[self.stack.items.len - 1];
434 self.stack.items[self.stack.items.len - 1] = self.stack.items[self.stack.items.len - 2];
435 self.stack.items[self.stack.items.len - 2] = self.stack.items[self.stack.items.len - 3];
436 self.stack.items[self.stack.items.len - 3] = first;
437 },
438 OP.deref,
439 OP.xderef,
440 OP.deref_size,
441 OP.xderef_size,
442 OP.deref_type,
443 OP.xderef_type,
444 => {
445 if (self.stack.items.len == 0) return error.InvalidExpression;
446 const addr = try self.stack.items[self.stack.items.len - 1].asIntegral();
447 const addr_space_identifier: ?usize = switch (opcode) {
448 OP.xderef,
449 OP.xderef_size,
450 OP.xderef_type,
451 => blk: {
452 _ = self.stack.pop();
453 if (self.stack.items.len == 0) return error.InvalidExpression;
454 break :blk try self.stack.items[self.stack.items.len - 1].asIntegral();
455 },
456 else => null,
457 };
458
459 // Usage of addr_space_identifier in the address calculation is implementation defined.
460 // This code will need to be updated to handle any architectures that utilize this.
461 _ = addr_space_identifier;
462
463 const size = switch (opcode) {
464 OP.deref,
465 OP.xderef,
466 => @sizeOf(addr_type),
467 OP.deref_size,
468 OP.xderef_size,
469 => operand.?.type_size,
470 OP.deref_type,
471 OP.xderef_type,
472 => operand.?.deref_type.size,
473 else => unreachable,
474 };
475
476 if (context.memory_accessor) |memory_accessor| {
477 if (!switch (size) {
478 1 => memory_accessor.load(u8, addr) != null,
479 2 => memory_accessor.load(u16, addr) != null,
480 4 => memory_accessor.load(u32, addr) != null,
481 8 => memory_accessor.load(u64, addr) != null,
482 else => return error.InvalidExpression,
483 }) return error.InvalidExpression;
484 }
485
486 const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) {
487 1 => @as(*const u8, @ptrFromInt(addr)).*,
488 2 => @as(*const u16, @ptrFromInt(addr)).*,
489 4 => @as(*const u32, @ptrFromInt(addr)).*,
490 8 => @as(*const u64, @ptrFromInt(addr)).*,
491 else => return error.InvalidExpression,
492 })) orelse return error.InvalidExpression;
493
494 switch (opcode) {
495 OP.deref_type,
496 OP.xderef_type,
497 => {
498 self.stack.items[self.stack.items.len - 1] = .{
499 .regval_type = .{
500 .type_offset = operand.?.deref_type.type_offset,
501 .type_size = operand.?.deref_type.size,
502 .value = value,
503 },
504 };
505 },
506 else => {
507 self.stack.items[self.stack.items.len - 1] = .{ .generic = value };
508 },
509 }
510 },
511 OP.push_object_address => {
512 // In sub-expressions, `push_object_address` is not meaningful (as per the
513 // spec), so treat it like a nop
514 if (!context.entry_value_context) {
515 if (context.object_address == null) return error.IncompleteExpressionContext;
516 try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });
517 }
518 },
519 OP.form_tls_address => {
520 return error.UnimplementedOpcode;
521 },
522 OP.call_frame_cfa => {
523 if (context.cfa) |cfa| {
524 try self.stack.append(allocator, .{ .generic = cfa });
525 } else return error.IncompleteExpressionContext;
526 },
527
528 // 2.5.1.4: Arithmetic and Logical Operations
529 OP.abs => {
530 if (self.stack.items.len == 0) return error.InvalidExpression;
531 const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
532 self.stack.items[self.stack.items.len - 1] = .{
533 .generic = @abs(value),
534 };
535 },
536 OP.@"and" => {
537 if (self.stack.items.len < 2) return error.InvalidExpression;
538 const a = try self.stack.pop().asIntegral();
539 self.stack.items[self.stack.items.len - 1] = .{
540 .generic = a & try self.stack.items[self.stack.items.len - 1].asIntegral(),
541 };
542 },
543 OP.div => {
544 if (self.stack.items.len < 2) return error.InvalidExpression;
545 const a: isize = @bitCast(try self.stack.pop().asIntegral());
546 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
547 self.stack.items[self.stack.items.len - 1] = .{
548 .generic = @bitCast(try std.math.divTrunc(isize, b, a)),
549 };
550 },
551 OP.minus => {
552 if (self.stack.items.len < 2) return error.InvalidExpression;
553 const b = try self.stack.pop().asIntegral();
554 self.stack.items[self.stack.items.len - 1] = .{
555 .generic = try std.math.sub(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
556 };
557 },
558 OP.mod => {
559 if (self.stack.items.len < 2) return error.InvalidExpression;
560 const a: isize = @bitCast(try self.stack.pop().asIntegral());
561 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
562 self.stack.items[self.stack.items.len - 1] = .{
563 .generic = @bitCast(@mod(b, a)),
564 };
565 },
566 OP.mul => {
567 if (self.stack.items.len < 2) return error.InvalidExpression;
568 const a: isize = @bitCast(try self.stack.pop().asIntegral());
569 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
570 self.stack.items[self.stack.items.len - 1] = .{
571 .generic = @bitCast(@mulWithOverflow(a, b)[0]),
572 };
573 },
574 OP.neg => {
575 if (self.stack.items.len == 0) return error.InvalidExpression;
576 self.stack.items[self.stack.items.len - 1] = .{
577 .generic = @bitCast(
578 try std.math.negate(
579 @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
580 ),
581 ),
582 };
583 },
584 OP.not => {
585 if (self.stack.items.len == 0) return error.InvalidExpression;
586 self.stack.items[self.stack.items.len - 1] = .{
587 .generic = ~try self.stack.items[self.stack.items.len - 1].asIntegral(),
588 };
589 },
590 OP.@"or" => {
591 if (self.stack.items.len < 2) return error.InvalidExpression;
592 const a = try self.stack.pop().asIntegral();
593 self.stack.items[self.stack.items.len - 1] = .{
594 .generic = a | try self.stack.items[self.stack.items.len - 1].asIntegral(),
595 };
596 },
597 OP.plus => {
598 if (self.stack.items.len < 2) return error.InvalidExpression;
599 const b = try self.stack.pop().asIntegral();
600 self.stack.items[self.stack.items.len - 1] = .{
601 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
602 };
603 },
604 OP.plus_uconst => {
605 if (self.stack.items.len == 0) return error.InvalidExpression;
606 const constant = operand.?.generic;
607 self.stack.items[self.stack.items.len - 1] = .{
608 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
609 };
610 },
611 OP.shl => {
612 if (self.stack.items.len < 2) return error.InvalidExpression;
613 const a = try self.stack.pop().asIntegral();
614 const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
615 self.stack.items[self.stack.items.len - 1] = .{
616 .generic = std.math.shl(usize, b, a),
617 };
618 },
619 OP.shr => {
620 if (self.stack.items.len < 2) return error.InvalidExpression;
621 const a = try self.stack.pop().asIntegral();
622 const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
623 self.stack.items[self.stack.items.len - 1] = .{
624 .generic = std.math.shr(usize, b, a),
625 };
626 },
627 OP.shra => {
628 if (self.stack.items.len < 2) return error.InvalidExpression;
629 const a = try self.stack.pop().asIntegral();
630 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
631 self.stack.items[self.stack.items.len - 1] = .{
632 .generic = @bitCast(std.math.shr(isize, b, a)),
633 };
634 },
635 OP.xor => {
636 if (self.stack.items.len < 2) return error.InvalidExpression;
637 const a = try self.stack.pop().asIntegral();
638 self.stack.items[self.stack.items.len - 1] = .{
639 .generic = a ^ try self.stack.items[self.stack.items.len - 1].asIntegral(),
640 };
641 },
642
643 // 2.5.1.5: Control Flow Operations
644 OP.le,
645 OP.ge,
646 OP.eq,
647 OP.lt,
648 OP.gt,
649 OP.ne,
650 => {
651 if (self.stack.items.len < 2) return error.InvalidExpression;
652 const a = self.stack.pop();
653 const b = self.stack.items[self.stack.items.len - 1];
654
655 if (a == .generic and b == .generic) {
656 const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
657 const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
658 const result = @intFromBool(switch (opcode) {
659 OP.le => b_int <= a_int,
660 OP.ge => b_int >= a_int,
661 OP.eq => b_int == a_int,
662 OP.lt => b_int < a_int,
663 OP.gt => b_int > a_int,
664 OP.ne => b_int != a_int,
665 else => unreachable,
666 });
667
668 self.stack.items[self.stack.items.len - 1] = .{ .generic = result };
669 } else {
670 // TODO: Load the types referenced by these values, find their comparison operator, and run it
671 return error.UnimplementedTypedComparison;
672 }
673 },
674 OP.skip, OP.bra => {
675 const branch_offset = operand.?.branch_offset;
676 const condition = if (opcode == OP.bra) blk: {
677 if (self.stack.items.len == 0) return error.InvalidExpression;
678 break :blk try self.stack.pop().asIntegral() != 0;
679 } else true;
680
681 if (condition) {
682 const new_pos = std.math.cast(
683 usize,
684 try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
685 ) orelse return error.InvalidExpression;
686
687 if (new_pos < 0 or new_pos > stream.buffer.len) return error.InvalidExpression;
688 stream.pos = new_pos;
689 }
690 },
691 OP.call2,
692 OP.call4,
693 OP.call_ref,
694 => {
695 const debug_info_offset = operand.?.generic;
696 _ = debug_info_offset;
697
698 // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it
699 // can be in a separate exe / shared object from the one containing this expression).
700 // Transfer control to the DW_AT_location attribute, with the current stack as input.
701
702 return error.UnimplementedExpressionCall;
703 },
704
705 // 2.5.1.6: Type Conversions
706 OP.convert => {
707 if (self.stack.items.len == 0) return error.InvalidExpression;
708 const type_offset = operand.?.generic;
709
710 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
711 const value = self.stack.items[self.stack.items.len - 1];
712 if (type_offset == 0) {
713 self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
714 } else {
715 // TODO: Load the DW_TAG_base_type entry in context.compile_unit, find a conversion operator
716 // from the old type to the new type, run it.
717 return error.UnimplementedTypeConversion;
718 }
719 },
720 OP.reinterpret => {
721 if (self.stack.items.len == 0) return error.InvalidExpression;
722 const type_offset = operand.?.generic;
723
724 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
725 const value = self.stack.items[self.stack.items.len - 1];
726 if (type_offset == 0) {
727 self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
728 } else {
729 self.stack.items[self.stack.items.len - 1] = switch (value) {
730 .generic => |v| .{
731 .regval_type = .{
732 .type_offset = type_offset,
733 .type_size = @sizeOf(addr_type),
734 .value = v,
735 },
736 },
737 .regval_type => |r| .{
738 .regval_type = .{
739 .type_offset = type_offset,
740 .type_size = r.type_size,
741 .value = r.value,
742 },
743 },
744 .const_type => |c| .{
745 .const_type = .{
746 .type_offset = type_offset,
747 .value_bytes = c.value_bytes,
748 },
749 },
750 };
751 }
752 },
753
754 // 2.5.1.7: Special Operations
755 OP.nop => {},
756 OP.entry_value => {
757 const block = operand.?.block;
758 if (block.len == 0) return error.InvalidSubExpression;
759
760 // TODO: The spec states that this sub-expression needs to observe the state (ie. registers)
761 // as it was upon entering the current subprogram. If this isn't being called at the
762 // end of a frame unwind operation, an additional ThreadContext with this state will be needed.
763
764 if (isOpcodeRegisterLocation(block[0])) {
765 if (context.thread_context == null) return error.IncompleteExpressionContext;
766
767 var block_stream = std.io.fixedBufferStream(block);
768 const register = (try readOperand(&block_stream, block[0], context)).?.register;
769 const value = mem.readInt(usize, (try abi.regBytes(context.thread_context.?, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
770 try self.stack.append(allocator, .{ .generic = value });
771 } else {
772 var stack_machine: Self = .{};
773 defer stack_machine.deinit(allocator);
774
775 var sub_context = context;
776 sub_context.entry_value_context = true;
777 const result = try stack_machine.run(block, allocator, sub_context, null);
778 try self.stack.append(allocator, result orelse return error.InvalidSubExpression);
779 }
780 },
781
782 // These have already been handled by readOperand
783 OP.lo_user...OP.hi_user => unreachable,
784 else => {
785 //std.debug.print("Unknown DWARF expression opcode: {x}\n", .{opcode});
786 return error.UnknownExpressionOpcode;
787 },
788 }
789
790 return stream.pos < stream.buffer.len;
791 }
792 };
793}
794
795pub fn Builder(comptime options: ExpressionOptions) type {
796 const addr_type = switch (options.addr_size) {
797 2 => u16,
798 4 => u32,
799 8 => u64,
800 else => @compileError("Unsupported address size of " ++ options.addr_size),
801 };
802
803 return struct {
804 /// Zero-operand instructions
805 pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {
806 if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
807 switch (opcode) {
808 OP.dup,
809 OP.drop,
810 OP.over,
811 OP.swap,
812 OP.rot,
813 OP.deref,
814 OP.xderef,
815 OP.push_object_address,
816 OP.form_tls_address,
817 OP.call_frame_cfa,
818 OP.abs,
819 OP.@"and",
820 OP.div,
821 OP.minus,
822 OP.mod,
823 OP.mul,
824 OP.neg,
825 OP.not,
826 OP.@"or",
827 OP.plus,
828 OP.shl,
829 OP.shr,
830 OP.shra,
831 OP.xor,
832 OP.le,
833 OP.ge,
834 OP.eq,
835 OP.lt,
836 OP.gt,
837 OP.ne,
838 OP.nop,
839 OP.stack_value,
840 => try writer.writeByte(opcode),
841 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
842 }
843 }
844
845 // 2.5.1.1: Literal Encodings
846 pub fn writeLiteral(writer: anytype, literal: u8) !void {
847 switch (literal) {
848 0...31 => |n| try writer.writeByte(n + OP.lit0),
849 else => return error.InvalidLiteral,
850 }
851 }
852
853 pub fn writeConst(writer: anytype, comptime T: type, value: T) !void {
854 if (@typeInfo(T) != .Int) @compileError("Constants must be integers");
855
856 switch (T) {
857 u8, i8, u16, i16, u32, i32, u64, i64 => {
858 try writer.writeByte(switch (T) {
859 u8 => OP.const1u,
860 i8 => OP.const1s,
861 u16 => OP.const2u,
862 i16 => OP.const2s,
863 u32 => OP.const4u,
864 i32 => OP.const4s,
865 u64 => OP.const8u,
866 i64 => OP.const8s,
867 else => unreachable,
868 });
869
870 try writer.writeInt(T, value, options.endian);
871 },
872 else => switch (@typeInfo(T).Int.signedness) {
873 .unsigned => {
874 try writer.writeByte(OP.constu);
875 try leb.writeUleb128(writer, value);
876 },
877 .signed => {
878 try writer.writeByte(OP.consts);
879 try leb.writeIleb128(writer, value);
880 },
881 },
882 }
883 }
884
885 pub fn writeConstx(writer: anytype, debug_addr_offset: anytype) !void {
886 try writer.writeByte(OP.constx);
887 try leb.writeUleb128(writer, debug_addr_offset);
888 }
889
890 pub fn writeConstType(writer: anytype, die_offset: anytype, value_bytes: []const u8) !void {
891 if (options.call_frame_context) return error.InvalidCFAOpcode;
892 if (value_bytes.len > 0xff) return error.InvalidTypeLength;
893 try writer.writeByte(OP.const_type);
894 try leb.writeUleb128(writer, die_offset);
895 try writer.writeByte(@intCast(value_bytes.len));
896 try writer.writeAll(value_bytes);
897 }
898
899 pub fn writeAddr(writer: anytype, value: addr_type) !void {
900 try writer.writeByte(OP.addr);
901 try writer.writeInt(addr_type, value, options.endian);
902 }
903
904 pub fn writeAddrx(writer: anytype, debug_addr_offset: anytype) !void {
905 if (options.call_frame_context) return error.InvalidCFAOpcode;
906 try writer.writeByte(OP.addrx);
907 try leb.writeUleb128(writer, debug_addr_offset);
908 }
909
910 // 2.5.1.2: Register Values
911 pub fn writeFbreg(writer: anytype, offset: anytype) !void {
912 try writer.writeByte(OP.fbreg);
913 try leb.writeIleb128(writer, offset);
914 }
915
916 pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {
917 if (register > 31) return error.InvalidRegister;
918 try writer.writeByte(OP.breg0 + register);
919 try leb.writeIleb128(writer, offset);
920 }
921
922 pub fn writeBregx(writer: anytype, register: anytype, offset: anytype) !void {
923 try writer.writeByte(OP.bregx);
924 try leb.writeUleb128(writer, register);
925 try leb.writeIleb128(writer, offset);
926 }
927
928 pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {
929 if (options.call_frame_context) return error.InvalidCFAOpcode;
930 try writer.writeByte(OP.regval_type);
931 try leb.writeUleb128(writer, register);
932 try leb.writeUleb128(writer, offset);
933 }
934
935 // 2.5.1.3: Stack Operations
936 pub fn writePick(writer: anytype, index: u8) !void {
937 try writer.writeByte(OP.pick);
938 try writer.writeByte(index);
939 }
940
941 pub fn writeDerefSize(writer: anytype, size: u8) !void {
942 try writer.writeByte(OP.deref_size);
943 try writer.writeByte(size);
944 }
945
946 pub fn writeXDerefSize(writer: anytype, size: u8) !void {
947 try writer.writeByte(OP.xderef_size);
948 try writer.writeByte(size);
949 }
950
951 pub fn writeDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
952 if (options.call_frame_context) return error.InvalidCFAOpcode;
953 try writer.writeByte(OP.deref_type);
954 try writer.writeByte(size);
955 try leb.writeUleb128(writer, die_offset);
956 }
957
958 pub fn writeXDerefType(writer: anytype, size: u8, die_offset: anytype) !void {
959 try writer.writeByte(OP.xderef_type);
960 try writer.writeByte(size);
961 try leb.writeUleb128(writer, die_offset);
962 }
963
964 // 2.5.1.4: Arithmetic and Logical Operations
965
966 pub fn writePlusUconst(writer: anytype, uint_value: anytype) !void {
967 try writer.writeByte(OP.plus_uconst);
968 try leb.writeUleb128(writer, uint_value);
969 }
970
971 // 2.5.1.5: Control Flow Operations
972
973 pub fn writeSkip(writer: anytype, offset: i16) !void {
974 try writer.writeByte(OP.skip);
975 try writer.writeInt(i16, offset, options.endian);
976 }
977
978 pub fn writeBra(writer: anytype, offset: i16) !void {
979 try writer.writeByte(OP.bra);
980 try writer.writeInt(i16, offset, options.endian);
981 }
982
983 pub fn writeCall(writer: anytype, comptime T: type, offset: T) !void {
984 if (options.call_frame_context) return error.InvalidCFAOpcode;
985 switch (T) {
986 u16 => try writer.writeByte(OP.call2),
987 u32 => try writer.writeByte(OP.call4),
988 else => @compileError("Call operand must be a 2 or 4 byte offset"),
989 }
990
991 try writer.writeInt(T, offset, options.endian);
992 }
993
994 pub fn writeCallRef(writer: anytype, comptime is_64: bool, value: if (is_64) u64 else u32) !void {
995 if (options.call_frame_context) return error.InvalidCFAOpcode;
996 try writer.writeByte(OP.call_ref);
997 try writer.writeInt(if (is_64) u64 else u32, value, options.endian);
998 }
999
1000 pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
1001 if (options.call_frame_context) return error.InvalidCFAOpcode;
1002 try writer.writeByte(OP.convert);
1003 try leb.writeUleb128(writer, die_offset);
1004 }
1005
1006 pub fn writeReinterpret(writer: anytype, die_offset: anytype) !void {
1007 if (options.call_frame_context) return error.InvalidCFAOpcode;
1008 try writer.writeByte(OP.reinterpret);
1009 try leb.writeUleb128(writer, die_offset);
1010 }
1011
1012 // 2.5.1.7: Special Operations
1013
1014 pub fn writeEntryValue(writer: anytype, expression: []const u8) !void {
1015 try writer.writeByte(OP.entry_value);
1016 try leb.writeUleb128(writer, expression.len);
1017 try writer.writeAll(expression);
1018 }
1019
1020 // 2.6: Location Descriptions
1021 pub fn writeReg(writer: anytype, register: u8) !void {
1022 try writer.writeByte(OP.reg0 + register);
1023 }
1024
1025 pub fn writeRegx(writer: anytype, register: anytype) !void {
1026 try writer.writeByte(OP.regx);
1027 try leb.writeUleb128(writer, register);
1028 }
1029
1030 pub fn writeImplicitValue(writer: anytype, value_bytes: []const u8) !void {
1031 try writer.writeByte(OP.implicit_value);
1032 try leb.writeUleb128(writer, value_bytes.len);
1033 try writer.writeAll(value_bytes);
1034 }
1035 };
1036}
1037
1038// Certain opcodes are not allowed in a CFA context, see 6.4.2
1039fn isOpcodeValidInCFA(opcode: u8) bool {
1040 return switch (opcode) {
1041 OP.addrx,
1042 OP.call2,
1043 OP.call4,
1044 OP.call_ref,
1045 OP.const_type,
1046 OP.constx,
1047 OP.convert,
1048 OP.deref_type,
1049 OP.regval_type,
1050 OP.reinterpret,
1051 OP.push_object_address,
1052 OP.call_frame_cfa,
1053 => false,
1054 else => true,
1055 };
1056}
1057
1058fn isOpcodeRegisterLocation(opcode: u8) bool {
1059 return switch (opcode) {
1060 OP.reg0...OP.reg31, OP.regx => true,
1061 else => false,
1062 };
1063}
1064
1065const testing = std.testing;
1066test "DWARF expressions" {
1067 const allocator = std.testing.allocator;
1068
1069 const options = ExpressionOptions{};
1070 var stack_machine = StackMachine(options){};
1071 defer stack_machine.deinit(allocator);
1072
1073 const b = Builder(options);
1074
1075 var program = std.ArrayList(u8).init(allocator);
1076 defer program.deinit();
1077
1078 const writer = program.writer();
1079
1080 // Literals
1081 {
1082 const context = ExpressionContext{};
1083 for (0..32) |i| {
1084 try b.writeLiteral(writer, @intCast(i));
1085 }
1086
1087 _ = try stack_machine.run(program.items, allocator, context, 0);
1088
1089 for (0..32) |i| {
1090 const expected = 31 - i;
1091 try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic);
1092 }
1093 }
1094
1095 // Constants
1096 {
1097 stack_machine.reset();
1098 program.clearRetainingCapacity();
1099
1100 const input = [_]comptime_int{
1101 1,
1102 -1,
1103 @as(usize, @truncate(0x0fff)),
1104 @as(isize, @truncate(-0x0fff)),
1105 @as(usize, @truncate(0x0fffffff)),
1106 @as(isize, @truncate(-0x0fffffff)),
1107 @as(usize, @truncate(0x0fffffffffffffff)),
1108 @as(isize, @truncate(-0x0fffffffffffffff)),
1109 @as(usize, @truncate(0x8000000)),
1110 @as(isize, @truncate(-0x8000000)),
1111 @as(usize, @truncate(0x12345678_12345678)),
1112 @as(usize, @truncate(0xffffffff_ffffffff)),
1113 @as(usize, @truncate(0xeeeeeeee_eeeeeeee)),
1114 };
1115
1116 try b.writeConst(writer, u8, input[0]);
1117 try b.writeConst(writer, i8, input[1]);
1118 try b.writeConst(writer, u16, input[2]);
1119 try b.writeConst(writer, i16, input[3]);
1120 try b.writeConst(writer, u32, input[4]);
1121 try b.writeConst(writer, i32, input[5]);
1122 try b.writeConst(writer, u64, input[6]);
1123 try b.writeConst(writer, i64, input[7]);
1124 try b.writeConst(writer, u28, input[8]);
1125 try b.writeConst(writer, i28, input[9]);
1126 try b.writeAddr(writer, input[10]);
1127
1128 var mock_compile_unit: dwarf.CompileUnit = undefined;
1129 mock_compile_unit.addr_base = 1;
1130
1131 var mock_debug_addr = std.ArrayList(u8).init(allocator);
1132 defer mock_debug_addr.deinit();
1133
1134 try mock_debug_addr.writer().writeInt(u16, 0, native_endian);
1135 try mock_debug_addr.writer().writeInt(usize, input[11], native_endian);
1136 try mock_debug_addr.writer().writeInt(usize, input[12], native_endian);
1137
1138 const context = ExpressionContext{
1139 .compile_unit = &mock_compile_unit,
1140 .debug_addr = mock_debug_addr.items,
1141 };
1142
1143 try b.writeConstx(writer, @as(usize, 1));
1144 try b.writeAddrx(writer, @as(usize, 1 + @sizeOf(usize)));
1145
1146 const die_offset: usize = @truncate(0xaabbccdd);
1147 const type_bytes: []const u8 = &.{ 1, 2, 3, 4 };
1148 try b.writeConstType(writer, die_offset, type_bytes);
1149
1150 _ = try stack_machine.run(program.items, allocator, context, 0);
1151
1152 const const_type = stack_machine.stack.popOrNull().?.const_type;
1153 try testing.expectEqual(die_offset, const_type.type_offset);
1154 try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes);
1155
1156 const expected = .{
1157 .{ usize, input[12], usize },
1158 .{ usize, input[11], usize },
1159 .{ usize, input[10], usize },
1160 .{ isize, input[9], isize },
1161 .{ usize, input[8], usize },
1162 .{ isize, input[7], isize },
1163 .{ usize, input[6], usize },
1164 .{ isize, input[5], isize },
1165 .{ usize, input[4], usize },
1166 .{ isize, input[3], isize },
1167 .{ usize, input[2], usize },
1168 .{ isize, input[1], isize },
1169 .{ usize, input[0], usize },
1170 };
1171
1172 inline for (expected) |e| {
1173 try testing.expectEqual(@as(e[0], e[1]), @as(e[2], @bitCast(stack_machine.stack.popOrNull().?.generic)));
1174 }
1175 }
1176
1177 // Register values
1178 if (@sizeOf(std.debug.ThreadContext) != 0) {
1179 stack_machine.reset();
1180 program.clearRetainingCapacity();
1181
1182 const reg_context = abi.RegisterContext{
1183 .eh_frame = true,
1184 .is_macho = builtin.os.tag == .macos,
1185 };
1186 var thread_context: std.debug.ThreadContext = undefined;
1187 std.debug.relocateContext(&thread_context);
1188 const context = ExpressionContext{
1189 .thread_context = &thread_context,
1190 .reg_context = reg_context,
1191 };
1192
1193 // Only test register operations on arch / os that have them implemented
1194 if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
1195
1196 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
1197
1198 mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1199 (try abi.regValueNative(usize, &thread_context, abi.fpRegNum(reg_context), reg_context)).* = 1;
1200 (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2;
1201 (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3;
1202
1203 try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
1204 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
1205 try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
1206 try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
1207
1208 _ = try stack_machine.run(program.items, allocator, context, 0);
1209
1210 const regval_type = stack_machine.stack.popOrNull().?.regval_type;
1211 try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
1212 try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
1213 try testing.expectEqual(@as(usize, 0xee), regval_type.value);
1214
1215 try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic);
1216 try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
1217 try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
1218 } else |err| {
1219 switch (err) {
1220 error.UnimplementedArch,
1221 error.UnimplementedOs,
1222 error.ThreadContextNotSupported,
1223 => {},
1224 else => return err,
1225 }
1226 }
1227 }
1228
1229 // Stack operations
1230 {
1231 var context = ExpressionContext{};
1232
1233 stack_machine.reset();
1234 program.clearRetainingCapacity();
1235 try b.writeConst(writer, u8, 1);
1236 try b.writeOpcode(writer, OP.dup);
1237 _ = try stack_machine.run(program.items, allocator, context, null);
1238 try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
1239 try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
1240
1241 stack_machine.reset();
1242 program.clearRetainingCapacity();
1243 try b.writeConst(writer, u8, 1);
1244 try b.writeOpcode(writer, OP.drop);
1245 _ = try stack_machine.run(program.items, allocator, context, null);
1246 try testing.expect(stack_machine.stack.popOrNull() == null);
1247
1248 stack_machine.reset();
1249 program.clearRetainingCapacity();
1250 try b.writeConst(writer, u8, 4);
1251 try b.writeConst(writer, u8, 5);
1252 try b.writeConst(writer, u8, 6);
1253 try b.writePick(writer, 2);
1254 _ = try stack_machine.run(program.items, allocator, context, null);
1255 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1256
1257 stack_machine.reset();
1258 program.clearRetainingCapacity();
1259 try b.writeConst(writer, u8, 4);
1260 try b.writeConst(writer, u8, 5);
1261 try b.writeConst(writer, u8, 6);
1262 try b.writeOpcode(writer, OP.over);
1263 _ = try stack_machine.run(program.items, allocator, context, null);
1264 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1265
1266 stack_machine.reset();
1267 program.clearRetainingCapacity();
1268 try b.writeConst(writer, u8, 5);
1269 try b.writeConst(writer, u8, 6);
1270 try b.writeOpcode(writer, OP.swap);
1271 _ = try stack_machine.run(program.items, allocator, context, null);
1272 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1273 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1274
1275 stack_machine.reset();
1276 program.clearRetainingCapacity();
1277 try b.writeConst(writer, u8, 4);
1278 try b.writeConst(writer, u8, 5);
1279 try b.writeConst(writer, u8, 6);
1280 try b.writeOpcode(writer, OP.rot);
1281 _ = try stack_machine.run(program.items, allocator, context, null);
1282 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1283 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1284 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1285
1286 const deref_target: usize = @truncate(0xffeeffee_ffeeffee);
1287
1288 stack_machine.reset();
1289 program.clearRetainingCapacity();
1290 try b.writeAddr(writer, @intFromPtr(&deref_target));
1291 try b.writeOpcode(writer, OP.deref);
1292 _ = try stack_machine.run(program.items, allocator, context, null);
1293 try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
1294
1295 stack_machine.reset();
1296 program.clearRetainingCapacity();
1297 try b.writeLiteral(writer, 0);
1298 try b.writeAddr(writer, @intFromPtr(&deref_target));
1299 try b.writeOpcode(writer, OP.xderef);
1300 _ = try stack_machine.run(program.items, allocator, context, null);
1301 try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
1302
1303 stack_machine.reset();
1304 program.clearRetainingCapacity();
1305 try b.writeAddr(writer, @intFromPtr(&deref_target));
1306 try b.writeDerefSize(writer, 1);
1307 _ = try stack_machine.run(program.items, allocator, context, null);
1308 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
1309
1310 stack_machine.reset();
1311 program.clearRetainingCapacity();
1312 try b.writeLiteral(writer, 0);
1313 try b.writeAddr(writer, @intFromPtr(&deref_target));
1314 try b.writeXDerefSize(writer, 1);
1315 _ = try stack_machine.run(program.items, allocator, context, null);
1316 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
1317
1318 const type_offset: usize = @truncate(0xaabbaabb_aabbaabb);
1319
1320 stack_machine.reset();
1321 program.clearRetainingCapacity();
1322 try b.writeAddr(writer, @intFromPtr(&deref_target));
1323 try b.writeDerefType(writer, 1, type_offset);
1324 _ = try stack_machine.run(program.items, allocator, context, null);
1325 const deref_type = stack_machine.stack.popOrNull().?.regval_type;
1326 try testing.expectEqual(type_offset, deref_type.type_offset);
1327 try testing.expectEqual(@as(u8, 1), deref_type.type_size);
1328 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), deref_type.value);
1329
1330 stack_machine.reset();
1331 program.clearRetainingCapacity();
1332 try b.writeLiteral(writer, 0);
1333 try b.writeAddr(writer, @intFromPtr(&deref_target));
1334 try b.writeXDerefType(writer, 1, type_offset);
1335 _ = try stack_machine.run(program.items, allocator, context, null);
1336 const xderef_type = stack_machine.stack.popOrNull().?.regval_type;
1337 try testing.expectEqual(type_offset, xderef_type.type_offset);
1338 try testing.expectEqual(@as(u8, 1), xderef_type.type_size);
1339 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value);
1340
1341 context.object_address = &deref_target;
1342
1343 stack_machine.reset();
1344 program.clearRetainingCapacity();
1345 try b.writeOpcode(writer, OP.push_object_address);
1346 _ = try stack_machine.run(program.items, allocator, context, null);
1347 try testing.expectEqual(@as(usize, @intFromPtr(context.object_address.?)), stack_machine.stack.popOrNull().?.generic);
1348
1349 // TODO: Test OP.form_tls_address
1350
1351 context.cfa = @truncate(0xccddccdd_ccddccdd);
1352
1353 stack_machine.reset();
1354 program.clearRetainingCapacity();
1355 try b.writeOpcode(writer, OP.call_frame_cfa);
1356 _ = try stack_machine.run(program.items, allocator, context, null);
1357 try testing.expectEqual(context.cfa.?, stack_machine.stack.popOrNull().?.generic);
1358 }
1359
1360 // Arithmetic and Logical Operations
1361 {
1362 const context = ExpressionContext{};
1363
1364 stack_machine.reset();
1365 program.clearRetainingCapacity();
1366 try b.writeConst(writer, i16, -4096);
1367 try b.writeOpcode(writer, OP.abs);
1368 _ = try stack_machine.run(program.items, allocator, context, null);
1369 try testing.expectEqual(@as(usize, 4096), stack_machine.stack.popOrNull().?.generic);
1370
1371 stack_machine.reset();
1372 program.clearRetainingCapacity();
1373 try b.writeConst(writer, u16, 0xff0f);
1374 try b.writeConst(writer, u16, 0xf0ff);
1375 try b.writeOpcode(writer, OP.@"and");
1376 _ = try stack_machine.run(program.items, allocator, context, null);
1377 try testing.expectEqual(@as(usize, 0xf00f), stack_machine.stack.popOrNull().?.generic);
1378
1379 stack_machine.reset();
1380 program.clearRetainingCapacity();
1381 try b.writeConst(writer, i16, -404);
1382 try b.writeConst(writer, i16, 100);
1383 try b.writeOpcode(writer, OP.div);
1384 _ = try stack_machine.run(program.items, allocator, context, null);
1385 try testing.expectEqual(@as(isize, -404 / 100), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
1386
1387 stack_machine.reset();
1388 program.clearRetainingCapacity();
1389 try b.writeConst(writer, u16, 200);
1390 try b.writeConst(writer, u16, 50);
1391 try b.writeOpcode(writer, OP.minus);
1392 _ = try stack_machine.run(program.items, allocator, context, null);
1393 try testing.expectEqual(@as(usize, 150), stack_machine.stack.popOrNull().?.generic);
1394
1395 stack_machine.reset();
1396 program.clearRetainingCapacity();
1397 try b.writeConst(writer, u16, 123);
1398 try b.writeConst(writer, u16, 100);
1399 try b.writeOpcode(writer, OP.mod);
1400 _ = try stack_machine.run(program.items, allocator, context, null);
1401 try testing.expectEqual(@as(usize, 23), stack_machine.stack.popOrNull().?.generic);
1402
1403 stack_machine.reset();
1404 program.clearRetainingCapacity();
1405 try b.writeConst(writer, u16, 0xff);
1406 try b.writeConst(writer, u16, 0xee);
1407 try b.writeOpcode(writer, OP.mul);
1408 _ = try stack_machine.run(program.items, allocator, context, null);
1409 try testing.expectEqual(@as(usize, 0xed12), stack_machine.stack.popOrNull().?.generic);
1410
1411 stack_machine.reset();
1412 program.clearRetainingCapacity();
1413 try b.writeConst(writer, u16, 5);
1414 try b.writeOpcode(writer, OP.neg);
1415 try b.writeConst(writer, i16, -6);
1416 try b.writeOpcode(writer, OP.neg);
1417 _ = try stack_machine.run(program.items, allocator, context, null);
1418 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1419 try testing.expectEqual(@as(isize, -5), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
1420
1421 stack_machine.reset();
1422 program.clearRetainingCapacity();
1423 try b.writeConst(writer, u16, 0xff0f);
1424 try b.writeOpcode(writer, OP.not);
1425 _ = try stack_machine.run(program.items, allocator, context, null);
1426 try testing.expectEqual(~@as(usize, 0xff0f), stack_machine.stack.popOrNull().?.generic);
1427
1428 stack_machine.reset();
1429 program.clearRetainingCapacity();
1430 try b.writeConst(writer, u16, 0xff0f);
1431 try b.writeConst(writer, u16, 0xf0ff);
1432 try b.writeOpcode(writer, OP.@"or");
1433 _ = try stack_machine.run(program.items, allocator, context, null);
1434 try testing.expectEqual(@as(usize, 0xffff), stack_machine.stack.popOrNull().?.generic);
1435
1436 stack_machine.reset();
1437 program.clearRetainingCapacity();
1438 try b.writeConst(writer, i16, 402);
1439 try b.writeConst(writer, i16, 100);
1440 try b.writeOpcode(writer, OP.plus);
1441 _ = try stack_machine.run(program.items, allocator, context, null);
1442 try testing.expectEqual(@as(usize, 502), stack_machine.stack.popOrNull().?.generic);
1443
1444 stack_machine.reset();
1445 program.clearRetainingCapacity();
1446 try b.writeConst(writer, u16, 4096);
1447 try b.writePlusUconst(writer, @as(usize, 8192));
1448 _ = try stack_machine.run(program.items, allocator, context, null);
1449 try testing.expectEqual(@as(usize, 4096 + 8192), stack_machine.stack.popOrNull().?.generic);
1450
1451 stack_machine.reset();
1452 program.clearRetainingCapacity();
1453 try b.writeConst(writer, u16, 0xfff);
1454 try b.writeConst(writer, u16, 1);
1455 try b.writeOpcode(writer, OP.shl);
1456 _ = try stack_machine.run(program.items, allocator, context, null);
1457 try testing.expectEqual(@as(usize, 0xfff << 1), stack_machine.stack.popOrNull().?.generic);
1458
1459 stack_machine.reset();
1460 program.clearRetainingCapacity();
1461 try b.writeConst(writer, u16, 0xfff);
1462 try b.writeConst(writer, u16, 1);
1463 try b.writeOpcode(writer, OP.shr);
1464 _ = try stack_machine.run(program.items, allocator, context, null);
1465 try testing.expectEqual(@as(usize, 0xfff >> 1), stack_machine.stack.popOrNull().?.generic);
1466
1467 stack_machine.reset();
1468 program.clearRetainingCapacity();
1469 try b.writeConst(writer, u16, 0xfff);
1470 try b.writeConst(writer, u16, 1);
1471 try b.writeOpcode(writer, OP.shr);
1472 _ = try stack_machine.run(program.items, allocator, context, null);
1473 try testing.expectEqual(@as(usize, @bitCast(@as(isize, 0xfff) >> 1)), stack_machine.stack.popOrNull().?.generic);
1474
1475 stack_machine.reset();
1476 program.clearRetainingCapacity();
1477 try b.writeConst(writer, u16, 0xf0ff);
1478 try b.writeConst(writer, u16, 0xff0f);
1479 try b.writeOpcode(writer, OP.xor);
1480 _ = try stack_machine.run(program.items, allocator, context, null);
1481 try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
1482 }
1483
1484 // Control Flow Operations
1485 {
1486 const context = ExpressionContext{};
1487 const expected = .{
1488 .{ OP.le, 1, 1, 0 },
1489 .{ OP.ge, 1, 0, 1 },
1490 .{ OP.eq, 1, 0, 0 },
1491 .{ OP.lt, 0, 1, 0 },
1492 .{ OP.gt, 0, 0, 1 },
1493 .{ OP.ne, 0, 1, 1 },
1494 };
1495
1496 inline for (expected) |e| {
1497 stack_machine.reset();
1498 program.clearRetainingCapacity();
1499
1500 try b.writeConst(writer, u16, 0);
1501 try b.writeConst(writer, u16, 0);
1502 try b.writeOpcode(writer, e[0]);
1503 try b.writeConst(writer, u16, 0);
1504 try b.writeConst(writer, u16, 1);
1505 try b.writeOpcode(writer, e[0]);
1506 try b.writeConst(writer, u16, 1);
1507 try b.writeConst(writer, u16, 0);
1508 try b.writeOpcode(writer, e[0]);
1509 _ = try stack_machine.run(program.items, allocator, context, null);
1510 try testing.expectEqual(@as(usize, e[3]), stack_machine.stack.popOrNull().?.generic);
1511 try testing.expectEqual(@as(usize, e[2]), stack_machine.stack.popOrNull().?.generic);
1512 try testing.expectEqual(@as(usize, e[1]), stack_machine.stack.popOrNull().?.generic);
1513 }
1514
1515 stack_machine.reset();
1516 program.clearRetainingCapacity();
1517 try b.writeLiteral(writer, 2);
1518 try b.writeSkip(writer, 1);
1519 try b.writeLiteral(writer, 3);
1520 _ = try stack_machine.run(program.items, allocator, context, null);
1521 try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic);
1522
1523 stack_machine.reset();
1524 program.clearRetainingCapacity();
1525 try b.writeLiteral(writer, 2);
1526 try b.writeBra(writer, 1);
1527 try b.writeLiteral(writer, 3);
1528 try b.writeLiteral(writer, 0);
1529 try b.writeBra(writer, 1);
1530 try b.writeLiteral(writer, 4);
1531 try b.writeLiteral(writer, 5);
1532 _ = try stack_machine.run(program.items, allocator, context, null);
1533 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1534 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1535 try testing.expect(stack_machine.stack.popOrNull() == null);
1536
1537 // TODO: Test call2, call4, call_ref once implemented
1538
1539 }
1540
1541 // Type conversions
1542 {
1543 const context = ExpressionContext{};
1544 stack_machine.reset();
1545 program.clearRetainingCapacity();
1546
1547 // TODO: Test typed OP.convert once implemented
1548
1549 const value: usize = @truncate(0xffeeffee_ffeeffee);
1550 var value_bytes: [options.addr_size]u8 = undefined;
1551 mem.writeInt(usize, &value_bytes, value, native_endian);
1552
1553 // Convert to generic type
1554 stack_machine.reset();
1555 program.clearRetainingCapacity();
1556 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1557 try b.writeConvert(writer, @as(usize, 0));
1558 _ = try stack_machine.run(program.items, allocator, context, null);
1559 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
1560
1561 // Reinterpret to generic type
1562 stack_machine.reset();
1563 program.clearRetainingCapacity();
1564 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1565 try b.writeReinterpret(writer, @as(usize, 0));
1566 _ = try stack_machine.run(program.items, allocator, context, null);
1567 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
1568
1569 // Reinterpret to new type
1570 const die_offset: usize = 0xffee;
1571
1572 stack_machine.reset();
1573 program.clearRetainingCapacity();
1574 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1575 try b.writeReinterpret(writer, die_offset);
1576 _ = try stack_machine.run(program.items, allocator, context, null);
1577 const const_type = stack_machine.stack.popOrNull().?.const_type;
1578 try testing.expectEqual(die_offset, const_type.type_offset);
1579
1580 stack_machine.reset();
1581 program.clearRetainingCapacity();
1582 try b.writeLiteral(writer, 0);
1583 try b.writeReinterpret(writer, die_offset);
1584 _ = try stack_machine.run(program.items, allocator, context, null);
1585 const regval_type = stack_machine.stack.popOrNull().?.regval_type;
1586 try testing.expectEqual(die_offset, regval_type.type_offset);
1587 }
1588
1589 // Special operations
1590 {
1591 var context = ExpressionContext{};
1592
1593 stack_machine.reset();
1594 program.clearRetainingCapacity();
1595 try b.writeOpcode(writer, OP.nop);
1596 _ = try stack_machine.run(program.items, allocator, context, null);
1597 try testing.expect(stack_machine.stack.popOrNull() == null);
1598
1599 // Sub-expression
1600 {
1601 var sub_program = std.ArrayList(u8).init(allocator);
1602 defer sub_program.deinit();
1603 const sub_writer = sub_program.writer();
1604 try b.writeLiteral(sub_writer, 3);
1605
1606 stack_machine.reset();
1607 program.clearRetainingCapacity();
1608 try b.writeEntryValue(writer, sub_program.items);
1609 _ = try stack_machine.run(program.items, allocator, context, null);
1610 try testing.expectEqual(@as(usize, 3), stack_machine.stack.popOrNull().?.generic);
1611 }
1612
1613 // Register location description
1614 const reg_context = abi.RegisterContext{
1615 .eh_frame = true,
1616 .is_macho = builtin.os.tag == .macos,
1617 };
1618 var thread_context: std.debug.ThreadContext = undefined;
1619 std.debug.relocateContext(&thread_context);
1620 context = ExpressionContext{
1621 .thread_context = &thread_context,
1622 .reg_context = reg_context,
1623 };
1624
1625 if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
1626 mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1627
1628 var sub_program = std.ArrayList(u8).init(allocator);
1629 defer sub_program.deinit();
1630 const sub_writer = sub_program.writer();
1631 try b.writeReg(sub_writer, 0);
1632
1633 stack_machine.reset();
1634 program.clearRetainingCapacity();
1635 try b.writeEntryValue(writer, sub_program.items);
1636 _ = try stack_machine.run(program.items, allocator, context, null);
1637 try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.popOrNull().?.generic);
1638 } else |err| {
1639 switch (err) {
1640 error.UnimplementedArch,
1641 error.UnimplementedOs,
1642 error.ThreadContextNotSupported,
1643 => {},
1644 else => return err,
1645 }
1646 }
1647 }
1648}
src/target.zig+1-1
......@@ -327,7 +327,7 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {
327327}
328328
329329pub fn needUnwindTables(target: std.Target) bool {
330 return target.os.tag == .windows or target.isDarwin() or std.dwarf.abi.supportsUnwinding(target);
330 return target.os.tag == .windows or target.isDarwin() or std.debug.Dwarf.abi.supportsUnwinding(target);
331331}
332332
333333pub fn defaultAddressSpace(