diff --git a/lib/std/debug.zig b/lib/std/debug.zig index dae233efda51..c468d006b6b8 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -767,7 +767,6 @@ pub const StackIterator = struct { first_address: ?usize, // Last known value of the frame pointer register. fp: usize, - ma: MemoryAccessor = MemoryAccessor.init, // When SelfInfo and a register context is available, this iterator can unwind // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer), @@ -819,7 +818,6 @@ pub const StackIterator = struct { } pub fn deinit(it: *StackIterator) void { - it.ma.deinit(); if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit(); } @@ -890,7 +888,6 @@ pub const StackIterator = struct { unwind_state.debug_info.allocator, module.base_address, &unwind_state.dwarf_context, - &it.ma, unwind_info, module.eh_frame, )) |return_address| { @@ -909,7 +906,6 @@ pub const StackIterator = struct { di, module.base_address, &unwind_state.dwarf_context, - &it.ma, null, ); } else return error.MissingDebugInfo; @@ -945,7 +941,7 @@ pub const StackIterator = struct { // Sanity check. if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) return null; - const new_fp = math.add(usize, it.ma.load(usize, fp) orelse return null, fp_bias) catch + const new_fp = math.add(usize, MemoryAccessor.load(usize, fp) orelse return null, fp_bias) catch return null; // Sanity check: the stack grows down thus all the parent frames must be @@ -953,7 +949,7 @@ pub const StackIterator = struct { // A zero frame pointer often signals this is the last frame, that case // is gracefully handled by the next call to next_internal. if (new_fp != 0 and new_fp < it.fp) return null; - const new_pc = it.ma.load(usize, math.add(usize, fp, pc_offset) catch return null) orelse + const new_pc = MemoryAccessor.load(usize, math.add(usize, fp, pc_offset) catch return null) orelse return null; it.fp = new_fp; diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 3f1fc41febe1..860d0943ee35 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -348,17 +348,11 @@ pub const ExceptionFrameHeader = struct { }; } - fn isValidPtr( - self: ExceptionFrameHeader, - comptime T: type, - ptr: usize, - ma: *MemoryAccessor, - eh_frame_len: ?usize, - ) bool { + fn isValidPtr(self: ExceptionFrameHeader, comptime T: type, ptr: usize, eh_frame_len: ?usize) bool { if (eh_frame_len) |len| { return ptr >= self.eh_frame_ptr and ptr <= self.eh_frame_ptr + len - @sizeOf(T); } else { - return ma.load(T, ptr) != null; + return MemoryAccessor.load(T, ptr) != null; } } @@ -369,7 +363,6 @@ pub const ExceptionFrameHeader = struct { /// If `eh_frame_len` is provided, then these checks can be skipped. pub fn findEntry( self: ExceptionFrameHeader, - ma: *MemoryAccessor, eh_frame_len: ?usize, eh_frame_hdr_ptr: usize, pc: usize, @@ -430,15 +423,15 @@ pub const ExceptionFrameHeader = struct { .endian = native_endian, }; - const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame); - if (fde_entry_header.entry_bytes.len > 0 and !self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad(); + const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, eh_frame_len == null, .eh_frame); + if (fde_entry_header.entry_bytes.len > 0 and !self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), eh_frame_len)) return bad(); if (fde_entry_header.type != .fde) return bad(); // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable const cie_offset = fde_entry_header.type.fde; try eh_frame_fbr.seekTo(cie_offset); - const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame); - if (cie_entry_header.entry_bytes.len > 0 and !self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad(); + const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, eh_frame_len == null, .eh_frame); + if (cie_entry_header.entry_bytes.len > 0 and !self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), eh_frame_len)) return bad(); if (cie_entry_header.type != .cie) return bad(); cie.* = try CommonInformationEntry.parse( @@ -485,15 +478,11 @@ pub const EntryHeader = struct { /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure. /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections. - pub fn read( - fbr: *FixedBufferReader, - opt_ma: ?*MemoryAccessor, - dwarf_section: Section.Id, - ) !EntryHeader { + pub fn read(fbr: *FixedBufferReader, checked: bool, dwarf_section: Section.Id) !EntryHeader { assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); const length_offset = fbr.pos; - const unit_header = try readUnitHeader(fbr, opt_ma); + const unit_header = try readUnitHeader(fbr, checked); const unit_length = cast(usize, unit_header.unit_length) orelse return bad(); if (unit_length == 0) return .{ .length_offset = length_offset, @@ -505,8 +494,8 @@ pub const EntryHeader = struct { const end_offset = start_offset + unit_length; defer fbr.pos = end_offset; - const id = try if (opt_ma) |ma| - fbr.readAddressChecked(unit_header.format, ma) + const id = try if (checked) + fbr.readAddressChecked(unit_header.format) else fbr.readAddress(unit_header.format); const entry_bytes = fbr.buf[fbr.pos..end_offset]; @@ -855,7 +844,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void { while (this_unit_offset < fbr.buf.len) { try fbr.seekTo(this_unit_offset); - const unit_header = try readUnitHeader(&fbr, null); + const unit_header = try readUnitHeader(&fbr, false); if (unit_header.unit_length == 0) return; const next_offset = unit_header.header_length + unit_header.unit_length; @@ -1044,7 +1033,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void { while (this_unit_offset < fbr.buf.len) { try fbr.seekTo(this_unit_offset); - const unit_header = try readUnitHeader(&fbr, null); + const unit_header = try readUnitHeader(&fbr, false); if (unit_header.unit_length == 0) return; const next_offset = unit_header.header_length + unit_header.unit_length; @@ -1426,7 +1415,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) ! }; try fbr.seekTo(line_info_offset); - const unit_header = try readUnitHeader(&fbr, null); + const unit_header = try readUnitHeader(&fbr, false); if (unit_header.unit_length == 0) return missing(); const next_offset = unit_header.header_length + unit_header.unit_length; @@ -1814,7 +1803,7 @@ pub fn scanCieFdeInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !vo if (di.section(frame_section)) |section_data| { var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian }; while (fbr.pos < fbr.buf.len) { - const entry_header = try EntryHeader.read(&fbr, null, frame_section); + const entry_header = try EntryHeader.read(&fbr, false, frame_section); switch (entry_header.type) { .cie => { const cie = try CommonInformationEntry.parse( @@ -1987,8 +1976,8 @@ const UnitHeader = struct { unit_length: u64, }; -fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!UnitHeader { - return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) { +fn readUnitHeader(fbr: *FixedBufferReader, checked: bool) ScanError!UnitHeader { + return switch (try if (checked) fbr.readIntChecked(u32) else fbr.readInt(u32)) { 0...0xfffffff0 - 1 => |unit_length| .{ .format = .@"32", .header_length = 4, @@ -1998,7 +1987,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!U 0xffffffff => .{ .format = .@"64", .header_length = 12, - .unit_length = try if (opt_ma) |ma| fbr.readIntChecked(u64, ma) else fbr.readInt(u64), + .unit_length = try if (checked) fbr.readIntChecked(u64) else fbr.readInt(u64), }, }; } diff --git a/lib/std/debug/Dwarf/expression.zig b/lib/std/debug/Dwarf/expression.zig index c0ebea750430..b312ec2219da 100644 --- a/lib/std/debug/Dwarf/expression.zig +++ b/lib/std/debug/Dwarf/expression.zig @@ -15,8 +15,8 @@ const assert = std.debug.assert; pub const Context = struct { /// The dwarf format of the section this expression is in format: std.dwarf.Format = .@"32", - /// If specified, any addresses will pass through before being accessed - memory_accessor: ?*std.debug.MemoryAccessor = null, + /// When true, addresses will be validated before being accessed + check_memory: bool = false, /// The compilation unit this expression relates to, if any compile_unit: ?*const std.debug.Dwarf.CompileUnit = null, /// When evaluating a user-presented expression, this is the address of the object being evaluated @@ -465,12 +465,12 @@ pub fn StackMachine(comptime options: Options) type { else => unreachable, }; - if (context.memory_accessor) |memory_accessor| { + if (context.check_memory) { if (!switch (size) { - 1 => memory_accessor.load(u8, addr) != null, - 2 => memory_accessor.load(u16, addr) != null, - 4 => memory_accessor.load(u32, addr) != null, - 8 => memory_accessor.load(u64, addr) != null, + 1 => std.debug.MemoryAccessor.load(u8, addr) != null, + 2 => std.debug.MemoryAccessor.load(u16, addr) != null, + 4 => std.debug.MemoryAccessor.load(u32, addr) != null, + 8 => std.debug.MemoryAccessor.load(u64, addr) != null, else => return error.InvalidExpression, }) return error.InvalidExpression; } diff --git a/lib/std/debug/FixedBufferReader.zig b/lib/std/debug/FixedBufferReader.zig index e4aec1a9c675..eb136144cd57 100644 --- a/lib/std/debug/FixedBufferReader.zig +++ b/lib/std/debug/FixedBufferReader.zig @@ -38,12 +38,8 @@ pub fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T { return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian); } -pub fn readIntChecked( - fbr: *FixedBufferReader, - comptime T: type, - ma: *MemoryAccessor, -) Error!T { - if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null) +pub fn readIntChecked(fbr: *FixedBufferReader, comptime T: type) Error!T { + if (MemoryAccessor.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null) return error.InvalidBuffer; return fbr.readInt(T); @@ -64,14 +60,10 @@ pub fn readAddress(fbr: *FixedBufferReader, format: std.dwarf.Format) Error!u64 }; } -pub fn readAddressChecked( - fbr: *FixedBufferReader, - format: std.dwarf.Format, - ma: *MemoryAccessor, -) Error!u64 { +pub fn readAddressChecked(fbr: *FixedBufferReader, format: std.dwarf.Format) Error!u64 { return switch (format) { - .@"32" => try fbr.readIntChecked(u32, ma), - .@"64" => try fbr.readIntChecked(u64, ma), + .@"32" => try fbr.readIntChecked(u32), + .@"64" => try fbr.readIntChecked(u64), }; } diff --git a/lib/std/debug/MemoryAccessor.zig b/lib/std/debug/MemoryAccessor.zig index 7857656554e5..4fd87cf6b3a5 100644 --- a/lib/std/debug/MemoryAccessor.zig +++ b/lib/std/debug/MemoryAccessor.zig @@ -11,82 +11,11 @@ const page_size_min = std.heap.page_size_min; const MemoryAccessor = @This(); -var cached_pid: posix.pid_t = -1; - -mem: switch (native_os) { - .linux => File, - else => void, -}, - -pub const init: MemoryAccessor = .{ - .mem = switch (native_os) { - .linux => .{ .handle = -1 }, - else => {}, - }, -}; - -pub fn deinit(ma: *MemoryAccessor) void { - switch (native_os) { - .linux => switch (ma.mem.handle) { - -2, -1 => {}, - else => ma.mem.close(), - }, - else => {}, - } - ma.* = undefined; -} - -fn read(ma: *MemoryAccessor, address: usize, buf: []u8) bool { - switch (native_os) { - .linux => while (true) switch (ma.mem.handle) { - -2 => break, - -1 => { - const linux = std.os.linux; - const pid = switch (@atomicLoad(posix.pid_t, &cached_pid, .monotonic)) { - -1 => pid: { - const pid = linux.getpid(); - @atomicStore(posix.pid_t, &cached_pid, pid, .monotonic); - break :pid pid; - }, - else => |pid| pid, - }; - const bytes_read = linux.process_vm_readv( - pid, - &.{.{ .base = buf.ptr, .len = buf.len }}, - &.{.{ .base = @ptrFromInt(address), .len = buf.len }}, - 0, - ); - switch (linux.E.init(bytes_read)) { - .SUCCESS => return bytes_read == buf.len, - .FAULT => return false, - .INVAL, .SRCH => unreachable, // own pid is always valid - .PERM => {}, // Known to happen in containers. - .NOMEM => {}, - .NOSYS => {}, // QEMU is known not to implement this syscall. - else => unreachable, // unexpected - } - var path_buf: [ - std.fmt.count("/proc/{d}/mem", .{std.math.minInt(posix.pid_t)}) - ]u8 = undefined; - const path = std.fmt.bufPrint(&path_buf, "/proc/{d}/mem", .{pid}) catch - unreachable; - ma.mem = std.fs.openFileAbsolute(path, .{}) catch { - ma.mem.handle = -2; - break; - }; - }, - else => return (ma.mem.pread(buf, address) catch return false) == buf.len, - }, - else => {}, - } - if (!isValidMemory(address)) return false; - @memcpy(buf, @as([*]const u8, @ptrFromInt(address))); - return true; -} - -pub fn load(ma: *MemoryAccessor, comptime Type: type, address: usize) ?Type { +pub fn load(comptime Type: type, address: usize) ?Type { + if (!isValidMemory(address)) return null; var result: Type = undefined; - return if (ma.read(address, std.mem.asBytes(&result))) result else null; + @memcpy(std.mem.asBytes(&result), @as([*]const u8, @ptrFromInt(address))); + return result; } pub fn isValidMemory(address: usize) bool { diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index a879309870e5..355f3cccbc5b 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -25,6 +25,7 @@ const testing = std.testing; const StackIterator = std.debug.StackIterator; const regBytes = Dwarf.abi.regBytes; const regValueNative = Dwarf.abi.regValueNative; +const MemoryAccessor = std.debug.MemoryAccessor; const SelfInfo = @This(); @@ -1159,7 +1160,6 @@ pub fn unwindFrameMachO( allocator: Allocator, base_address: usize, context: *UnwindContext, - ma: *std.debug.MemoryAccessor, unwind_info: []const u8, eh_frame: ?[]const u8, ) !usize { @@ -1324,7 +1324,7 @@ pub fn unwindFrameMachO( const new_sp = fp + 2 * @sizeOf(usize); // Verify the stack range we're about to read register values from - if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo; + if (MemoryAccessor.load(usize, new_sp) == null or MemoryAccessor.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo; const ip_ptr = fp + @sizeOf(usize); const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; @@ -1355,7 +1355,7 @@ pub fn unwindFrameMachO( base_address + entry.function_offset + encoding.value.x86_64.frameless.stack.indirect.sub_offset; - if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo; + if (MemoryAccessor.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo; // `sub_offset_addr` points to the offset of the literal within the instruction const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*; @@ -1397,7 +1397,7 @@ pub fn unwindFrameMachO( } var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1); - if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo; + if (MemoryAccessor.load(usize, reg_addr) == null) return error.InvalidUnwindInfo; for (0..reg_count) |i| { const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]); (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; @@ -1409,7 +1409,7 @@ pub fn unwindFrameMachO( const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; const new_sp = ip_ptr + @sizeOf(usize); - if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo; + if (MemoryAccessor.load(usize, new_sp) == null) return error.InvalidUnwindInfo; (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; @@ -1417,7 +1417,7 @@ pub fn unwindFrameMachO( break :blk new_ip; }, .DWARF => { - return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf)); + return unwindFrameMachODwarf(allocator, base_address, context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf)); }, }, .aarch64, .aarch64_be => switch (encoding.mode.arm64) { @@ -1426,12 +1426,12 @@ pub fn unwindFrameMachO( const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*; const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*; - if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo; + if (MemoryAccessor.load(usize, new_sp) == null) return error.InvalidUnwindInfo; (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; break :blk new_ip; }, .DWARF => { - return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf)); + return unwindFrameMachODwarf(allocator, base_address, context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf)); }, .FRAME => blk: { const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*; @@ -1443,7 +1443,7 @@ pub fn unwindFrameMachO( @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs))); const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize); - if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo; + if (MemoryAccessor.load(usize, new_sp) == null or MemoryAccessor.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo; var reg_addr = fp - @sizeOf(usize); inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| { @@ -1567,7 +1567,6 @@ pub fn unwindFrameDwarf( di: *Dwarf, base_address: usize, context: *UnwindContext, - ma: *std.debug.MemoryAccessor, explicit_fde_offset: ?usize, ) !usize { if (!supports_unwinding) return error.UnsupportedCpuArchitecture; @@ -1585,14 +1584,14 @@ pub fn unwindFrameDwarf( .endian = di.endian, }; - const fde_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section); + const fde_entry_header = try Dwarf.EntryHeader.read(&fbr, false, dwarf_section); if (fde_entry_header.type != .fde) return error.MissingFDE; const cie_offset = fde_entry_header.type.fde; try fbr.seekTo(cie_offset); fbr.endian = native_endian; - const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section); + const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, false, dwarf_section); if (cie_entry_header.type != .cie) return Dwarf.bad(); const cie = try Dwarf.CommonInformationEntry.parse( @@ -1626,7 +1625,6 @@ pub fn unwindFrameDwarf( var fde: Dwarf.FrameDescriptionEntry = undefined; header.findEntry( - ma, eh_frame_len, @intFromPtr(di.section(.eh_frame_hdr).?.ptr), context.pc, @@ -1670,7 +1668,6 @@ pub fn unwindFrameDwarf( var expression_context: Dwarf.expression.Context = .{ .format = cie.format, - .memory_accessor = ma, .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, .thread_context = context.thread_context, .reg_context = context.reg_context, @@ -1705,7 +1702,7 @@ pub fn unwindFrameDwarf( else => return error.InvalidCFARule, }; - if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA; + if (MemoryAccessor.load(usize, context.cfa.?) == null) return error.InvalidCFA; expression_context.cfa = context.cfa; // Buffering the modifications is done because copying the thread context is not portable, @@ -1744,7 +1741,6 @@ pub fn unwindFrameDwarf( try column.resolveValue( context, expression_context, - ma, src, ); } @@ -1834,7 +1830,6 @@ fn unwindFrameMachODwarf( allocator: Allocator, base_address: usize, context: *UnwindContext, - ma: *std.debug.MemoryAccessor, eh_frame: []const u8, fde_offset: usize, ) !usize { @@ -1849,7 +1844,7 @@ fn unwindFrameMachODwarf( .owned = false, }; - return unwindFrameDwarf(allocator, &di, base_address, context, ma, fde_offset); + return unwindFrameDwarf(allocator, &di, base_address, context, fde_offset); } /// This is a virtual machine that runs DWARF call frame instructions. @@ -1899,7 +1894,6 @@ pub const VirtualMachine = struct { self: Column, context: *SelfInfo.UnwindContext, expression_context: std.debug.Dwarf.expression.Context, - ma: *std.debug.MemoryAccessor, out: []u8, ) !void { switch (self.rule) { @@ -1920,7 +1914,7 @@ pub const VirtualMachine = struct { .offset => |offset| { if (context.cfa) |cfa| { const addr = try applyOffset(cfa, offset); - if (ma.load(usize, addr) == null) return error.InvalidAddress; + if (MemoryAccessor.load(usize, addr) == null) return error.InvalidAddress; const ptr: *const usize = @ptrFromInt(addr); mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); } else return error.InvalidCFA; @@ -1943,7 +1937,7 @@ pub const VirtualMachine = struct { break :blk v.generic; } else return error.NoExpressionValue; - if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress; + if (MemoryAccessor.load(usize, addr) == null) return error.InvalidExpressionAddress; const ptr: *usize = @ptrFromInt(addr); mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); },