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 {
628628
629629 // TODO: Unwind using __unwind_info,
630630 unreachable;
631
632631 },
633632 else => {},
634633 }
lib/std/dwarf.zig+9-4
......@@ -1592,6 +1592,7 @@ pub const DwarfInfo = struct {
15921592 entry_header.entry_bytes,
15931593 -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))),
15941594 true,
1595 entry_header.is_64,
15951596 frame_section,
15961597 entry_header.length_offset,
15971598 @sizeOf(usize),
......@@ -1674,6 +1675,7 @@ pub const DwarfInfo = struct {
16741675 }
16751676
16761677 var expression_context = .{
1678 .is_64 = cie.is_64,
16771679 .isValidMemory = context.isValidMemory,
16781680 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
16791681 .thread_context = context.thread_context,
......@@ -2042,6 +2044,7 @@ pub const ExceptionFrameHeader = struct {
20422044 cie_entry_header.entry_bytes,
20432045 0,
20442046 true,
2047 cie_entry_header.is_64,
20452048 .eh_frame,
20462049 cie_entry_header.length_offset,
20472050 @sizeOf(usize),
......@@ -2135,8 +2138,8 @@ pub const CommonInformationEntry = struct {
21352138 // This is the key that FDEs use to reference CIEs.
21362139 length_offset: u64,
21372140 version: u8,
2138
21392141 address_size: u8,
2142 is_64: bool,
21402143
21412144 // Only present in version 4
21422145 segment_selector_size: ?u8,
......@@ -2175,11 +2178,12 @@ pub const CommonInformationEntry = struct {
21752178 /// of `pc_rel_offset` and `is_runtime`.
21762179 ///
21772180 /// `length_offset` specifies the offset of this CIE's length field in the
2178 /// .eh_frame / .debug_framesection.
2181 /// .eh_frame / .debug_frame section.
21792182 pub fn parse(
21802183 cie_bytes: []const u8,
21812184 pc_rel_offset: i64,
21822185 is_runtime: bool,
2186 is_64: bool,
21832187 dwarf_section: DwarfSection,
21842188 length_offset: u64,
21852189 addr_size_bytes: u8,
......@@ -2280,6 +2284,7 @@ pub const CommonInformationEntry = struct {
22802284 .length_offset = length_offset,
22812285 .version = version,
22822286 .address_size = address_size,
2287 .is_64 = is_64,
22832288 .segment_selector_size = segment_selector_size,
22842289 .code_alignment_factor = code_alignment_factor,
22852290 .data_alignment_factor = data_alignment_factor,
......@@ -2316,8 +2321,8 @@ pub const FrameDescriptionEntry = struct {
23162321 /// where the section is currently stored in memory, to where it *would* be
23172322 /// stored at runtime: section runtime offset - backing section data base ptr.
23182323 ///
2319 /// Similarly, `is_runtime` specifies this function is being called on a runtime section, and so
2320 /// indirect pointers can be followed.
2324 /// Similarly, `is_runtime` specifies this function is being called on a runtime
2325 /// section, and so indirect pointers can be followed.
23212326 pub fn parse(
23222327 fde_bytes: []const u8,
23232328 pc_rel_offset: i64,
lib/std/dwarf/abi.zig+12-1
......@@ -59,12 +59,23 @@ pub const RegisterContext = struct {
5959 is_macho: bool,
6060};
6161
62pub const AbiError = error{
63 InvalidRegister,
64 UnimplementedArch,
65 UnimplementedOs,
66 ThreadContextNotSupported,
67};
68
6269/// Returns a slice containing the backing storage for `reg_number`.
6370///
6471/// `reg_context` describes in what context the register number is used, as it can have different
6572/// meanings depending on the DWARF container. It is only required when getting the stack or
6673/// 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)) {
6879 if (builtin.os.tag == .windows) {
6980 return switch (builtin.cpu.arch) {
7081 .x86 => switch (reg_number) {
lib/std/dwarf/expressions.zig+169-52
......@@ -11,6 +11,9 @@ const assert = std.debug.assert;
1111/// Callers should specify all the fields relevant to their context. If a field is required
1212/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
1313pub const ExpressionContext = struct {
14 /// This expression is from a DWARF64 section
15 is_64: bool = false,
16
1417 /// If specified, any addresses will pass through this function before being
1518 isValidMemory: ?*const fn (address: usize) bool = null,
1619
......@@ -29,6 +32,9 @@ pub const ExpressionContext = struct {
2932
3033 /// Call frame address, if in a CFI context
3134 cfa: ?usize = null,
35
36 /// This expression is a sub-expression from an OP.entry_value instruction
37 entry_value_context: bool = false,
3238};
3339
3440pub const ExpressionOptions = struct {
......@@ -42,6 +48,28 @@ pub const ExpressionOptions = struct {
4248 call_frame_context: bool = false,
4349};
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
4573/// A stack machine that can decode and run DWARF expressions.
4674/// Expressions can be decoded for non-native address size and endianness,
4775/// but can only be executed if the current target matches the configuration.
......@@ -156,12 +184,14 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
156184 }
157185 }
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 {
160188 const reader = stream.reader();
161189 return switch (opcode) {
162 OP.addr,
163 OP.call_ref,
164 => generic(try reader.readInt(addr_type, options.endian)),
190 OP.addr => generic(try reader.readInt(addr_type, options.endian)),
191 OP.call_ref => if (context.is_64)
192 generic(try reader.readInt(u64, options.endian))
193 else
194 generic(try reader.readInt(u32, options.endian)),
165195 OP.const1u,
166196 OP.pick,
167197 => generic(try reader.readByte()),
......@@ -267,7 +297,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
267297 allocator: std.mem.Allocator,
268298 context: ExpressionContext,
269299 initial_value: ?usize,
270 ) !?Value {
300 ) ExpressionError!?Value {
271301 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
272302 var stream = std.io.fixedBufferStream(expression);
273303 while (try self.step(&stream, allocator, context)) {}
......@@ -281,12 +311,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
281311 stream: *std.io.FixedBufferStream([]const u8),
282312 allocator: std.mem.Allocator,
283313 context: ExpressionContext,
284 ) !bool {
314 ) ExpressionError!bool {
285315 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
286316 @compileError("Execution of non-native address sizes / endianness is not supported");
287317
288318 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;
290320 switch (opcode) {
291321
292322 // 2.5.1.1: Literal Encodings
......@@ -302,10 +332,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
302332 OP.const8s,
303333 OP.constu,
304334 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
307337 OP.const_type => {
308 const const_type = (try readOperand(stream, opcode)).?.const_type;
338 const const_type = (try readOperand(stream, opcode, context)).?.const_type;
309339 try self.stack.append(allocator, .{ .const_type = .{
310340 .type_offset = const_type.type_offset,
311341 .value_bytes = const_type.value_bytes,
......@@ -315,9 +345,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
315345 OP.addrx,
316346 OP.constx,
317347 => {
318 if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;
319 if (context.debug_addr == null) return error.ExpressionRequiresDebugAddr;
320 const debug_addr_index = (try readOperand(stream, opcode)).?.generic;
348 if (context.compile_unit == null) return error.IncompleteExpressionContext;
349 if (context.debug_addr == null) return error.IncompleteExpressionContext;
350 const debug_addr_index = (try readOperand(stream, opcode, context)).?.generic;
321351 const offset = context.compile_unit.?.addr_base + debug_addr_index;
322352 if (offset >= context.debug_addr.?.len) return error.InvalidExpression;
323353 const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]);
......@@ -326,10 +356,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
326356
327357 // 2.5.1.2: Register Values
328358 OP.fbreg => {
329 if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;
330 if (context.compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
359 if (context.compile_unit == null) return error.IncompleteExpressionContext;
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);
333363 _ = offset;
334364
335365 switch (context.compile_unit.?.frame_base.?.*) {
......@@ -353,7 +383,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
353383 => {
354384 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;
357387 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
358388 context.thread_context.?,
359389 base_register.base_register,
......@@ -363,7 +393,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
363393 try self.stack.append(allocator, .{ .generic = @intCast(value) });
364394 },
365395 OP.regval_type => {
366 const register_type = (try readOperand(stream, opcode)).?.register_type;
396 const register_type = (try readOperand(stream, opcode, context)).?.register_type;
367397 const value = mem.readIntSliceNative(usize, try abi.regBytes(
368398 context.thread_context.?,
369399 register_type.register,
......@@ -387,7 +417,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
387417 _ = self.stack.pop();
388418 },
389419 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;
391421 if (stack_index >= self.stack.items.len) return error.InvalidExpression;
392422 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
393423 },
......@@ -429,7 +459,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
429459
430460 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);
433463 const size = switch (opcode) {
434464 OP.deref,
435465 OP.xderef,
......@@ -469,11 +499,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
469499 }
470500 },
471501 OP.push_object_address => {
472 if (context.object_address == null) return error.IncompleteExpressionContext;
473 try self.stack.append(allocator, .{ .generic = @intFromPtr(context.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 }
474508 },
475509 OP.form_tls_address => {
476 return error.UnimplementedExpressionOpcode;
510 return error.UnimplementedOpcode;
477511 },
478512 OP.call_frame_cfa => {
479513 if (context.cfa) |cfa| {
......@@ -559,7 +593,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
559593 },
560594 OP.plus_uconst => {
561595 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;
563597 self.stack.items[self.stack.items.len - 1] = .{
564598 .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
565599 };
......@@ -628,7 +662,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
628662 }
629663 },
630664 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;
632666 const condition = if (opcode == OP.bra) blk: {
633667 if (self.stack.items.len == 0) return error.InvalidExpression;
634668 break :blk try self.stack.pop().asIntegral() != 0;
......@@ -648,7 +682,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
648682 OP.call4,
649683 OP.call_ref,
650684 => {
651 const debug_info_offset = (try readOperand(stream, opcode)).?.generic;
685 const debug_info_offset = (try readOperand(stream, opcode, context)).?.generic;
652686 _ = debug_info_offset;
653687
654688 // 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 {
661695 // 2.5.1.6: Type Conversions
662696 OP.convert => {
663697 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
666700 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
667701 const value = self.stack.items[self.stack.items.len - 1];
......@@ -675,7 +709,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
675709 },
676710 OP.reinterpret => {
677711 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
680714 // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
681715 const value = self.stack.items[self.stack.items.len - 1];
......@@ -710,15 +744,29 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
710744 // 2.5.1.7: Special Operations
711745 OP.nop => {},
712746 OP.entry_value => {
713 const block = (try readOperand(stream, opcode)).?.block;
714 _ = block;
747 const block = (try readOperand(stream, opcode, context)).?.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.
717 // TODO: If block is a register location, push the value that location had before running this program onto this stack.
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
754 if (isOpcodeRegisterLocation(block[0])) {
755 if (context.thread_context == null) return error.IncompleteExpressionContext;
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 }
722770 },
723771
724772 // These have already been handled by readOperand
......@@ -745,7 +793,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
745793 return struct {
746794 /// Zero-operand instructions
747795 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;
749797 switch (opcode) {
750798 OP.dup,
751799 OP.drop,
......@@ -778,6 +826,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
778826 OP.gt,
779827 OP.ne,
780828 OP.nop,
829 OP.stack_value,
781830 => try writer.writeByte(opcode),
782831 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
783832 }
......@@ -828,12 +877,12 @@ pub fn Builder(comptime options: ExpressionOptions) type {
828877 try leb.writeULEB128(writer, debug_addr_offset);
829878 }
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 {
832881 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;
834883 try writer.writeByte(OP.const_type);
835884 try leb.writeULEB128(writer, die_offset);
836 try writer.writeByte(size);
885 try writer.writeByte(@intCast(value_bytes.len));
837886 try writer.writeAll(value_bytes);
838887 }
839888
......@@ -932,10 +981,10 @@ pub fn Builder(comptime options: ExpressionOptions) type {
932981 try writer.writeInt(T, offset, options.endian);
933982 }
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 {
936985 if (options.call_frame_context) return error.InvalidCFAOpcode;
937986 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);
939988 }
940989
941990 pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
......@@ -959,13 +1008,29 @@ pub fn Builder(comptime options: ExpressionOptions) type {
9591008 }
9601009
9611010 // 2.6: Location Descriptions
962 // TODO
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 // pub fn writeImplicitPointer(writer: anytype, ) void {
1027 // }
9631028
9641029 };
9651030}
9661031
9671032// Certain opcodes are not allowed in a CFA context, see 6.4.2
968fn opcodeValidInCFA(opcode: u8) bool {
1033fn isOpcodeValidInCFA(opcode: u8) bool {
9691034 return switch (opcode) {
9701035 OP.addrx,
9711036 OP.call2,
......@@ -984,6 +1049,13 @@ fn opcodeValidInCFA(opcode: u8) bool {
9841049 };
9851050}
9861051
1052fn isOpcodeRegisterLocation(opcode: u8) bool {
1053 return switch (opcode) {
1054 OP.reg0...OP.reg31, OP.regx => true,
1055 else => false,
1056 };
1057}
1058
9871059const testing = std.testing;
9881060test "DWARF expressions" {
9891061 const allocator = std.testing.allocator;
......@@ -1067,7 +1139,7 @@ test "DWARF expressions" {
10671139
10681140 const die_offset: usize = @truncate(0xaabbccdd);
10691141 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
10721144 _ = try stack_machine.run(program.items, allocator, context, 0);
10731145
......@@ -1137,7 +1209,13 @@ test "DWARF expressions" {
11371209 try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
11381210 try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
11391211 } 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 }
11411219 }
11421220 }
11431221
......@@ -1396,7 +1474,6 @@ test "DWARF expressions" {
13961474 try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
13971475 }
13981476
1399
14001477 // Control Flow Operations
14011478 {
14021479 var context = ExpressionContext{};
......@@ -1436,7 +1513,6 @@ test "DWARF expressions" {
14361513 _ = try stack_machine.run(program.items, allocator, context, null);
14371514 try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic);
14381515
1439
14401516 stack_machine.reset();
14411517 program.clearRetainingCapacity();
14421518 try b.writeLiteral(writer, 2);
......@@ -1470,7 +1546,7 @@ test "DWARF expressions" {
14701546 // Convert to generic type
14711547 stack_machine.reset();
14721548 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);
14741550 try b.writeConvert(writer, @as(usize, 0));
14751551 _ = try stack_machine.run(program.items, allocator, context, null);
14761552 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
......@@ -1478,7 +1554,7 @@ test "DWARF expressions" {
14781554 // Reinterpret to generic type
14791555 stack_machine.reset();
14801556 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);
14821558 try b.writeReinterpret(writer, @as(usize, 0));
14831559 _ = try stack_machine.run(program.items, allocator, context, null);
14841560 try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
......@@ -1488,7 +1564,7 @@ test "DWARF expressions" {
14881564
14891565 stack_machine.reset();
14901566 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);
14921568 try b.writeReinterpret(writer, die_offset);
14931569 _ = try stack_machine.run(program.items, allocator, context, null);
14941570 const const_type = stack_machine.stack.popOrNull().?.const_type;
......@@ -1506,18 +1582,59 @@ test "DWARF expressions" {
15061582 // Special operations
15071583 {
15081584 var context = ExpressionContext{};
1585
15091586 stack_machine.reset();
15101587 program.clearRetainingCapacity();
1511
15121588 try b.writeOpcode(writer, OP.nop);
15131589 _ = try stack_machine.run(program.items, allocator, context, null);
15141590 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 }
15221640}
1523