From b85f84061aebb6c61ab9ca42d8147e8b76154818 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Mon, 3 Jul 2023 03:45:06 -0400 Subject: [PATCH] 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 --- lib/std/debug.zig | 10 +- lib/std/dwarf.zig | 77 +++++++----- lib/std/dwarf/call_frame.zig | 69 +++++++---- lib/std/dwarf/expressions.zig | 222 +++++++++++++++++++++++++++++----- 4 files changed, 291 insertions(+), 87 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 18c13ada83..f231a4ac47 100644 --- a/lib/std/debug.zig +++ b/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; } diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index abfd3f9358..807bc09a2f 100644 --- a/lib/std/dwarf.zig +++ b/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| { + .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, + ); - // TODO: Evaluate expression - _ = expression; - - return error.UnimplementedTODO; + 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 { diff --git a/lib/std/dwarf/call_frame.zig b/lib/std/dwarf/call_frame.zig index f512d7a909..0fabaa70f0 100644 --- a/lib/std/dwarf/call_frame.zig +++ b/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, + }; }, } diff --git a/lib/std/dwarf/expressions.zig b/lib/std/dwarf/expressions.zig index f2b8bfc881..49c548aecd 100644 --- a/lib/std/dwarf/expressions.zig +++ b/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 step( - self: *StackMachine, - stream: std.io.FixedBufferStream([]const u8), + pub fn run( + self: *Self, + expression: []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: 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: *Self, + stream: *std.io.FixedBufferStream([]const u8), + allocator: std.mem.Allocator, + 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) { - // OP.addr => try self.stack.append(allocator, try readOperand(stream, opcode)), - // } + 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, + } + + return stream.pos < stream.buffer.len; } }; }