authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-08 02:52:42-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
logd226b74ae8a408ca6d363295e00fdc2876d77fb0
treec17b60b625a22b072837957147454cbee8c0f413
parent21d0154139fbbbb218dc7734ba0cd44b5baf2876

dwarf: add ExpressionError to work around the compiler not being able to infer it

dwarf: implement OP.entry_value, add tests

4 files changed, 190 insertions(+), 58 deletions(-)

lib/std/debug.zig-1
...@@ -628,7 +628,6 @@ pub const StackIterator = struct {...@@ -628,7 +628,6 @@ pub const StackIterator = struct {
628628
629 // TODO: Unwind using __unwind_info,629 // TODO: Unwind using __unwind_info,
630 unreachable;630 unreachable;
631
632 },631 },
633 else => {},632 else => {},
634 }633 }
lib/std/dwarf.zig+9-4
...@@ -1592,6 +1592,7 @@ pub const DwarfInfo = struct {...@@ -1592,6 +1592,7 @@ pub const DwarfInfo = struct {
1592 entry_header.entry_bytes,1592 entry_header.entry_bytes,
1593 -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))),1593 -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))),
1594 true,1594 true,
1595 entry_header.is_64,
1595 frame_section,1596 frame_section,
1596 entry_header.length_offset,1597 entry_header.length_offset,
1597 @sizeOf(usize),1598 @sizeOf(usize),
...@@ -1674,6 +1675,7 @@ pub const DwarfInfo = struct {...@@ -1674,6 +1675,7 @@ pub const DwarfInfo = struct {
1674 }1675 }
16751676
1676 var expression_context = .{1677 var expression_context = .{
1678 .is_64 = cie.is_64,
1677 .isValidMemory = context.isValidMemory,1679 .isValidMemory = context.isValidMemory,
1678 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,1680 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1679 .thread_context = context.thread_context,1681 .thread_context = context.thread_context,
...@@ -2042,6 +2044,7 @@ pub const ExceptionFrameHeader = struct {...@@ -2042,6 +2044,7 @@ pub const ExceptionFrameHeader = struct {
2042 cie_entry_header.entry_bytes,2044 cie_entry_header.entry_bytes,
2043 0,2045 0,
2044 true,2046 true,
2047 cie_entry_header.is_64,
2045 .eh_frame,2048 .eh_frame,
2046 cie_entry_header.length_offset,2049 cie_entry_header.length_offset,
2047 @sizeOf(usize),2050 @sizeOf(usize),
...@@ -2135,8 +2138,8 @@ pub const CommonInformationEntry = struct {...@@ -2135,8 +2138,8 @@ pub const CommonInformationEntry = struct {
2135 // This is the key that FDEs use to reference CIEs.2138 // This is the key that FDEs use to reference CIEs.
2136 length_offset: u64,2139 length_offset: u64,
2137 version: u8,2140 version: u8,
2138
2139 address_size: u8,2141 address_size: u8,
2142 is_64: bool,
21402143
2141 // Only present in version 42144 // Only present in version 4
2142 segment_selector_size: ?u8,2145 segment_selector_size: ?u8,
...@@ -2175,11 +2178,12 @@ pub const CommonInformationEntry = struct {...@@ -2175,11 +2178,12 @@ pub const CommonInformationEntry = struct {
2175 /// of `pc_rel_offset` and `is_runtime`.2178 /// of `pc_rel_offset` and `is_runtime`.
2176 ///2179 ///
2177 /// `length_offset` specifies the offset of this CIE's length field in the2180 /// `length_offset` specifies the offset of this CIE's length field in the
2178 /// .eh_frame / .debug_framesection.2181 /// .eh_frame / .debug_frame section.
2179 pub fn parse(2182 pub fn parse(
2180 cie_bytes: []const u8,2183 cie_bytes: []const u8,
2181 pc_rel_offset: i64,2184 pc_rel_offset: i64,
2182 is_runtime: bool,2185 is_runtime: bool,
2186 is_64: bool,
2183 dwarf_section: DwarfSection,2187 dwarf_section: DwarfSection,
2184 length_offset: u64,2188 length_offset: u64,
2185 addr_size_bytes: u8,2189 addr_size_bytes: u8,
...@@ -2280,6 +2284,7 @@ pub const CommonInformationEntry = struct {...@@ -2280,6 +2284,7 @@ pub const CommonInformationEntry = struct {
2280 .length_offset = length_offset,2284 .length_offset = length_offset,
2281 .version = version,2285 .version = version,
2282 .address_size = address_size,2286 .address_size = address_size,
2287 .is_64 = is_64,
2283 .segment_selector_size = segment_selector_size,2288 .segment_selector_size = segment_selector_size,
2284 .code_alignment_factor = code_alignment_factor,2289 .code_alignment_factor = code_alignment_factor,
2285 .data_alignment_factor = data_alignment_factor,2290 .data_alignment_factor = data_alignment_factor,
...@@ -2316,8 +2321,8 @@ pub const FrameDescriptionEntry = struct {...@@ -2316,8 +2321,8 @@ pub const FrameDescriptionEntry = struct {
2316 /// where the section is currently stored in memory, to where it *would* be2321 /// where the section is currently stored in memory, to where it *would* be
2317 /// stored at runtime: section runtime offset - backing section data base ptr.2322 /// stored at runtime: section runtime offset - backing section data base ptr.
2318 ///2323 ///
2319 /// Similarly, `is_runtime` specifies this function is being called on a runtime section, and so2324 /// Similarly, `is_runtime` specifies this function is being called on a runtime
2320 /// indirect pointers can be followed.2325 /// section, and so indirect pointers can be followed.
2321 pub fn parse(2326 pub fn parse(
2322 fde_bytes: []const u8,2327 fde_bytes: []const u8,
2323 pc_rel_offset: i64,2328 pc_rel_offset: i64,
lib/std/dwarf/abi.zig+12-1
...@@ -59,12 +59,23 @@ pub const RegisterContext = struct {...@@ -59,12 +59,23 @@ pub const RegisterContext = struct {
59 is_macho: bool,59 is_macho: bool,
60};60};
6161
62pub const AbiError = error{
63 InvalidRegister,
64 UnimplementedArch,
65 UnimplementedOs,
66 ThreadContextNotSupported,
67};
68
62/// Returns a slice containing the backing storage for `reg_number`.69/// Returns a slice containing the backing storage for `reg_number`.
63///70///
64/// `reg_context` describes in what context the register number is used, as it can have different71/// `reg_context` describes in what context the register number is used, as it can have different
65/// meanings depending on the DWARF container. It is only required when getting the stack or72/// meanings depending on the DWARF container. It is only required when getting the stack or
66/// frame pointer register on some architectures.73/// frame pointer register on some architectures.
67pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) {74pub fn regBytes(
75 thread_context_ptr: anytype,
76 reg_number: u8,
77 reg_context: ?RegisterContext,
78) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
68 if (builtin.os.tag == .windows) {79 if (builtin.os.tag == .windows) {
69 return switch (builtin.cpu.arch) {80 return switch (builtin.cpu.arch) {
70 .x86 => switch (reg_number) {81 .x86 => switch (reg_number) {
lib/std/dwarf/expressions.zig+169-52
...@@ -11,6 +11,9 @@ const assert = std.debug.assert;...@@ -11,6 +11,9 @@ const assert = std.debug.assert;
11/// Callers should specify all the fields relevant to their context. If a field is required11/// 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.12/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
13pub const ExpressionContext = struct {13pub const ExpressionContext = struct {
14 /// This expression is from a DWARF64 section
15 is_64: bool = false,
16
14 /// If specified, any addresses will pass through this function before being17 /// If specified, any addresses will pass through this function before being
15 isValidMemory: ?*const fn (address: usize) bool = null,18 isValidMemory: ?*const fn (address: usize) bool = null,
1619
...@@ -29,6 +32,9 @@ pub const ExpressionContext = struct {...@@ -29,6 +32,9 @@ pub const ExpressionContext = struct {
2932
30 /// Call frame address, if in a CFI context33 /// Call frame address, if in a CFI context
31 cfa: ?usize = null,34 cfa: ?usize = null,
35
36 /// This expression is a sub-expression from an OP.entry_value instruction
37 entry_value_context: bool = false,
32};38};
3339
34pub const ExpressionOptions = struct {40pub const ExpressionOptions = struct {
...@@ -42,6 +48,28 @@ pub const ExpressionOptions = struct {...@@ -42,6 +48,28 @@ pub const ExpressionOptions = struct {
42 call_frame_context: bool = false,48 call_frame_context: bool = false,
43};49};
4450
51pub const ExpressionError = error{
52 UnimplementedExpressionCall,
53 UnimplementedOpcode,
54 UnimplementedUserOpcode,
55 UnimplementedTypedComparison,
56 UnimplementedTypeConversion,
57
58 UnknownExpressionOpcode,
59
60 IncompleteExpressionContext,
61
62 InvalidCFAOpcode,
63 InvalidExpression,
64 InvalidFrameBase,
65 InvalidIntegralTypeSize,
66 InvalidRegister,
67 InvalidSubExpression,
68 InvalidTypeLength,
69
70 TruncatedIntegralType,
71} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
72
45/// A stack machine that can decode and run DWARF expressions.73/// A stack machine that can decode and run DWARF expressions.
46/// Expressions can be decoded for non-native address size and endianness,74/// Expressions can be decoded for non-native address size and endianness,
47/// but can only be executed if the current target matches the configuration.75/// but can only be executed if the current target matches the configuration.
...@@ -156,12 +184,14 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -156,12 +184,14 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
156 }184 }
157 }185 }
158186
159 pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Operand {187 pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8, context: ExpressionContext) !?Operand {
160 const reader = stream.reader();188 const reader = stream.reader();
161 return switch (opcode) {189 return switch (opcode) {
162 OP.addr,190 OP.addr => generic(try reader.readInt(addr_type, options.endian)),
163 OP.call_ref,191 OP.call_ref => if (context.is_64)
164 => generic(try reader.readInt(addr_type, options.endian)),192 generic(try reader.readInt(u64, options.endian))
193 else
194 generic(try reader.readInt(u32, options.endian)),
165 OP.const1u,195 OP.const1u,
166 OP.pick,196 OP.pick,
167 => generic(try reader.readByte()),197 => generic(try reader.readByte()),
...@@ -267,7 +297,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -267,7 +297,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
267 allocator: std.mem.Allocator,297 allocator: std.mem.Allocator,
268 context: ExpressionContext,298 context: ExpressionContext,
269 initial_value: ?usize,299 initial_value: ?usize,
270 ) !?Value {300 ) ExpressionError!?Value {
271 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });301 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
272 var stream = std.io.fixedBufferStream(expression);302 var stream = std.io.fixedBufferStream(expression);
273 while (try self.step(&stream, allocator, context)) {}303 while (try self.step(&stream, allocator, context)) {}
...@@ -281,12 +311,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -281,12 +311,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
281 stream: *std.io.FixedBufferStream([]const u8),311 stream: *std.io.FixedBufferStream([]const u8),
282 allocator: std.mem.Allocator,312 allocator: std.mem.Allocator,
283 context: ExpressionContext,313 context: ExpressionContext,
284 ) !bool {314 ) ExpressionError!bool {
285 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())315 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
286 @compileError("Execution of non-native address sizes / endianness is not supported");316 @compileError("Execution of non-native address sizes / endianness is not supported");
287317
288 const opcode = try stream.reader().readByte();318 const opcode = try stream.reader().readByte();
289 if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;319 if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
290 switch (opcode) {320 switch (opcode) {
291321
292 // 2.5.1.1: Literal Encodings322 // 2.5.1.1: Literal Encodings
...@@ -302,10 +332,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -302,10 +332,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
302 OP.const8s,332 OP.const8s,
303 OP.constu,333 OP.constu,
304 OP.consts,334 OP.consts,
305 => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode)).?.generic }),335 => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode, context)).?.generic }),
306336
307 OP.const_type => {337 OP.const_type => {
308 const const_type = (try readOperand(stream, opcode)).?.const_type;338 const const_type = (try readOperand(stream, opcode, context)).?.const_type;
309 try self.stack.append(allocator, .{ .const_type = .{339 try self.stack.append(allocator, .{ .const_type = .{
310 .type_offset = const_type.type_offset,340 .type_offset = const_type.type_offset,
311 .value_bytes = const_type.value_bytes,341 .value_bytes = const_type.value_bytes,
...@@ -315,9 +345,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -315,9 +345,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
315 OP.addrx,345 OP.addrx,
316 OP.constx,346 OP.constx,
317 => {347 => {
318 if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;348 if (context.compile_unit == null) return error.IncompleteExpressionContext;
319 if (context.debug_addr == null) return error.ExpressionRequiresDebugAddr;349 if (context.debug_addr == null) return error.IncompleteExpressionContext;
320 const debug_addr_index = (try readOperand(stream, opcode)).?.generic;350 const debug_addr_index = (try readOperand(stream, opcode, context)).?.generic;
321 const offset = context.compile_unit.?.addr_base + debug_addr_index;351 const offset = context.compile_unit.?.addr_base + debug_addr_index;
322 if (offset >= context.debug_addr.?.len) return error.InvalidExpression;352 if (offset >= context.debug_addr.?.len) return error.InvalidExpression;
323 const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]);353 const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]);
...@@ -326,10 +356,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -326,10 +356,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
326356
327 // 2.5.1.2: Register Values357 // 2.5.1.2: Register Values
328 OP.fbreg => {358 OP.fbreg => {
329 if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;359 if (context.compile_unit == null) return error.IncompleteExpressionContext;
330 if (context.compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;360 if (context.compile_unit.?.frame_base == null) return error.IncompleteExpressionContext;
331361
332 const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);362 const offset: i64 = @intCast((try readOperand(stream, opcode, context)).?.generic);
333 _ = offset;363 _ = offset;
334364
335 switch (context.compile_unit.?.frame_base.?.*) {365 switch (context.compile_unit.?.frame_base.?.*) {
...@@ -353,7 +383,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -353,7 +383,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
353 => {383 => {
354 if (context.thread_context == null) return error.IncompleteExpressionContext;384 if (context.thread_context == null) return error.IncompleteExpressionContext;
355385
356 const base_register = (try readOperand(stream, opcode)).?.base_register;386 const base_register = (try readOperand(stream, opcode, context)).?.base_register;
357 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(387 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
358 context.thread_context.?,388 context.thread_context.?,
359 base_register.base_register,389 base_register.base_register,
...@@ -363,7 +393,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -363,7 +393,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
363 try self.stack.append(allocator, .{ .generic = @intCast(value) });393 try self.stack.append(allocator, .{ .generic = @intCast(value) });
364 },394 },
365 OP.regval_type => {395 OP.regval_type => {
366 const register_type = (try readOperand(stream, opcode)).?.register_type;396 const register_type = (try readOperand(stream, opcode, context)).?.register_type;
367 const value = mem.readIntSliceNative(usize, try abi.regBytes(397 const value = mem.readIntSliceNative(usize, try abi.regBytes(
368 context.thread_context.?,398 context.thread_context.?,
369 register_type.register,399 register_type.register,
...@@ -387,7 +417,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -387,7 +417,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
387 _ = self.stack.pop();417 _ = self.stack.pop();
388 },418 },
389 OP.pick, OP.over => {419 OP.pick, OP.over => {
390 const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode)).?.generic;420 const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode, context)).?.generic;
391 if (stack_index >= self.stack.items.len) return error.InvalidExpression;421 if (stack_index >= self.stack.items.len) return error.InvalidExpression;
392 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);422 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
393 },423 },
...@@ -429,7 +459,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -429,7 +459,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
429459
430 if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression;460 if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression;
431461
432 const operand = try readOperand(stream, opcode);462 const operand = try readOperand(stream, opcode, context);
433 const size = switch (opcode) {463 const size = switch (opcode) {
434 OP.deref,464 OP.deref,
435 OP.xderef,465 OP.xderef,
...@@ -469,11 +499,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -469,11 +499,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
469 }499 }
470 },500 },
471 OP.push_object_address => {501 OP.push_object_address => {
472 if (context.object_address == null) return error.IncompleteExpressionContext;502 // In sub-expressions, `push_object_address` is not meaningful (as per the
473 try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });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 }
474 },508 },
475 OP.form_tls_address => {509 OP.form_tls_address => {
476 return error.UnimplementedExpressionOpcode;510 return error.UnimplementedOpcode;
477 },511 },
478 OP.call_frame_cfa => {512 OP.call_frame_cfa => {
479 if (context.cfa) |cfa| {513 if (context.cfa) |cfa| {
...@@ -559,7 +593,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -559,7 +593,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
559 },593 },
560 OP.plus_uconst => {594 OP.plus_uconst => {
561 if (self.stack.items.len == 0) return error.InvalidExpression;595 if (self.stack.items.len == 0) return error.InvalidExpression;
562 const constant = (try readOperand(stream, opcode)).?.generic;596 const constant = (try readOperand(stream, opcode, context)).?.generic;
563 self.stack.items[self.stack.items.len - 1] = .{597 self.stack.items[self.stack.items.len - 1] = .{
564 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),598 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
565 };599 };
...@@ -628,7 +662,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -628,7 +662,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
628 }662 }
629 },663 },
630 OP.skip, OP.bra => {664 OP.skip, OP.bra => {
631 const branch_offset = (try readOperand(stream, opcode)).?.branch_offset;665 const branch_offset = (try readOperand(stream, opcode, context)).?.branch_offset;
632 const condition = if (opcode == OP.bra) blk: {666 const condition = if (opcode == OP.bra) blk: {
633 if (self.stack.items.len == 0) return error.InvalidExpression;667 if (self.stack.items.len == 0) return error.InvalidExpression;
634 break :blk try self.stack.pop().asIntegral() != 0;668 break :blk try self.stack.pop().asIntegral() != 0;
...@@ -648,7 +682,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -648,7 +682,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
648 OP.call4,682 OP.call4,
649 OP.call_ref,683 OP.call_ref,
650 => {684 => {
651 const debug_info_offset = (try readOperand(stream, opcode)).?.generic;685 const debug_info_offset = (try readOperand(stream, opcode, context)).?.generic;
652 _ = debug_info_offset;686 _ = debug_info_offset;
653687
654 // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it688 // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it
...@@ -661,7 +695,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -661,7 +695,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
661 // 2.5.1.6: Type Conversions695 // 2.5.1.6: Type Conversions
662 OP.convert => {696 OP.convert => {
663 if (self.stack.items.len == 0) return error.InvalidExpression;697 if (self.stack.items.len == 0) return error.InvalidExpression;
664 const type_offset = (try readOperand(stream, opcode)).?.generic;698 const type_offset = (try readOperand(stream, opcode, context)).?.generic;
665699
666 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size700 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
667 const value = self.stack.items[self.stack.items.len - 1];701 const value = self.stack.items[self.stack.items.len - 1];
...@@ -675,7 +709,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -675,7 +709,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
675 },709 },
676 OP.reinterpret => {710 OP.reinterpret => {
677 if (self.stack.items.len == 0) return error.InvalidExpression;711 if (self.stack.items.len == 0) return error.InvalidExpression;
678 const type_offset = (try readOperand(stream, opcode)).?.generic;712 const type_offset = (try readOperand(stream, opcode, context)).?.generic;
679713
680 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size714 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
681 const value = self.stack.items[self.stack.items.len - 1];715 const value = self.stack.items[self.stack.items.len - 1];
...@@ -710,15 +744,29 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -710,15 +744,29 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
710 // 2.5.1.7: Special Operations744 // 2.5.1.7: Special Operations
711 OP.nop => {},745 OP.nop => {},
712 OP.entry_value => {746 OP.entry_value => {
713 const block = (try readOperand(stream, opcode)).?.block;747 const block = (try readOperand(stream, opcode, context)).?.block;
714 _ = 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.
715753
716 // TODO: If block is an expression, run it on a new stack. Push the resulting value onto this stack.754 if (isOpcodeRegisterLocation(block[0])) {
717 // TODO: If block is a register location, push the value that location had before running this program onto this stack.755 if (context.thread_context == null) return error.IncompleteExpressionContext;
718 // This implies capturing all register values before executing this block, in case this program modifies them.
719 // TODO: If the block contains, OP.push_object_address, treat it as OP.nop
720756
721 return error.UnimplementedSubExpression;757 var block_stream = std.io.fixedBufferStream(block);
758 const register = (try readOperand(&block_stream, block[0], context)).?.register;
759 const value = mem.readIntSliceNative(usize, try abi.regBytes(context.thread_context.?, register, context.reg_context));
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 }
722 },770 },
723771
724 // These have already been handled by readOperand772 // These have already been handled by readOperand
...@@ -745,7 +793,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -745,7 +793,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
745 return struct {793 return struct {
746 /// Zero-operand instructions794 /// Zero-operand instructions
747 pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {795 pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {
748 if (options.call_frame_context and !comptime opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;796 if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
749 switch (opcode) {797 switch (opcode) {
750 OP.dup,798 OP.dup,
751 OP.drop,799 OP.drop,
...@@ -778,6 +826,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -778,6 +826,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
778 OP.gt,826 OP.gt,
779 OP.ne,827 OP.ne,
780 OP.nop,828 OP.nop,
829 OP.stack_value,
781 => try writer.writeByte(opcode),830 => try writer.writeByte(opcode),
782 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),831 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
783 }832 }
...@@ -828,12 +877,12 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -828,12 +877,12 @@ pub fn Builder(comptime options: ExpressionOptions) type {
828 try leb.writeULEB128(writer, debug_addr_offset);877 try leb.writeULEB128(writer, debug_addr_offset);
829 }878 }
830879
831 pub fn writeConstType(writer: anytype, die_offset: anytype, size: u8, value_bytes: []const u8) !void {880 pub fn writeConstType(writer: anytype, die_offset: anytype, value_bytes: []const u8) !void {
832 if (options.call_frame_context) return error.InvalidCFAOpcode;881 if (options.call_frame_context) return error.InvalidCFAOpcode;
833 if (size != value_bytes.len) return error.InvalidValueSize;882 if (value_bytes.len > 0xff) return error.InvalidTypeLength;
834 try writer.writeByte(OP.const_type);883 try writer.writeByte(OP.const_type);
835 try leb.writeULEB128(writer, die_offset);884 try leb.writeULEB128(writer, die_offset);
836 try writer.writeByte(size);885 try writer.writeByte(@intCast(value_bytes.len));
837 try writer.writeAll(value_bytes);886 try writer.writeAll(value_bytes);
838 }887 }
839888
...@@ -932,10 +981,10 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -932,10 +981,10 @@ pub fn Builder(comptime options: ExpressionOptions) type {
932 try writer.writeInt(T, offset, options.endian);981 try writer.writeInt(T, offset, options.endian);
933 }982 }
934983
935 pub fn writeCallRef(writer: anytype, debug_info_offset: addr_type) !void {984 pub fn writeCallRef(writer: anytype, comptime is_64: bool, value: if (is_64) u64 else u32) !void {
936 if (options.call_frame_context) return error.InvalidCFAOpcode;985 if (options.call_frame_context) return error.InvalidCFAOpcode;
937 try writer.writeByte(OP.call_ref);986 try writer.writeByte(OP.call_ref);
938 try writer.writeInt(addr_type, debug_info_offset, options.endian);987 try writer.writeInt(if (is_64) u64 else u32, value, options.endian);
939 }988 }
940989
941 pub fn writeConvert(writer: anytype, die_offset: anytype) !void {990 pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
...@@ -959,13 +1008,29 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -959,13 +1008,29 @@ pub fn Builder(comptime options: ExpressionOptions) type {
959 }1008 }
9601009
961 // 2.6: Location Descriptions1010 // 2.6: Location Descriptions
962 // TODO1011 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 // pub fn writeImplicitPointer(writer: anytype, ) void {
1027 // }
9631028
964 };1029 };
965}1030}
9661031
967// Certain opcodes are not allowed in a CFA context, see 6.4.21032// Certain opcodes are not allowed in a CFA context, see 6.4.2
968fn opcodeValidInCFA(opcode: u8) bool {1033fn isOpcodeValidInCFA(opcode: u8) bool {
969 return switch (opcode) {1034 return switch (opcode) {
970 OP.addrx,1035 OP.addrx,
971 OP.call2,1036 OP.call2,
...@@ -984,6 +1049,13 @@ fn opcodeValidInCFA(opcode: u8) bool {...@@ -984,6 +1049,13 @@ fn opcodeValidInCFA(opcode: u8) bool {
984 };1049 };
985}1050}
9861051
1052fn isOpcodeRegisterLocation(opcode: u8) bool {
1053 return switch (opcode) {
1054 OP.reg0...OP.reg31, OP.regx => true,
1055 else => false,
1056 };
1057}
1058
987const testing = std.testing;1059const testing = std.testing;
988test "DWARF expressions" {1060test "DWARF expressions" {
989 const allocator = std.testing.allocator;1061 const allocator = std.testing.allocator;
...@@ -1067,7 +1139,7 @@ test "DWARF expressions" {...@@ -1067,7 +1139,7 @@ test "DWARF expressions" {
10671139
1068 const die_offset: usize = @truncate(0xaabbccdd);1140 const die_offset: usize = @truncate(0xaabbccdd);
1069 const type_bytes: []const u8 = &.{ 1, 2, 3, 4 };1141 const type_bytes: []const u8 = &.{ 1, 2, 3, 4 };
1070 try b.writeConstType(writer, die_offset, type_bytes.len, type_bytes);1142 try b.writeConstType(writer, die_offset, type_bytes);
10711143
1072 _ = try stack_machine.run(program.items, allocator, context, 0);1144 _ = try stack_machine.run(program.items, allocator, context, 0);
10731145
...@@ -1137,7 +1209,13 @@ test "DWARF expressions" {...@@ -1137,7 +1209,13 @@ test "DWARF expressions" {
1137 try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);1209 try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
1138 try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);1210 try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
1139 } else |err| {1211 } else |err| {
1140 if (err != error.UnimplementedArch and err != error.UnimplementedOs) return err;1212 switch (err) {
1213 error.UnimplementedArch,
1214 error.UnimplementedOs,
1215 error.ThreadContextNotSupported,
1216 => {},
1217 else => return err,
1218 }
1141 }1219 }
1142 }1220 }
11431221
...@@ -1396,7 +1474,6 @@ test "DWARF expressions" {...@@ -1396,7 +1474,6 @@ test "DWARF expressions" {
1396 try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);1474 try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
1397 }1475 }
13981476
1399
1400 // Control Flow Operations1477 // Control Flow Operations
1401 {1478 {
1402 var context = ExpressionContext{};1479 var context = ExpressionContext{};
...@@ -1436,7 +1513,6 @@ test "DWARF expressions" {...@@ -1436,7 +1513,6 @@ test "DWARF expressions" {
1436 _ = try stack_machine.run(program.items, allocator, context, null);1513 _ = try stack_machine.run(program.items, allocator, context, null);
1437 try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic);1514 try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic);
14381515
1439
1440 stack_machine.reset();1516 stack_machine.reset();
1441 program.clearRetainingCapacity();1517 program.clearRetainingCapacity();
1442 try b.writeLiteral(writer, 2);1518 try b.writeLiteral(writer, 2);
...@@ -1470,7 +1546,7 @@ test "DWARF expressions" {...@@ -1470,7 +1546,7 @@ test "DWARF expressions" {
1470 // Convert to generic type1546 // Convert to generic type
1471 stack_machine.reset();1547 stack_machine.reset();
1472 program.clearRetainingCapacity();1548 program.clearRetainingCapacity();
1473 try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);1549 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1474 try b.writeConvert(writer, @as(usize, 0));1550 try b.writeConvert(writer, @as(usize, 0));
1475 _ = try stack_machine.run(program.items, allocator, context, null);1551 _ = try stack_machine.run(program.items, allocator, context, null);
1476 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);1552 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
...@@ -1478,7 +1554,7 @@ test "DWARF expressions" {...@@ -1478,7 +1554,7 @@ test "DWARF expressions" {
1478 // Reinterpret to generic type1554 // Reinterpret to generic type
1479 stack_machine.reset();1555 stack_machine.reset();
1480 program.clearRetainingCapacity();1556 program.clearRetainingCapacity();
1481 try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);1557 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1482 try b.writeReinterpret(writer, @as(usize, 0));1558 try b.writeReinterpret(writer, @as(usize, 0));
1483 _ = try stack_machine.run(program.items, allocator, context, null);1559 _ = try stack_machine.run(program.items, allocator, context, null);
1484 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);1560 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
...@@ -1488,7 +1564,7 @@ test "DWARF expressions" {...@@ -1488,7 +1564,7 @@ test "DWARF expressions" {
14881564
1489 stack_machine.reset();1565 stack_machine.reset();
1490 program.clearRetainingCapacity();1566 program.clearRetainingCapacity();
1491 try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);1567 try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1492 try b.writeReinterpret(writer, die_offset);1568 try b.writeReinterpret(writer, die_offset);
1493 _ = try stack_machine.run(program.items, allocator, context, null);1569 _ = try stack_machine.run(program.items, allocator, context, null);
1494 const const_type = stack_machine.stack.popOrNull().?.const_type;1570 const const_type = stack_machine.stack.popOrNull().?.const_type;
...@@ -1506,18 +1582,59 @@ test "DWARF expressions" {...@@ -1506,18 +1582,59 @@ test "DWARF expressions" {
1506 // Special operations1582 // Special operations
1507 {1583 {
1508 var context = ExpressionContext{};1584 var context = ExpressionContext{};
1585
1509 stack_machine.reset();1586 stack_machine.reset();
1510 program.clearRetainingCapacity();1587 program.clearRetainingCapacity();
1511
1512 try b.writeOpcode(writer, OP.nop);1588 try b.writeOpcode(writer, OP.nop);
1513 _ = try stack_machine.run(program.items, allocator, context, null);1589 _ = try stack_machine.run(program.items, allocator, context, null);
1514 try testing.expect(stack_machine.stack.popOrNull() == null);1590 try testing.expect(stack_machine.stack.popOrNull() == null);
15151591
1592 // Sub-expression
1593 {
1594 var sub_program = std.ArrayList(u8).init(allocator);
1595 defer sub_program.deinit();
1596 const sub_writer = sub_program.writer();
1597 try b.writeLiteral(sub_writer, 3);
15161598
1599 stack_machine.reset();
1600 program.clearRetainingCapacity();
1601 try b.writeEntryValue(writer, sub_program.items);
1602 _ = try stack_machine.run(program.items, allocator, context, null);
1603 try testing.expectEqual(@as(usize, 3), stack_machine.stack.popOrNull().?.generic);
1604 }
15171605
1606 // Register location description
1607 const reg_context = abi.RegisterContext{
1608 .eh_frame = true,
1609 .is_macho = builtin.os.tag == .macos,
1610 };
1611 var thread_context: std.debug.ThreadContext = undefined;
1612 context = ExpressionContext{
1613 .thread_context = &thread_context,
1614 .reg_context = reg_context,
1615 };
15181616
1519 }1617 if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
1618 mem.writeIntSliceNative(usize, reg_bytes, 0xee);
15201619
1620 var sub_program = std.ArrayList(u8).init(allocator);
1621 defer sub_program.deinit();
1622 const sub_writer = sub_program.writer();
1623 try b.writeReg(sub_writer, 0);
15211624
1625 stack_machine.reset();
1626 program.clearRetainingCapacity();
1627 try b.writeEntryValue(writer, sub_program.items);
1628 _ = try stack_machine.run(program.items, allocator, context, null);
1629 try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.popOrNull().?.generic);
1630 } else |err| {
1631 switch (err) {
1632 error.UnimplementedArch,
1633 error.UnimplementedOs,
1634 error.ThreadContextNotSupported,
1635 => {},
1636 else => return err,
1637 }
1638 }
1639 }
1522}1640}
1523