Commit 54ca62fef4

kcbanner <kcbanner@gmail.com>
2023-07-08 00:37:10
dwarf: fixup regBytes for the case where there is no context support expressions: add more tests, fix tests for mipsel debug: add lookupModuleName implementation for macos
1 parent 5c0d4ce
lib/std/dwarf/abi.zig
@@ -110,11 +110,14 @@ pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?Regis
                 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
                 31 => mem.asBytes(&thread_context_ptr.Sp),
                 32 => mem.asBytes(&thread_context_ptr.Pc),
+                else => error.InvalidRegister,
             },
             else => error.UnimplementedArch,
         };
     }
 
+    if (!std.debug.have_ucontext) return error.ThreadContextNotSupported;
+
     const ucontext_ptr = thread_context_ptr;
     var m = &ucontext_ptr.mcontext;
     return switch (builtin.cpu.arch) {
lib/std/dwarf/call_frame.zig
@@ -322,19 +322,22 @@ pub const VirtualMachine = struct {
                 .expression => |expression| {
                     context.stack_machine.reset();
                     const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
+                    const addr = if (value) |v| blk: {
+                        if (v != .generic) return error.InvalidExpressionValue;
+                        break :blk v.generic;
+                    } else return error.NoExpressionValue;
 
-                    if (value != .generic) return error.InvalidExpressionValue;
-                    if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;
-
-                    const ptr: *usize = @ptrFromInt(value.generic);
+                    if (!context.isValidMemory(addr)) return error.InvalidExpressionAddress;
+                    const ptr: *usize = @ptrFromInt(addr);
                     mem.writeIntSliceNative(usize, out, ptr.*);
                 },
                 .val_expression => |expression| {
                     context.stack_machine.reset();
                     const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
-
-                    if (value != .generic) return error.InvalidExpressionValue;
-                    mem.writeIntSliceNative(usize, out, value.generic);
+                    if (value) |v| {
+                        if (v != .generic) return error.InvalidExpressionValue;
+                        mem.writeIntSliceNative(usize, out, v.generic);
+                    } else return error.NoExpressionValue;
                 },
                 .architectural => return error.UnimplementedRegisterRule,
             }
lib/std/dwarf/expressions.zig
@@ -263,12 +263,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
             expression: []const u8,
             allocator: std.mem.Allocator,
             context: ExpressionContext,
-            initial_value: usize,
-        ) !Value {
-            try self.stack.append(allocator, .{ .generic = initial_value });
+            initial_value: ?usize,
+        ) !?Value {
+            if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
             var stream = std.io.fixedBufferStream(expression);
             while (try self.step(&stream, allocator, context)) {}
-            if (self.stack.items.len == 0) return error.InvalidExpression;
+            if (self.stack.items.len == 0) return null;
             return self.stack.items[self.stack.items.len - 1];
         }
 
@@ -412,7 +412,11 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                         OP.xderef,
                         OP.xderef_size,
                         OP.xderef_type,
-                        => try self.stack.pop().asIntegral(),
+                        => blk: {
+                            _ = self.stack.pop();
+                            if (self.stack.items.len == 0) return error.InvalidExpression;
+                            break :blk try self.stack.items[self.stack.items.len - 1].asIntegral();
+                        },
                         else => null,
                     };
 
@@ -424,7 +428,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
 
                     const operand = try readOperand(stream, opcode);
                     const size = switch (opcode) {
-                        OP.deref => @sizeOf(addr_type),
+                        OP.deref,
+                        OP.xderef,
+                        => @sizeOf(addr_type),
                         OP.deref_size,
                         OP.xderef_size,
                         => operand.?.type_size,
@@ -442,16 +448,21 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                         else => return error.InvalidExpression,
                     })) orelse return error.InvalidExpression;
 
-                    if (opcode == OP.deref_type) {
-                        self.stack.items[self.stack.items.len - 1] = .{
-                            .regval_type = .{
-                                .type_offset = operand.?.deref_type.type_offset,
-                                .type_size = operand.?.deref_type.size,
-                                .value = value,
-                            },
-                        };
-                    } else {
-                        self.stack.items[self.stack.items.len - 1] = .{ .generic = value };
+                    switch (opcode) {
+                        OP.deref_type,
+                        OP.xderef_type,
+                        => {
+                            self.stack.items[self.stack.items.len - 1] = .{
+                                .regval_type = .{
+                                    .type_offset = operand.?.deref_type.type_offset,
+                                    .type_size = operand.?.deref_type.size,
+                                    .value = value,
+                                },
+                            };
+                        },
+                        else => {
+                            self.stack.items[self.stack.items.len - 1] = .{ .generic = value };
+                        },
                     }
                 },
                 OP.push_object_address,
@@ -759,7 +770,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
                 OP.ne,
                 OP.nop,
                 => try writer.writeByte(opcode),
-                else => @compileError("This opcode requires operands, use write<Opcode>() instead"),
+                else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
             }
         }
 
@@ -836,7 +847,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
 
         pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {
             if (register > 31) return error.InvalidRegister;
-            try writer.writeByte(OP.reg0 + register);
+            try writer.writeByte(OP.breg0 + register);
             try leb.writeILEB128(writer, offset);
         }
 
@@ -848,7 +859,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
 
         pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {
             if (options.call_frame_context) return error.InvalidCFAOpcode;
-            try writer.writeByte(OP.bregx);
+            try writer.writeByte(OP.regval_type);
             try leb.writeULEB128(writer, register);
             try leb.writeULEB128(writer, offset);
         }
@@ -996,35 +1007,36 @@ test "DWARF expressions" {
 
     // Constants
     {
+        stack_machine.reset();
         program.clearRetainingCapacity();
 
-        const expected = [_]comptime_int{
+        const input = [_]comptime_int{
             1,
             -1,
-            0x0fff,
-            -0x0fff,
-            0x0fffffff,
-            -0x0fffffff,
-            0x0fffffffffffffff,
-            -0x0fffffffffffffff,
-            0x8000000,
-            -0x8000000,
+            @as(usize, @truncate(0x0fff)),
+            @as(isize, @truncate(-0x0fff)),
+            @as(usize, @truncate(0x0fffffff)),
+            @as(isize, @truncate(-0x0fffffff)),
+            @as(usize, @truncate(0x0fffffffffffffff)),
+            @as(isize, @truncate(-0x0fffffffffffffff)),
+            @as(usize, @truncate(0x8000000)),
+            @as(isize, @truncate(-0x8000000)),
             @as(usize, @truncate(0x12345678_12345678)),
             @as(usize, @truncate(0xffffffff_ffffffff)),
             @as(usize, @truncate(0xeeeeeeee_eeeeeeee)),
         };
 
-        try b.writeConst(writer, u8, expected[0]);
-        try b.writeConst(writer, i8, expected[1]);
-        try b.writeConst(writer, u16, expected[2]);
-        try b.writeConst(writer, i16, expected[3]);
-        try b.writeConst(writer, u32, expected[4]);
-        try b.writeConst(writer, i32, expected[5]);
-        try b.writeConst(writer, u64, expected[6]);
-        try b.writeConst(writer, i64, expected[7]);
-        try b.writeConst(writer, u28, expected[8]);
-        try b.writeConst(writer, i28, expected[9]);
-        try b.writeAddr(writer, expected[10]);
+        try b.writeConst(writer, u8, input[0]);
+        try b.writeConst(writer, i8, input[1]);
+        try b.writeConst(writer, u16, input[2]);
+        try b.writeConst(writer, i16, input[3]);
+        try b.writeConst(writer, u32, input[4]);
+        try b.writeConst(writer, i32, input[5]);
+        try b.writeConst(writer, u64, input[6]);
+        try b.writeConst(writer, i64, input[7]);
+        try b.writeConst(writer, u28, input[8]);
+        try b.writeConst(writer, i28, input[9]);
+        try b.writeAddr(writer, input[10]);
 
         var mock_compile_unit: dwarf.CompileUnit = undefined;
         mock_compile_unit.addr_base = 1;
@@ -1033,8 +1045,8 @@ test "DWARF expressions" {
         defer mock_debug_addr.deinit();
 
         try mock_debug_addr.writer().writeIntNative(u16, 0);
-        try mock_debug_addr.writer().writeIntNative(usize, expected[11]);
-        try mock_debug_addr.writer().writeIntNative(usize, expected[12]);
+        try mock_debug_addr.writer().writeIntNative(usize, input[11]);
+        try mock_debug_addr.writer().writeIntNative(usize, input[12]);
 
         const context = ExpressionContext{
             .compile_unit = &mock_compile_unit,
@@ -1054,26 +1066,311 @@ test "DWARF expressions" {
         try testing.expectEqual(die_offset, const_type.type_offset);
         try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes);
 
-        try testing.expectEqual(@as(usize, expected[12]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, expected[11]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, expected[10]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(isize, @truncate(expected[9])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, @truncate(expected[8])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(isize, @truncate(expected[7])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, @truncate(expected[6])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(isize, @truncate(expected[5])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, @truncate(expected[4])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(isize, @truncate(expected[3])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, @truncate(expected[2])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(isize, @truncate(expected[1])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
-        try testing.expectEqual(@as(usize, @truncate(expected[0])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
+        const expected = .{
+            .{ usize, input[12], usize },
+            .{ usize, input[11], usize },
+            .{ usize, input[10], usize },
+            .{ isize, input[9], isize },
+            .{ usize, input[8], usize },
+            .{ isize, input[7], isize },
+            .{ usize, input[6], usize },
+            .{ isize, input[5], isize },
+            .{ usize, input[4], usize },
+            .{ isize, input[3], isize },
+            .{ usize, input[2], usize },
+            .{ isize, input[1], isize },
+            .{ usize, input[0], usize },
+        };
+
+        inline for (expected) |e| {
+            try testing.expectEqual(@as(e[0], e[1]), @as(e[2], @bitCast(stack_machine.stack.popOrNull().?.generic)));
+        }
     }
 
     // Register values
-    if (@TypeOf(std.debug.ThreadContext) != void) {
+    if (@sizeOf(std.debug.ThreadContext) != 0) {
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+
+        const reg_context = abi.RegisterContext{
+            .eh_frame = true,
+            .is_macho = builtin.os.tag == .macos,
+        };
         var thread_context: std.debug.ThreadContext = undefined;
-        _ = thread_context;
+        const context = ExpressionContext{
+            .thread_context = &thread_context,
+            .reg_context = reg_context,
+        };
+
+        // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
+
+        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee);
+        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1);
+        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2);
+        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3);
 
-        // TODO: Test fbreg, breg0..31, bregx, regval_type
+        try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
+        try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
+        try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
+        try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
+
+        _ = try stack_machine.run(program.items, allocator, context, 0);
+
+        const regval_type = stack_machine.stack.popOrNull().?.regval_type;
+        try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
+        try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
+        try testing.expectEqual(@as(usize, 0xee), regval_type.value);
+
+        try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
+    }
+
+    // Stack operations
+    {
+        var context = ExpressionContext{};
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u8, 1);
+        try b.writeOpcode(writer, OP.dup);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u8, 1);
+        try b.writeOpcode(writer, OP.drop);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expect(stack_machine.stack.popOrNull() == null);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u8, 4);
+        try b.writeConst(writer, u8, 5);
+        try b.writeConst(writer, u8, 6);
+        try b.writePick(writer, 2);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u8, 4);
+        try b.writeConst(writer, u8, 5);
+        try b.writeConst(writer, u8, 6);
+        try b.writeOpcode(writer, OP.over);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u8, 5);
+        try b.writeConst(writer, u8, 6);
+        try b.writeOpcode(writer, OP.swap);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u8, 4);
+        try b.writeConst(writer, u8, 5);
+        try b.writeConst(writer, u8, 6);
+        try b.writeOpcode(writer, OP.rot);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
+
+        const deref_target: usize = @truncate(0xffeeffee_ffeeffee);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeAddr(writer, @intFromPtr(&deref_target));
+        try b.writeOpcode(writer, OP.deref);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeLiteral(writer, 0);
+        try b.writeAddr(writer, @intFromPtr(&deref_target));
+        try b.writeOpcode(writer, OP.xderef);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeAddr(writer, @intFromPtr(&deref_target));
+        try b.writeDerefSize(writer, 1);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeLiteral(writer, 0);
+        try b.writeAddr(writer, @intFromPtr(&deref_target));
+        try b.writeXDerefSize(writer, 1);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
+
+        const type_offset: usize = @truncate(0xaabbaabb_aabbaabb);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeAddr(writer, @intFromPtr(&deref_target));
+        try b.writeDerefType(writer, 1, type_offset);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        const deref_type = stack_machine.stack.popOrNull().?.regval_type;
+        try testing.expectEqual(type_offset, deref_type.type_offset);
+        try testing.expectEqual(@as(u8, 1), deref_type.type_size);
+        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), deref_type.value);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeLiteral(writer, 0);
+        try b.writeAddr(writer, @intFromPtr(&deref_target));
+        try b.writeXDerefType(writer, 1, type_offset);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        const xderef_type = stack_machine.stack.popOrNull().?.regval_type;
+        try testing.expectEqual(type_offset, xderef_type.type_offset);
+        try testing.expectEqual(@as(u8, 1), xderef_type.type_size);
+        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value);
+
+        // TODO: Test OP.push_object_address
+        // TODO: Test OP.form_tls_address
+
+        context.cfa = @truncate(0xccddccdd_ccddccdd);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeOpcode(writer, OP.call_frame_cfa);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(context.cfa.?, stack_machine.stack.popOrNull().?.generic);
+    }
+
+    // Arithmetic and Logical Operations
+    {
+        var context = ExpressionContext{};
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, i16, -4096);
+        try b.writeOpcode(writer, OP.abs);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 4096), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xff0f);
+        try b.writeConst(writer, u16, 0xf0ff);
+        try b.writeOpcode(writer, OP.@"and");
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 0xf00f), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, i16, -404);
+        try b.writeConst(writer, i16, 100);
+        try b.writeOpcode(writer, OP.div);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(isize, -404 / 100), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 200);
+        try b.writeConst(writer, u16, 50);
+        try b.writeOpcode(writer, OP.minus);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 150), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 123);
+        try b.writeConst(writer, u16, 100);
+        try b.writeOpcode(writer, OP.mod);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 23), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xff);
+        try b.writeConst(writer, u16, 0xee);
+        try b.writeOpcode(writer, OP.mul);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 0xed12), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 5);
+        try b.writeOpcode(writer, OP.neg);
+        try b.writeConst(writer, i16, -6);
+        try b.writeOpcode(writer, OP.neg);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
+        try testing.expectEqual(@as(isize, -5), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xff0f);
+        try b.writeOpcode(writer, OP.not);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(~@as(usize, 0xff0f), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xff0f);
+        try b.writeConst(writer, u16, 0xf0ff);
+        try b.writeOpcode(writer, OP.@"or");
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 0xffff), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, i16, 402);
+        try b.writeConst(writer, i16, 100);
+        try b.writeOpcode(writer, OP.plus);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 502), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 4096);
+        try b.writePlusUconst(writer, @as(usize, 8192));
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 4096 + 8192), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xfff);
+        try b.writeConst(writer, u16, 1);
+        try b.writeOpcode(writer, OP.shl);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 0xfff << 1), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xfff);
+        try b.writeConst(writer, u16, 1);
+        try b.writeOpcode(writer, OP.shr);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 0xfff >> 1), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xfff);
+        try b.writeConst(writer, u16, 1);
+        try b.writeOpcode(writer, OP.shr);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, @bitCast(@as(isize, 0xfff) >> 1)), stack_machine.stack.popOrNull().?.generic);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConst(writer, u16, 0xf0ff);
+        try b.writeConst(writer, u16, 0xff0f);
+        try b.writeOpcode(writer, OP.xor);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
     }
 }
lib/std/debug.zig
@@ -1518,10 +1518,10 @@ pub const DebugInfo = struct {
 
     // Returns the module name for a given address.
     // This can be called when getModuleForAddress fails, so implementations should provide
-    // a path that doesn't rely on any side-effects of successful module lookup.
+    // a path that doesn't rely on any side-effects of a prior successful module lookup.
     pub fn getModuleNameForAddress(self: *DebugInfo, address: usize) ?[]const u8 {
         if (comptime builtin.target.isDarwin()) {
-            return null;
+            return self.lookupModuleNameDyld(address);
         } else if (native_os == .windows) {
             return self.lookupModuleNameWin32(address);
         } else if (native_os == .haiku) {
@@ -1590,6 +1590,44 @@ pub const DebugInfo = struct {
         return error.MissingDebugInfo;
     }
 
+    fn lookupModuleNameDyld(self: *DebugInfo, address: usize) ?[]const u8 {
+        _ = self;
+        const image_count = std.c._dyld_image_count();
+
+        var i: u32 = 0;
+        while (i < image_count) : (i += 1) {
+            const header = std.c._dyld_get_image_header(i) orelse continue;
+            const base_address = @intFromPtr(header);
+            if (address < base_address) continue;
+            const vmaddr_slide = std.c._dyld_get_image_vmaddr_slide(i);
+
+            var it = macho.LoadCommandIterator{
+                .ncmds = header.ncmds,
+                .buffer = @alignCast(@as(
+                    [*]u8,
+                    @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)),
+                )[0..header.sizeofcmds]),
+            };
+
+            while (it.next()) |cmd| switch (cmd.cmd()) {
+                .SEGMENT_64 => {
+                    const segment_cmd = cmd.cast(macho.segment_command_64).?;
+                    if (!mem.eql(u8, "__TEXT", segment_cmd.segName())) continue;
+
+                    const original_address = address - vmaddr_slide;
+                    const seg_start = segment_cmd.vmaddr;
+                    const seg_end = seg_start + segment_cmd.vmsize;
+                    if (original_address >= seg_start and original_address < seg_end) {
+                        return mem.sliceTo(std.c._dyld_get_image_name(i), 0);
+                    }
+                },
+                else => {},
+            };
+        }
+
+        return null;
+    }
+
     fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
         for (self.modules.items) |*module| {
             if (address >= module.base_address and address < module.base_address + module.size) {
lib/std/dwarf.zig
@@ -1699,11 +1699,13 @@ pub const DwarfInfo = struct {
                     expression,
                     context.allocator,
                     expression_context,
-                    context.cfa orelse 0,
+                    context.cfa,
                 );
 
-                if (value != .generic) return error.InvalidExpressionValue;
-                break :blk value.generic;
+                if (value) |v| {
+                    if (v != .generic) return error.InvalidExpressionValue;
+                    break :blk v.generic;
+                } else return error.NoExpressionValue;
             },
             else => return error.InvalidCFARule,
         };