Commit b85f84061a

kcbanner <kcbanner@gmail.com>
2023-07-03 09:45:06
dwarf: don't dupe function names, as they are backed by the memory mapped sections dwarf: const-correctness fixups dwarf: implement the remaining register rules dwarf: start implmenting the DWARF expression stack machine
1 parent 62598c2
lib/std/dwarf/call_frame.zig
@@ -183,9 +183,9 @@ pub const Instruction = union(Opcode) {
     offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
     def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
     def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }),
-    val_offset: InstructionType(.{ .a = .uleb128_offset, .b = .uleb128_offset }),
-    val_offset_sf: InstructionType(.{ .a = .uleb128_offset, .b = .sleb128_offset }),
-    val_expression: InstructionType(.{ .a = .uleb128_offset, .block = .block }),
+    val_offset: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
+    val_offset_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
+    val_expression: InstructionType(.{ .register = .uleb128_register, .block = .block }),
 
     fn readOperands(
         self: *Instruction,
@@ -292,7 +292,14 @@ pub const VirtualMachine = struct {
         rule: RegisterRule = .{ .default = {} },
 
         /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
-        pub fn resolveValue(self: Column, context: dwarf.UnwindContext, out: []u8) !void {
+        pub fn resolveValue(
+            self: Column,
+            context: *dwarf.UnwindContext,
+            compile_unit: ?*const dwarf.CompileUnit,
+            ucontext: *const std.os.ucontext_t,
+            reg_ctx: abi.RegisterContext,
+            out: []u8,
+        ) !void {
             switch (self.rule) {
                 .default => {
                     const register = self.register orelse return error.InvalidRegister;
@@ -321,14 +328,21 @@ pub const VirtualMachine = struct {
                     @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
                 },
                 .expression => |expression| {
-                    // TODO
-                    _ = expression;
-                    unreachable;
+                    context.stack_machine.reset();
+                    const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?);
+
+                    if (value != .generic) return error.InvalidExpressionValue;
+                    if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;
+
+                    const ptr: *usize = @ptrFromInt(value.generic);
+                    mem.writeIntSliceNative(usize, out, ptr.*);
                 },
                 .val_expression => |expression| {
-                    // TODO
-                    _ = expression;
-                    unreachable;
+                    context.stack_machine.reset();
+                    const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?);
+
+                    if (value != .generic) return error.InvalidExpressionValue;
+                    mem.writeIntSliceNative(usize, out, value.generic);
                 },
                 .architectural => return error.UnimplementedRule,
             }
@@ -546,12 +560,16 @@ pub const VirtualMachine = struct {
             .def_cfa_offset => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
-                self.current_row.cfa.rule = .{ .val_offset = @intCast(i.operands.offset) };
+                self.current_row.cfa.rule = .{
+                    .val_offset = @intCast(i.operands.offset),
+                };
             },
             .def_cfa_offset_sf => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
-                self.current_row.cfa.rule = .{ .val_offset = i.operands.offset * cie.data_alignment_factor };
+                self.current_row.cfa.rule = .{
+                    .val_offset = i.operands.offset * cie.data_alignment_factor,
+                };
             },
             .def_cfa_expression => |i| {
                 try self.resolveCopyOnWrite(allocator);
@@ -567,17 +585,26 @@ pub const VirtualMachine = struct {
                     .expression = i.operands.block,
                 };
             },
-            .val_offset => {
-                // TODO: Implement
-                unreachable;
+            .val_offset => |i| {
+                try self.resolveCopyOnWrite(allocator);
+                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                column.rule = .{
+                    .val_offset = @as(i64, @intCast(i.operands.offset)) * cie.data_alignment_factor,
+                };
             },
-            .val_offset_sf => {
-                // TODO: Implement
-                unreachable;
+            .val_offset_sf => |i| {
+                try self.resolveCopyOnWrite(allocator);
+                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                column.rule = .{
+                    .val_offset = i.operands.offset * cie.data_alignment_factor,
+                };
             },
-            .val_expression => {
-                // TODO: Implement
-                unreachable;
+            .val_expression => |i| {
+                try self.resolveCopyOnWrite(allocator);
+                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                column.rule = .{
+                    .val_expression = i.operands.block,
+                };
             },
         }
 
lib/std/dwarf/expressions.zig
@@ -2,6 +2,9 @@ const std = @import("std");
 const builtin = @import("builtin");
 const OP = @import("OP.zig");
 const leb = @import("../leb128.zig");
+const dwarf = @import("../dwarf.zig");
+const abi = dwarf.abi;
+const mem = std.mem;
 
 pub const StackMachineOptions = struct {
     /// The address size of the target architecture
@@ -33,9 +36,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
     };
 
     return struct {
-        const Value = union(enum) {
+        const Self = @This();
+
+        const Operand = union(enum) {
             generic: addr_type,
-            const_type: []const u8,
             register: u8,
             base_register: struct {
                 base_register: u8,
@@ -46,7 +50,11 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
                 offset: i64,
             },
             block: []const u8,
-            base_type: struct {
+            register_type: struct {
+                register: u8,
+                type_offset: u64,
+            },
+            const_type: struct {
                 type_offset: u64,
                 value_bytes: []const u8,
             },
@@ -56,9 +64,31 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
             },
         };
 
+        const Value = union(enum) {
+            generic: addr_type,
+            regval_type: struct {
+                // Offset of DW_TAG_base_type DIE
+                type_offset: u64,
+                value: addr_type,
+            },
+            const_type: struct {
+                // Offset of DW_TAG_base_type DIE
+                type_offset: u64,
+                value_bytes: []const u8,
+            },
+        };
+
         stack: std.ArrayListUnmanaged(Value) = .{},
 
-        fn generic(value: anytype) Value {
+        pub fn reset(self: *Self) void {
+            self.stack.clearRetainingCapacity();
+        }
+
+        pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
+            self.stack.deinit(allocator);
+        }
+
+        fn generic(value: anytype) Operand {
             const int_info = @typeInfo(@TypeOf(value)).Int;
             if (@sizeOf(@TypeOf(value)) > options.addr_size) {
                 return .{ .generic = switch (int_info.signedness) {
@@ -73,7 +103,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
             }
         }
 
-        pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Value {
+        pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Operand {
             const reader = stream.reader();
             return switch (opcode) {
                 OP.addr,
@@ -87,8 +117,8 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
                 OP.const1s => generic(try reader.readByteSigned()),
                 OP.const2u,
                 OP.call2,
-                OP.call4,
                 => generic(try reader.readInt(u16, options.endian)),
+                OP.call4 => generic(try reader.readInt(u32, options.endian)),
                 OP.const2s,
                 OP.bra,
                 OP.skip,
@@ -114,21 +144,35 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
                     .offset = try leb.readILEB128(i64, reader),
                 } },
                 OP.regx => .{ .register = try leb.readULEB128(u8, reader) },
-                OP.bregx, OP.regval_type => .{ .base_register = .{
-                    .base_register = try leb.readULEB128(u8, reader),
-                    .offset = try leb.readILEB128(i64, reader),
-                } },
+                OP.bregx => blk: {
+                    const base_register = try leb.readULEB128(u8, reader);
+                    const offset = try leb.readILEB128(i64, reader);
+                    break :blk .{ .base_register = .{
+                        .base_register = base_register,
+                        .offset = offset,
+                    } };
+                },
+                OP.regval_type => blk: {
+                    const register = try leb.readULEB128(u8, reader);
+                    const type_offset = try leb.readULEB128(u64, reader);
+                    break :blk .{ .register_type = .{
+                        .register = register,
+                        .type_offset = type_offset,
+                    } };
+                },
                 OP.piece => .{
                     .composite_location = .{
                         .size = try leb.readULEB128(u8, reader),
                         .offset = 0,
                     },
                 },
-                OP.bit_piece => .{
-                    .composite_location = .{
-                        .size = try leb.readULEB128(u8, reader),
-                        .offset = try leb.readILEB128(i64, reader),
-                    },
+                OP.bit_piece => blk: {
+                    const size = try leb.readULEB128(u8, reader);
+                    const offset = try leb.readILEB128(i64, reader);
+                    break :blk .{ .composite_location = .{
+                        .size = size,
+                        .offset = offset,
+                    } };
                 },
                 OP.implicit_value, OP.entry_value => blk: {
                     const size = try leb.readULEB128(u8, reader);
@@ -145,7 +189,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
                     if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
                     const value_bytes = stream.buffer[stream.pos..][0..size];
                     stream.pos += size;
-                    break :blk .{ .base_type = .{
+                    break :blk .{ .const_type = .{
                         .type_offset = type_offset,
                         .value_bytes = value_bytes,
                     } };
@@ -163,22 +207,144 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
             };
         }
 
+        pub fn run(
+            self: *Self,
+            expression: []const u8,
+            allocator: std.mem.Allocator,
+            compile_unit: ?*const dwarf.CompileUnit,
+            ucontext: *const std.os.ucontext_t,
+            reg_ctx: abi.RegisterContext,
+            initial_value: usize,
+        ) !Value {
+            try self.stack.append(allocator, .{ .generic = initial_value });
+            var stream = std.io.fixedBufferStream(expression);
+            while (try self.step(&stream, allocator, compile_unit, ucontext, reg_ctx)) {}
+            if (self.stack.items.len == 0) return error.InvalidExpression;
+            return self.stack.items[self.stack.items.len - 1];
+        }
+
+        /// Reads an opcode and its operands from the stream and executes it
         pub fn step(
-            self: *StackMachine,
-            stream: std.io.FixedBufferStream([]const u8),
+            self: *Self,
+            stream: *std.io.FixedBufferStream([]const u8),
             allocator: std.mem.Allocator,
-        ) !void {
-            if (@sizeOf(usize) != addr_type or options.endian != builtin.target.cpu.arch.endian())
+            compile_unit: ?*const dwarf.CompileUnit,
+            ucontext: *const std.os.ucontext_t,
+            reg_ctx: dwarf.abi.RegisterContext,
+        ) !bool {
+            if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
                 @compileError("Execution of non-native address sizees / endianness is not supported");
 
-            const opcode = try stream.reader.readByte();
-            _ = opcode;
-            _ = self;
-            _ = allocator;
+            const opcode = try stream.reader().readByte();
+            if (options.call_frame_mode) {
+                // Certain opcodes are not allowed in a CFA context, see 6.4.2
+                switch (opcode) {
+                    OP.addrx,
+                    OP.call2,
+                    OP.call4,
+                    OP.call_ref,
+                    OP.const_type,
+                    OP.constx,
+                    OP.convert,
+                    OP.deref_type,
+                    OP.regval_type,
+                    OP.reinterpret,
+                    OP.push_object_address,
+                    OP.call_frame_cfa,
+                    => return error.InvalidCFAExpression,
+                    else => {},
+                }
+            }
+
+            switch (opcode) {
+
+                // 2.5.1.1: Literal Encodings
+                OP.lit0...OP.lit31,
+                OP.addr,
+                OP.const1u,
+                OP.const2u,
+                OP.const4u,
+                OP.const8u,
+                OP.const1s,
+                OP.const2s,
+                OP.const4s,
+                OP.const8s,
+                OP.constu,
+                OP.consts,
+                => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode)).?.generic }),
+
+                OP.const_type => {
+                    const const_type = (try readOperand(stream, opcode)).?.const_type;
+                    try self.stack.append(allocator, .{ .const_type = .{
+                        .type_offset = const_type.type_offset,
+                        .value_bytes = const_type.value_bytes,
+                    } });
+                },
+
+                OP.addrx, OP.constx => {
+                    const debug_addr_index = (try readOperand(stream, opcode)).?.generic;
+
+                    // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic
+
+                    _ = debug_addr_index;
+                    unreachable;
+                },
+
+                // 2.5.1.2: Register Values
+                OP.fbreg => {
+                    if (compile_unit == null) return error.ExpressionRequiresCompileUnit;
+                    if (compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
+
+                    const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);
+                    _ = offset;
+
+                    switch (compile_unit.?.frame_base.?.*) {
+                        .ExprLoc => {
+                            // TODO: Run this expression in a nested stack machine
+                            return error.UnimplementedOpcode;
+                        },
+                        .LocListOffset => {
+                            // TODO: Read value from .debug_loclists
+                            return error.UnimplementedOpcode;
+                        },
+                        .SecOffset => {
+                            // TODO: Read value from .debug_loclists
+                            return error.UnimplementedOpcode;
+                        },
+                        else => return error.InvalidFrameBase,
+                    }
+                },
+                OP.breg0...OP.breg31, OP.bregx => {
+                    const base_register = (try readOperand(stream, opcode)).?.base_register;
+                    var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(ucontext, base_register.base_register, reg_ctx)));
+                    value += base_register.offset;
+                    try self.stack.append(allocator, .{ .generic = @intCast(value) });
+                },
+                OP.regval_type => {
+                    const register_type = (try readOperand(stream, opcode)).?.register_type;
+                    const value = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, register_type.register, reg_ctx));
+                    try self.stack.append(allocator, .{
+                        .regval_type = .{
+                            .value = value,
+                            .type_offset = register_type.type_offset,
+                        },
+                    });
+                },
+
+                // 2.5.1.3: Stack Operations
+
+                OP.dup => {},
+
+                else => {
+                    std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode});
+                    unreachable;
+                },
+
+                // These have already been handled by readOperand
+                OP.lo_user...OP.hi_user => unreachable,
+            }
 
-            // switch (opcode) {
-            //     OP.addr => try self.stack.append(allocator, try readOperand(stream, opcode)),
-            // }
+            return stream.pos < stream.buffer.len;
         }
     };
 }
lib/std/debug.zig
@@ -483,16 +483,14 @@ pub const StackIterator = struct {
     pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator {
         var iterator = init(first_address, null);
         iterator.debug_info = debug_info;
-        iterator.dwarf_context = try DW.UnwindContext.init(context, &isValidMemory);
+        iterator.dwarf_context = try DW.UnwindContext.init(debug_info.allocator, context, &isValidMemory);
         iterator.last_error = null;
         return iterator;
     }
 
     pub fn deinit(self: *StackIterator) void {
-        if (have_ucontext) {
-            if (self.debug_info) |debug_info| {
-                self.dwarf_context.deinit(debug_info.allocator);
-            }
+        if (have_ucontext and self.debug_info != null) {
+            self.dwarf_context.deinit();
         }
     }
 
@@ -599,7 +597,7 @@ pub const StackIterator = struct {
         if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
             self.dwarf_context.reg_ctx.eh_frame = true;
             self.dwarf_context.reg_ctx.is_macho = di.is_macho;
-            return di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address);
+            return di.unwindFrame(&self.dwarf_context, module.base_address);
         } else return error.MissingDebugInfo;
     }
 
lib/std/dwarf.zig
@@ -163,15 +163,9 @@ const PcRange = struct {
 const Func = struct {
     pc_range: ?PcRange,
     name: ?[]const u8,
-
-    fn deinit(func: *Func, allocator: mem.Allocator) void {
-        if (func.name) |name| {
-            allocator.free(name);
-        }
-    }
 };
 
-const CompileUnit = struct {
+pub const CompileUnit = struct {
     version: u16,
     is_64: bool,
     die: *Die,
@@ -181,6 +175,7 @@ const CompileUnit = struct {
     addr_base: usize,
     rnglists_base: usize,
     loclists_base: usize,
+    frame_base: ?*const FormValue,
 };
 
 const AbbrevTable = std.ArrayList(AbbrevTableEntry);
@@ -216,7 +211,7 @@ const AbbrevAttr = struct {
     payload: i64,
 };
 
-const FormValue = union(enum) {
+pub const FormValue = union(enum) {
     Address: u64,
     AddrOffset: usize,
     Block: []u8,
@@ -298,7 +293,7 @@ const Die = struct {
 
     fn getAttrAddr(
         self: *const Die,
-        di: *DwarfInfo,
+        di: *const DwarfInfo,
         id: u64,
         compile_unit: CompileUnit,
     ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
@@ -708,9 +703,6 @@ pub const DwarfInfo = struct {
             allocator.destroy(cu.die);
         }
         di.compile_unit_list.deinit(allocator);
-        for (di.func_list.items) |*func| {
-            func.deinit(allocator);
-        }
         di.func_list.deinit(allocator);
         di.cie_map.deinit(allocator);
         di.fde_list.deinit(allocator);
@@ -793,6 +785,7 @@ pub const DwarfInfo = struct {
                             .addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0,
                             .rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0,
                             .loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0,
+                            .frame_base = die_obj.getAttr(AT.frame_base),
                         };
                     },
                     TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => {
@@ -802,8 +795,7 @@ pub const DwarfInfo = struct {
                             // Prevent endless loops
                             while (depth > 0) : (depth -= 1) {
                                 if (this_die_obj.getAttr(AT.name)) |_| {
-                                    const name = try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);
-                                    break :x try allocator.dupe(u8, name);
+                                    break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);
                                 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
                                     // Follow the DIE it points to and repeat
                                     const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
@@ -834,7 +826,7 @@ pub const DwarfInfo = struct {
                             break :x null;
                         };
 
-                        var found_range = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
+                        var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
                             if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
                                 const pc_end = switch (high_pc_value.*) {
                                     FormValue.Address => |value| value,
@@ -852,9 +844,11 @@ pub const DwarfInfo = struct {
                                         .end = pc_end,
                                     },
                                 });
+
+                                break :blk true;
                             }
 
-                            break :blk true;
+                            break :blk false;
                         } else |err| blk: {
                             if (err != error.MissingDebugInfo) return err;
                             break :blk false;
@@ -867,7 +861,7 @@ pub const DwarfInfo = struct {
                             };
 
                             while (try iter.next()) |range| {
-                                found_range = true;
+                                range_added = true;
                                 try di.func_list.append(allocator, Func{
                                     .name = fn_name,
                                     .pc_range = .{
@@ -878,7 +872,7 @@ pub const DwarfInfo = struct {
                             }
                         }
 
-                        if (!found_range) {
+                        if (fn_name != null and !range_added) {
                             try di.func_list.append(allocator, Func{
                                 .name = fn_name,
                                 .pc_range = null,
@@ -952,6 +946,7 @@ pub const DwarfInfo = struct {
                 .addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0,
                 .rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0,
                 .loclists_base = if (compile_unit_die.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0,
+                .frame_base = compile_unit_die.getAttr(AT.frame_base),
             };
 
             compile_unit.pc_range = x: {
@@ -987,11 +982,11 @@ pub const DwarfInfo = struct {
     const DebugRangeIterator = struct {
         base_address: u64,
         section_type: DwarfSection,
-        di: *DwarfInfo,
+        di: *const DwarfInfo,
         compile_unit: *const CompileUnit,
         stream: io.FixedBufferStream([]const u8),
 
-        pub fn init(ranges_value: *const FormValue, di: *DwarfInfo, compile_unit: *const CompileUnit) !@This() {
+        pub fn init(ranges_value: *const FormValue, di: *const DwarfInfo, compile_unit: *const CompileUnit) !@This() {
             const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges;
             const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
 
@@ -1129,7 +1124,7 @@ pub const DwarfInfo = struct {
         }
     };
 
-    pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
+    pub fn findCompileUnit(di: *const DwarfInfo, target_address: u64) !*const CompileUnit {
         for (di.compile_unit_list.items) |*compile_unit| {
             if (compile_unit.pc_range) |range| {
                 if (target_address >= range.start and target_address < range.end) return compile_unit;
@@ -1630,7 +1625,7 @@ pub const DwarfInfo = struct {
         }
     }
 
-    pub fn unwindFrame(di: *const DwarfInfo, allocator: mem.Allocator, context: *UnwindContext, module_base_address: usize) !usize {
+    pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, module_base_address: usize) !usize {
         if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
         if (context.pc == 0) return 0;
 
@@ -1678,10 +1673,11 @@ pub const DwarfInfo = struct {
             cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
         }
 
+        const compile_unit: ?*const CompileUnit = di.findCompileUnit(fde.pc_begin) catch null;
         context.vm.reset();
         context.reg_ctx.eh_frame = cie.version != 4;
 
-        _ = try context.vm.runToNative(allocator, mapped_pc, cie, fde);
+        _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
         const row = &context.vm.current_row;
 
         context.cfa = switch (row.cfa.rule) {
@@ -1690,12 +1686,19 @@ pub const DwarfInfo = struct {
                 const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
                 break :blk try call_frame.applyOffset(value, offset);
             },
-            .expression => |expression| {
-
-                // TODO: Evaluate expression
-                _ = expression;
-
-                return error.UnimplementedTODO;
+            .expression => |expression| blk: {
+                context.stack_machine.reset();
+                const value = try context.stack_machine.run(
+                    expression,
+                    context.allocator,
+                    compile_unit,
+                    &context.ucontext,
+                    context.reg_ctx,
+                    context.cfa orelse 0,
+                );
+
+                if (value != .generic) return error.InvalidExpressionValue;
+                break :blk value.generic;
             },
             else => return error.InvalidCFARule,
         };
@@ -1713,7 +1716,13 @@ pub const DwarfInfo = struct {
                     has_next_ip = column.rule != .undefined;
                 }
 
-                try column.resolveValue(context.*, dest);
+                try column.resolveValue(
+                    context,
+                    compile_unit,
+                    &context.ucontext,
+                    context.reg_ctx,
+                    dest,
+                );
             }
         }
 
@@ -1738,16 +1747,19 @@ pub const DwarfInfo = struct {
 };
 
 pub const UnwindContext = struct {
+    allocator: mem.Allocator,
     cfa: ?usize,
     pc: usize,
     ucontext: os.ucontext_t,
     reg_ctx: abi.RegisterContext,
     isValidMemory: *const fn (address: usize) bool,
     vm: call_frame.VirtualMachine = .{},
+    stack_machine: expressions.StackMachine(.{ .call_frame_mode = true }) = .{},
 
-    pub fn init(ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
+    pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
         const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null));
         return .{
+            .allocator = allocator,
             .cfa = null,
             .pc = pc,
             .ucontext = ucontext.*,
@@ -1756,8 +1768,9 @@ pub const UnwindContext = struct {
         };
     }
 
-    pub fn deinit(self: *UnwindContext, allocator: mem.Allocator) void {
-        self.vm.deinit(allocator);
+    pub fn deinit(self: *UnwindContext) void {
+        self.vm.deinit(self.allocator);
+        self.stack_machine.deinit(self.allocator);
     }
 
     pub fn getFp(self: *const UnwindContext) !usize {