Commit d226b74ae8

kcbanner <kcbanner@gmail.com>
2023-07-08 08:52:42
dwarf: add ExpressionError to work around the compiler not being able to infer it dwarf: implement OP.entry_value, add tests
1 parent 21d0154
Changed files (4)
lib/std/dwarf/abi.zig
@@ -59,12 +59,23 @@ pub const RegisterContext = struct {
     is_macho: bool,
 };
 
+pub const AbiError = error{
+    InvalidRegister,
+    UnimplementedArch,
+    UnimplementedOs,
+    ThreadContextNotSupported,
+};
+
 /// Returns a slice containing the backing storage for `reg_number`.
 ///
 /// `reg_context` describes in what context the register number is used, as it can have different
 /// meanings depending on the DWARF container. It is only required when getting the stack or
 /// frame pointer register on some architectures.
-pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) {
+pub fn regBytes(
+    thread_context_ptr: anytype,
+    reg_number: u8,
+    reg_context: ?RegisterContext,
+) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
     if (builtin.os.tag == .windows) {
         return switch (builtin.cpu.arch) {
             .x86 => switch (reg_number) {
lib/std/dwarf/expressions.zig
@@ -11,6 +11,9 @@ const assert = std.debug.assert;
 /// Callers should specify all the fields relevant to their context. If a field is required
 /// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
 pub const ExpressionContext = struct {
+    /// This expression is from a DWARF64 section
+    is_64: bool = false,
+
     /// If specified, any addresses will pass through this function before being
     isValidMemory: ?*const fn (address: usize) bool = null,
 
@@ -29,6 +32,9 @@ pub const ExpressionContext = struct {
 
     /// Call frame address, if in a CFI context
     cfa: ?usize = null,
+
+    /// This expression is a sub-expression from an OP.entry_value instruction
+    entry_value_context: bool = false,
 };
 
 pub const ExpressionOptions = struct {
@@ -42,6 +48,28 @@ pub const ExpressionOptions = struct {
     call_frame_context: bool = false,
 };
 
+pub const ExpressionError = error{
+    UnimplementedExpressionCall,
+    UnimplementedOpcode,
+    UnimplementedUserOpcode,
+    UnimplementedTypedComparison,
+    UnimplementedTypeConversion,
+
+    UnknownExpressionOpcode,
+
+    IncompleteExpressionContext,
+
+    InvalidCFAOpcode,
+    InvalidExpression,
+    InvalidFrameBase,
+    InvalidIntegralTypeSize,
+    InvalidRegister,
+    InvalidSubExpression,
+    InvalidTypeLength,
+
+    TruncatedIntegralType,
+} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
+
 /// A stack machine that can decode and run DWARF expressions.
 /// Expressions can be decoded for non-native address size and endianness,
 /// but can only be executed if the current target matches the configuration.
@@ -156,12 +184,14 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
             }
         }
 
-        pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Operand {
+        pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8, context: ExpressionContext) !?Operand {
             const reader = stream.reader();
             return switch (opcode) {
-                OP.addr,
-                OP.call_ref,
-                => generic(try reader.readInt(addr_type, options.endian)),
+                OP.addr => generic(try reader.readInt(addr_type, options.endian)),
+                OP.call_ref => if (context.is_64)
+                    generic(try reader.readInt(u64, options.endian))
+                else
+                    generic(try reader.readInt(u32, options.endian)),
                 OP.const1u,
                 OP.pick,
                 => generic(try reader.readByte()),
@@ -267,7 +297,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
             allocator: std.mem.Allocator,
             context: ExpressionContext,
             initial_value: ?usize,
-        ) !?Value {
+        ) ExpressionError!?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)) {}
@@ -281,12 +311,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
             stream: *std.io.FixedBufferStream([]const u8),
             allocator: std.mem.Allocator,
             context: ExpressionContext,
-        ) !bool {
+        ) ExpressionError!bool {
             if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
                 @compileError("Execution of non-native address sizes / endianness is not supported");
 
             const opcode = try stream.reader().readByte();
-            if (options.call_frame_context and !opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
+            if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
             switch (opcode) {
 
                 // 2.5.1.1: Literal Encodings
@@ -302,10 +332,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 OP.const8s,
                 OP.constu,
                 OP.consts,
-                => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode)).?.generic }),
+                => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode, context)).?.generic }),
 
                 OP.const_type => {
-                    const const_type = (try readOperand(stream, opcode)).?.const_type;
+                    const const_type = (try readOperand(stream, opcode, context)).?.const_type;
                     try self.stack.append(allocator, .{ .const_type = .{
                         .type_offset = const_type.type_offset,
                         .value_bytes = const_type.value_bytes,
@@ -315,9 +345,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 OP.addrx,
                 OP.constx,
                 => {
-                    if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;
-                    if (context.debug_addr == null) return error.ExpressionRequiresDebugAddr;
-                    const debug_addr_index = (try readOperand(stream, opcode)).?.generic;
+                    if (context.compile_unit == null) return error.IncompleteExpressionContext;
+                    if (context.debug_addr == null) return error.IncompleteExpressionContext;
+                    const debug_addr_index = (try readOperand(stream, opcode, context)).?.generic;
                     const offset = context.compile_unit.?.addr_base + debug_addr_index;
                     if (offset >= context.debug_addr.?.len) return error.InvalidExpression;
                     const value = mem.readIntSliceNative(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)]);
@@ -326,10 +356,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
 
                 // 2.5.1.2: Register Values
                 OP.fbreg => {
-                    if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;
-                    if (context.compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
+                    if (context.compile_unit == null) return error.IncompleteExpressionContext;
+                    if (context.compile_unit.?.frame_base == null) return error.IncompleteExpressionContext;
 
-                    const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);
+                    const offset: i64 = @intCast((try readOperand(stream, opcode, context)).?.generic);
                     _ = offset;
 
                     switch (context.compile_unit.?.frame_base.?.*) {
@@ -353,7 +383,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 => {
                     if (context.thread_context == null) return error.IncompleteExpressionContext;
 
-                    const base_register = (try readOperand(stream, opcode)).?.base_register;
+                    const base_register = (try readOperand(stream, opcode, context)).?.base_register;
                     var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
                         context.thread_context.?,
                         base_register.base_register,
@@ -363,7 +393,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     try self.stack.append(allocator, .{ .generic = @intCast(value) });
                 },
                 OP.regval_type => {
-                    const register_type = (try readOperand(stream, opcode)).?.register_type;
+                    const register_type = (try readOperand(stream, opcode, context)).?.register_type;
                     const value = mem.readIntSliceNative(usize, try abi.regBytes(
                         context.thread_context.?,
                         register_type.register,
@@ -387,7 +417,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     _ = self.stack.pop();
                 },
                 OP.pick, OP.over => {
-                    const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode)).?.generic;
+                    const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode, context)).?.generic;
                     if (stack_index >= self.stack.items.len) return error.InvalidExpression;
                     try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
                 },
@@ -429,7 +459,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
 
                     if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression;
 
-                    const operand = try readOperand(stream, opcode);
+                    const operand = try readOperand(stream, opcode, context);
                     const size = switch (opcode) {
                         OP.deref,
                         OP.xderef,
@@ -469,11 +499,15 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     }
                 },
                 OP.push_object_address => {
-                    if (context.object_address == null) return error.IncompleteExpressionContext;
-                    try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });
+                    // In sub-expressions, `push_object_address` is not meaningful (as per the
+                    // spec), so treat it like a nop
+                    if (!context.entry_value_context) {
+                        if (context.object_address == null) return error.IncompleteExpressionContext;
+                        try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });
+                    }
                 },
                 OP.form_tls_address => {
-                    return error.UnimplementedExpressionOpcode;
+                    return error.UnimplementedOpcode;
                 },
                 OP.call_frame_cfa => {
                     if (context.cfa) |cfa| {
@@ -559,7 +593,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 },
                 OP.plus_uconst => {
                     if (self.stack.items.len == 0) return error.InvalidExpression;
-                    const constant = (try readOperand(stream, opcode)).?.generic;
+                    const constant = (try readOperand(stream, opcode, context)).?.generic;
                     self.stack.items[self.stack.items.len - 1] = .{
                         .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
                     };
@@ -628,7 +662,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     }
                 },
                 OP.skip, OP.bra => {
-                    const branch_offset = (try readOperand(stream, opcode)).?.branch_offset;
+                    const branch_offset = (try readOperand(stream, opcode, context)).?.branch_offset;
                     const condition = if (opcode == OP.bra) blk: {
                         if (self.stack.items.len == 0) return error.InvalidExpression;
                         break :blk try self.stack.pop().asIntegral() != 0;
@@ -648,7 +682,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 OP.call4,
                 OP.call_ref,
                 => {
-                    const debug_info_offset = (try readOperand(stream, opcode)).?.generic;
+                    const debug_info_offset = (try readOperand(stream, opcode, context)).?.generic;
                     _ = debug_info_offset;
 
                     // 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 {
                 // 2.5.1.6: Type Conversions
                 OP.convert => {
                     if (self.stack.items.len == 0) return error.InvalidExpression;
-                    const type_offset = (try readOperand(stream, opcode)).?.generic;
+                    const type_offset = (try readOperand(stream, opcode, context)).?.generic;
 
                     // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
                     const value = self.stack.items[self.stack.items.len - 1];
@@ -675,7 +709,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 },
                 OP.reinterpret => {
                     if (self.stack.items.len == 0) return error.InvalidExpression;
-                    const type_offset = (try readOperand(stream, opcode)).?.generic;
+                    const type_offset = (try readOperand(stream, opcode, context)).?.generic;
 
                     // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
                     const value = self.stack.items[self.stack.items.len - 1];
@@ -710,15 +744,29 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 // 2.5.1.7: Special Operations
                 OP.nop => {},
                 OP.entry_value => {
-                    const block = (try readOperand(stream, opcode)).?.block;
-                    _ = block;
+                    const block = (try readOperand(stream, opcode, context)).?.block;
+                    if (block.len == 0) return error.InvalidSubExpression;
+
+                    // TODO: The spec states that this sub-expression needs to observe the state (ie. registers)
+                    //       as it was upon entering the current subprogram. If this isn't being called at the
+                    //       end of a frame unwind operation, an additional ThreadContext with this state will be needed.
 
-                    // TODO: If block is an expression, run it on a new stack. Push the resulting value onto this stack.
-                    // TODO: If block is a register location, push the value that location had before running this program onto this stack.
-                    //       This implies capturing all register values before executing this block, in case this program modifies them.
-                    // TODO: If the block contains, OP.push_object_address, treat it as OP.nop
+                    if (isOpcodeRegisterLocation(block[0])) {
+                        if (context.thread_context == null) return error.IncompleteExpressionContext;
 
-                    return error.UnimplementedSubExpression;
+                        var block_stream = std.io.fixedBufferStream(block);
+                        const register = (try readOperand(&block_stream, block[0], context)).?.register;
+                        const value = mem.readIntSliceNative(usize, try abi.regBytes(context.thread_context.?, register, context.reg_context));
+                        try self.stack.append(allocator, .{ .generic = value });
+                    } else {
+                        var stack_machine: Self = .{};
+                        defer stack_machine.deinit(allocator);
+
+                        var sub_context = context;
+                        sub_context.entry_value_context = true;
+                        const result = try stack_machine.run(block, allocator, sub_context, null);
+                        try self.stack.append(allocator, result orelse return error.InvalidSubExpression);
+                    }
                 },
 
                 // These have already been handled by readOperand
@@ -745,7 +793,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
     return struct {
         /// Zero-operand instructions
         pub fn writeOpcode(writer: anytype, comptime opcode: u8) !void {
-            if (options.call_frame_context and !comptime opcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
+            if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
             switch (opcode) {
                 OP.dup,
                 OP.drop,
@@ -778,6 +826,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
                 OP.gt,
                 OP.ne,
                 OP.nop,
+                OP.stack_value,
                 => try writer.writeByte(opcode),
                 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
             }
@@ -828,12 +877,12 @@ pub fn Builder(comptime options: ExpressionOptions) type {
             try leb.writeULEB128(writer, debug_addr_offset);
         }
 
-        pub fn writeConstType(writer: anytype, die_offset: anytype, size: u8, value_bytes: []const u8) !void {
+        pub fn writeConstType(writer: anytype, die_offset: anytype, value_bytes: []const u8) !void {
             if (options.call_frame_context) return error.InvalidCFAOpcode;
-            if (size != value_bytes.len) return error.InvalidValueSize;
+            if (value_bytes.len > 0xff) return error.InvalidTypeLength;
             try writer.writeByte(OP.const_type);
             try leb.writeULEB128(writer, die_offset);
-            try writer.writeByte(size);
+            try writer.writeByte(@intCast(value_bytes.len));
             try writer.writeAll(value_bytes);
         }
 
@@ -932,10 +981,10 @@ pub fn Builder(comptime options: ExpressionOptions) type {
             try writer.writeInt(T, offset, options.endian);
         }
 
-        pub fn writeCallRef(writer: anytype, debug_info_offset: addr_type) !void {
+        pub fn writeCallRef(writer: anytype, comptime is_64: bool, value: if (is_64) u64 else u32) !void {
             if (options.call_frame_context) return error.InvalidCFAOpcode;
             try writer.writeByte(OP.call_ref);
-            try writer.writeInt(addr_type, debug_info_offset, options.endian);
+            try writer.writeInt(if (is_64) u64 else u32, value, options.endian);
         }
 
         pub fn writeConvert(writer: anytype, die_offset: anytype) !void {
@@ -959,13 +1008,29 @@ pub fn Builder(comptime options: ExpressionOptions) type {
         }
 
         // 2.6: Location Descriptions
-        // TODO
+        pub fn writeReg(writer: anytype, register: u8) !void {
+            try writer.writeByte(OP.reg0 + register);
+        }
+
+        pub fn writeRegx(writer: anytype, register: anytype) !void {
+            try writer.writeByte(OP.regx);
+            try leb.writeULEB128(writer, register);
+        }
+
+        pub fn writeImplicitValue(writer: anytype, value_bytes: []const u8) !void {
+            try writer.writeByte(OP.implicit_value);
+            try leb.writeULEB128(writer, value_bytes.len);
+            try writer.writeAll(value_bytes);
+        }
+
+        // pub fn writeImplicitPointer(writer: anytype, ) void {
+        // }
 
     };
 }
 
 // Certain opcodes are not allowed in a CFA context, see 6.4.2
-fn opcodeValidInCFA(opcode: u8) bool {
+fn isOpcodeValidInCFA(opcode: u8) bool {
     return switch (opcode) {
         OP.addrx,
         OP.call2,
@@ -984,6 +1049,13 @@ fn opcodeValidInCFA(opcode: u8) bool {
     };
 }
 
+fn isOpcodeRegisterLocation(opcode: u8) bool {
+    return switch (opcode) {
+        OP.reg0...OP.reg31, OP.regx => true,
+        else => false,
+    };
+}
+
 const testing = std.testing;
 test "DWARF expressions" {
     const allocator = std.testing.allocator;
@@ -1067,7 +1139,7 @@ test "DWARF expressions" {
 
         const die_offset: usize = @truncate(0xaabbccdd);
         const type_bytes: []const u8 = &.{ 1, 2, 3, 4 };
-        try b.writeConstType(writer, die_offset, type_bytes.len, type_bytes);
+        try b.writeConstType(writer, die_offset, type_bytes);
 
         _ = try stack_machine.run(program.items, allocator, context, 0);
 
@@ -1137,7 +1209,13 @@ test "DWARF expressions" {
             try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
             try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
         } else |err| {
-            if (err != error.UnimplementedArch and err != error.UnimplementedOs) return err;
+            switch (err) {
+                error.UnimplementedArch,
+                error.UnimplementedOs,
+                error.ThreadContextNotSupported,
+                => {},
+                else => return err,
+            }
         }
     }
 
@@ -1396,7 +1474,6 @@ test "DWARF expressions" {
         try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
     }
 
-
     // Control Flow Operations
     {
         var context = ExpressionContext{};
@@ -1436,7 +1513,6 @@ test "DWARF expressions" {
         _ = try stack_machine.run(program.items, allocator, context, null);
         try testing.expectEqual(@as(usize, 2), stack_machine.stack.popOrNull().?.generic);
 
-
         stack_machine.reset();
         program.clearRetainingCapacity();
         try b.writeLiteral(writer, 2);
@@ -1470,7 +1546,7 @@ test "DWARF expressions" {
         // Convert to generic type
         stack_machine.reset();
         program.clearRetainingCapacity();
-        try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);
+        try b.writeConstType(writer, @as(usize, 0), &value_bytes);
         try b.writeConvert(writer, @as(usize, 0));
         _ = try stack_machine.run(program.items, allocator, context, null);
         try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
@@ -1478,7 +1554,7 @@ test "DWARF expressions" {
         // Reinterpret to generic type
         stack_machine.reset();
         program.clearRetainingCapacity();
-        try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);
+        try b.writeConstType(writer, @as(usize, 0), &value_bytes);
         try b.writeReinterpret(writer, @as(usize, 0));
         _ = try stack_machine.run(program.items, allocator, context, null);
         try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
@@ -1488,7 +1564,7 @@ test "DWARF expressions" {
 
         stack_machine.reset();
         program.clearRetainingCapacity();
-        try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);
+        try b.writeConstType(writer, @as(usize, 0), &value_bytes);
         try b.writeReinterpret(writer, die_offset);
         _ = try stack_machine.run(program.items, allocator, context, null);
         const const_type = stack_machine.stack.popOrNull().?.const_type;
@@ -1506,18 +1582,59 @@ test "DWARF expressions" {
     // Special operations
     {
         var context = ExpressionContext{};
+
         stack_machine.reset();
         program.clearRetainingCapacity();
-
         try b.writeOpcode(writer, OP.nop);
         _ = try stack_machine.run(program.items, allocator, context, null);
         try testing.expect(stack_machine.stack.popOrNull() == null);
 
+        // Sub-expression
+        {
+            var sub_program = std.ArrayList(u8).init(allocator);
+            defer sub_program.deinit();
+            const sub_writer = sub_program.writer();
+            try b.writeLiteral(sub_writer, 3);
 
+            stack_machine.reset();
+            program.clearRetainingCapacity();
+            try b.writeEntryValue(writer, sub_program.items);
+            _ = try stack_machine.run(program.items, allocator, context, null);
+            try testing.expectEqual(@as(usize, 3), stack_machine.stack.popOrNull().?.generic);
+        }
 
+        // Register location description
+        const reg_context = abi.RegisterContext{
+            .eh_frame = true,
+            .is_macho = builtin.os.tag == .macos,
+        };
+        var thread_context: std.debug.ThreadContext = undefined;
+        context = ExpressionContext{
+            .thread_context = &thread_context,
+            .reg_context = reg_context,
+        };
 
-    }
+        if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
+            mem.writeIntSliceNative(usize, reg_bytes, 0xee);
 
+            var sub_program = std.ArrayList(u8).init(allocator);
+            defer sub_program.deinit();
+            const sub_writer = sub_program.writer();
+            try b.writeReg(sub_writer, 0);
 
+            stack_machine.reset();
+            program.clearRetainingCapacity();
+            try b.writeEntryValue(writer, sub_program.items);
+            _ = try stack_machine.run(program.items, allocator, context, null);
+            try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.popOrNull().?.generic);
+        } else |err| {
+            switch (err) {
+                error.UnimplementedArch,
+                error.UnimplementedOs,
+                error.ThreadContextNotSupported,
+                => {},
+                else => return err,
+            }
+        }
+    }
 }
-
lib/std/debug.zig
@@ -628,7 +628,6 @@ pub const StackIterator = struct {
 
                 // TODO: Unwind using __unwind_info,
                 unreachable;
-
             },
             else => {},
         }
lib/std/dwarf.zig
@@ -1592,6 +1592,7 @@ pub const DwarfInfo = struct {
                                 entry_header.entry_bytes,
                                 -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))),
                                 true,
+                                entry_header.is_64,
                                 frame_section,
                                 entry_header.length_offset,
                                 @sizeOf(usize),
@@ -1674,6 +1675,7 @@ pub const DwarfInfo = struct {
         }
 
         var expression_context = .{
+            .is_64 = cie.is_64,
             .isValidMemory = context.isValidMemory,
             .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
             .thread_context = context.thread_context,
@@ -2042,6 +2044,7 @@ pub const ExceptionFrameHeader = struct {
             cie_entry_header.entry_bytes,
             0,
             true,
+            cie_entry_header.is_64,
             .eh_frame,
             cie_entry_header.length_offset,
             @sizeOf(usize),
@@ -2135,8 +2138,8 @@ pub const CommonInformationEntry = struct {
     // This is the key that FDEs use to reference CIEs.
     length_offset: u64,
     version: u8,
-
     address_size: u8,
+    is_64: bool,
 
     // Only present in version 4
     segment_selector_size: ?u8,
@@ -2175,11 +2178,12 @@ pub const CommonInformationEntry = struct {
     /// of `pc_rel_offset` and `is_runtime`.
     ///
     /// `length_offset` specifies the offset of this CIE's length field in the
-    /// .eh_frame / .debug_framesection.
+    /// .eh_frame / .debug_frame section.
     pub fn parse(
         cie_bytes: []const u8,
         pc_rel_offset: i64,
         is_runtime: bool,
+        is_64: bool,
         dwarf_section: DwarfSection,
         length_offset: u64,
         addr_size_bytes: u8,
@@ -2280,6 +2284,7 @@ pub const CommonInformationEntry = struct {
             .length_offset = length_offset,
             .version = version,
             .address_size = address_size,
+            .is_64 = is_64,
             .segment_selector_size = segment_selector_size,
             .code_alignment_factor = code_alignment_factor,
             .data_alignment_factor = data_alignment_factor,
@@ -2316,8 +2321,8 @@ pub const FrameDescriptionEntry = struct {
     /// where the section is currently stored in memory, to where it *would* be
     /// stored at runtime: section runtime offset - backing section data base ptr.
     ///
-    /// Similarly, `is_runtime` specifies this function is being called on a runtime section, and so
-    /// indirect pointers can be followed.
+    /// Similarly, `is_runtime` specifies this function is being called on a runtime
+    /// section, and so indirect pointers can be followed.
     pub fn parse(
         fde_bytes: []const u8,
         pc_rel_offset: i64,