authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-22 18:30:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-23 13:48:52-07:00
logd00cc100867bbb7a5e4305a5597cb45982c4593f
tree6c04d8c2d13a72d3a2117d6723494311d53018ed
parent77c09d16f9d992b19bc7cb2d2497f8009e0250ba

std.debug: delete MemoryAccessor

This API is based around the unsound idea that a process can perform checked virtual memory loads to prevent crashing. This depends on OS-specific APIs that may be unavailable, disabled, or impossible due to virtualization. It also makes collecting stack traces ridiculously slow, which is a problem for users of DebugAllocator - in other words, everybody, all the time. It also makes strace go from being superbly clean to being awful.

6 files changed, 28 insertions(+), 267 deletions(-)

lib/std/debug.zig+3-10
...@@ -14,7 +14,6 @@ const native_os = builtin.os.tag;...@@ -14,7 +14,6 @@ const native_os = builtin.os.tag;
14const native_endian = native_arch.endian();14const native_endian = native_arch.endian();
15const Writer = std.io.Writer;15const Writer = std.io.Writer;
1616
17pub const MemoryAccessor = @import("debug/MemoryAccessor.zig");
18pub const FixedBufferReader = @import("debug/FixedBufferReader.zig");17pub const FixedBufferReader = @import("debug/FixedBufferReader.zig");
19pub const Dwarf = @import("debug/Dwarf.zig");18pub const Dwarf = @import("debug/Dwarf.zig");
20pub const Pdb = @import("debug/Pdb.zig");19pub const Pdb = @import("debug/Pdb.zig");
...@@ -773,7 +772,6 @@ pub const StackIterator = struct {...@@ -773,7 +772,6 @@ pub const StackIterator = struct {
773 first_address: ?usize,772 first_address: ?usize,
774 // Last known value of the frame pointer register.773 // Last known value of the frame pointer register.
775 fp: usize,774 fp: usize,
776 ma: MemoryAccessor = MemoryAccessor.init,
777775
778 // When SelfInfo and a register context is available, this iterator can unwind776 // When SelfInfo and a register context is available, this iterator can unwind
779 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer),777 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer),
...@@ -795,7 +793,7 @@ pub const StackIterator = struct {...@@ -795,7 +793,7 @@ pub const StackIterator = struct {
795 ::: .{ .memory = true });793 ::: .{ .memory = true });
796 }794 }
797795
798 return StackIterator{796 return .{
799 .first_address = first_address,797 .first_address = first_address,
800 // TODO: this is a workaround for #16876798 // TODO: this is a workaround for #16876
801 //.fp = fp orelse @frameAddress(),799 //.fp = fp orelse @frameAddress(),
...@@ -825,7 +823,6 @@ pub const StackIterator = struct {...@@ -825,7 +823,6 @@ pub const StackIterator = struct {
825 }823 }
826824
827 pub fn deinit(it: *StackIterator) void {825 pub fn deinit(it: *StackIterator) void {
828 it.ma.deinit();
829 if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit();826 if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit();
830 }827 }
831828
...@@ -896,7 +893,6 @@ pub const StackIterator = struct {...@@ -896,7 +893,6 @@ pub const StackIterator = struct {
896 unwind_state.debug_info.allocator,893 unwind_state.debug_info.allocator,
897 module.base_address,894 module.base_address,
898 &unwind_state.dwarf_context,895 &unwind_state.dwarf_context,
899 &it.ma,
900 unwind_info,896 unwind_info,
901 module.eh_frame,897 module.eh_frame,
902 )) |return_address| {898 )) |return_address| {
...@@ -915,7 +911,6 @@ pub const StackIterator = struct {...@@ -915,7 +911,6 @@ pub const StackIterator = struct {
915 di,911 di,
916 module.base_address,912 module.base_address,
917 &unwind_state.dwarf_context,913 &unwind_state.dwarf_context,
918 &it.ma,
919 null,914 null,
920 );915 );
921 } else return error.MissingDebugInfo;916 } else return error.MissingDebugInfo;
...@@ -951,7 +946,7 @@ pub const StackIterator = struct {...@@ -951,7 +946,7 @@ pub const StackIterator = struct {
951946
952 // Sanity check.947 // Sanity check.
953 if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) return null;948 if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) return null;
954 const new_fp = math.add(usize, it.ma.load(usize, fp) orelse return null, fp_bias) catch949 const new_fp = math.add(usize, @as(*usize, @ptrFromInt(fp)).*, fp_bias) catch
955 return null;950 return null;
956951
957 // Sanity check: the stack grows down thus all the parent frames must be952 // Sanity check: the stack grows down thus all the parent frames must be
...@@ -959,8 +954,7 @@ pub const StackIterator = struct {...@@ -959,8 +954,7 @@ pub const StackIterator = struct {
959 // A zero frame pointer often signals this is the last frame, that case954 // A zero frame pointer often signals this is the last frame, that case
960 // is gracefully handled by the next call to next_internal.955 // is gracefully handled by the next call to next_internal.
961 if (new_fp != 0 and new_fp < it.fp) return null;956 if (new_fp != 0 and new_fp < it.fp) return null;
962 const new_pc = it.ma.load(usize, math.add(usize, fp, pc_offset) catch return null) orelse957 const new_pc = @as(*usize, @ptrFromInt(math.add(usize, fp, pc_offset) catch return null)).*;
963 return null;
964958
965 it.fp = new_fp;959 it.fp = new_fp;
966960
...@@ -1774,7 +1768,6 @@ pub inline fn inValgrind() bool {...@@ -1774,7 +1768,6 @@ pub inline fn inValgrind() bool {
17741768
1775test {1769test {
1776 _ = &Dwarf;1770 _ = &Dwarf;
1777 _ = &MemoryAccessor;
1778 _ = &FixedBufferReader;1771 _ = &FixedBufferReader;
1779 _ = &Pdb;1772 _ = &Pdb;
1780 _ = &SelfInfo;1773 _ = &SelfInfo;
lib/std/debug/Dwarf.zig+14-45
...@@ -24,7 +24,6 @@ const UT = DW.UT;...@@ -24,7 +24,6 @@ const UT = DW.UT;
24const assert = std.debug.assert;24const assert = std.debug.assert;
25const cast = std.math.cast;25const cast = std.math.cast;
26const maxInt = std.math.maxInt;26const maxInt = std.math.maxInt;
27const MemoryAccessor = std.debug.MemoryAccessor;
28const Path = std.Build.Cache.Path;27const Path = std.Build.Cache.Path;
29const FixedBufferReader = std.debug.FixedBufferReader;28const FixedBufferReader = std.debug.FixedBufferReader;
30const ArrayList = std.ArrayList;29const ArrayList = std.ArrayList;
...@@ -349,29 +348,9 @@ pub const ExceptionFrameHeader = struct {...@@ -349,29 +348,9 @@ pub const ExceptionFrameHeader = struct {
349 };348 };
350 }349 }
351350
352 fn isValidPtr(
353 self: ExceptionFrameHeader,
354 comptime T: type,
355 ptr: usize,
356 ma: *MemoryAccessor,
357 eh_frame_len: ?usize,
358 ) bool {
359 if (eh_frame_len) |len| {
360 return ptr >= self.eh_frame_ptr and ptr <= self.eh_frame_ptr + len - @sizeOf(T);
361 } else {
362 return ma.load(T, ptr) != null;
363 }
364 }
365
366 /// Find an entry by binary searching the eh_frame_hdr section.
367 ///
368 /// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller,
369 /// MemoryAccessor will be used to verify readability of the header entries.
370 /// If `eh_frame_len` is provided, then these checks can be skipped.
371 pub fn findEntry(351 pub fn findEntry(
372 self: ExceptionFrameHeader,352 self: ExceptionFrameHeader,
373 ma: *MemoryAccessor,353 eh_frame_len: usize,
374 eh_frame_len: ?usize,
375 eh_frame_hdr_ptr: usize,354 eh_frame_hdr_ptr: usize,
376 pc: usize,355 pc: usize,
377 cie: *CommonInformationEntry,356 cie: *CommonInformationEntry,
...@@ -421,8 +400,7 @@ pub const ExceptionFrameHeader = struct {...@@ -421,8 +400,7 @@ pub const ExceptionFrameHeader = struct {
421400
422 if (fde_ptr < self.eh_frame_ptr) return bad();401 if (fde_ptr < self.eh_frame_ptr) return bad();
423402
424 // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor403 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0..eh_frame_len];
425 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)];
426404
427 const fde_offset = fde_ptr - self.eh_frame_ptr;405 const fde_offset = fde_ptr - self.eh_frame_ptr;
428 var eh_frame_fbr: FixedBufferReader = .{406 var eh_frame_fbr: FixedBufferReader = .{
...@@ -431,15 +409,13 @@ pub const ExceptionFrameHeader = struct {...@@ -431,15 +409,13 @@ pub const ExceptionFrameHeader = struct {
431 .endian = native_endian,409 .endian = native_endian,
432 };410 };
433411
434 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);412 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame);
435 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();
436 if (fde_entry_header.type != .fde) return bad();413 if (fde_entry_header.type != .fde) return bad();
437414
438 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable415 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
439 const cie_offset = fde_entry_header.type.fde;416 const cie_offset = fde_entry_header.type.fde;
440 try eh_frame_fbr.seekTo(cie_offset);417 try eh_frame_fbr.seekTo(cie_offset);
441 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);418 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame);
442 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();
443 if (cie_entry_header.type != .cie) return bad();419 if (cie_entry_header.type != .cie) return bad();
444420
445 cie.* = try CommonInformationEntry.parse(421 cie.* = try CommonInformationEntry.parse(
...@@ -486,15 +462,11 @@ pub const EntryHeader = struct {...@@ -486,15 +462,11 @@ pub const EntryHeader = struct {
486462
487 /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure.463 /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure.
488 /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections.464 /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections.
489 pub fn read(465 pub fn read(fbr: *FixedBufferReader, dwarf_section: Section.Id) !EntryHeader {
490 fbr: *FixedBufferReader,
491 opt_ma: ?*MemoryAccessor,
492 dwarf_section: Section.Id,
493 ) !EntryHeader {
494 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);466 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
495467
496 const length_offset = fbr.pos;468 const length_offset = fbr.pos;
497 const unit_header = try readUnitHeader(fbr, opt_ma);469 const unit_header = try readUnitHeader(fbr);
498 const unit_length = cast(usize, unit_header.unit_length) orelse return bad();470 const unit_length = cast(usize, unit_header.unit_length) orelse return bad();
499 if (unit_length == 0) return .{471 if (unit_length == 0) return .{
500 .length_offset = length_offset,472 .length_offset = length_offset,
...@@ -506,10 +478,7 @@ pub const EntryHeader = struct {...@@ -506,10 +478,7 @@ pub const EntryHeader = struct {
506 const end_offset = start_offset + unit_length;478 const end_offset = start_offset + unit_length;
507 defer fbr.pos = end_offset;479 defer fbr.pos = end_offset;
508480
509 const id = try if (opt_ma) |ma|481 const id = try fbr.readAddress(unit_header.format);
510 fbr.readAddressChecked(unit_header.format, ma)
511 else
512 fbr.readAddress(unit_header.format);
513 const entry_bytes = fbr.buf[fbr.pos..end_offset];482 const entry_bytes = fbr.buf[fbr.pos..end_offset];
514 const cie_id: u64 = switch (dwarf_section) {483 const cie_id: u64 = switch (dwarf_section) {
515 .eh_frame => CommonInformationEntry.eh_id,484 .eh_frame => CommonInformationEntry.eh_id,
...@@ -856,7 +825,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -856,7 +825,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
856 while (this_unit_offset < fbr.buf.len) {825 while (this_unit_offset < fbr.buf.len) {
857 try fbr.seekTo(this_unit_offset);826 try fbr.seekTo(this_unit_offset);
858827
859 const unit_header = try readUnitHeader(&fbr, null);828 const unit_header = try readUnitHeader(&fbr);
860 if (unit_header.unit_length == 0) return;829 if (unit_header.unit_length == 0) return;
861 const next_offset = unit_header.header_length + unit_header.unit_length;830 const next_offset = unit_header.header_length + unit_header.unit_length;
862831
...@@ -1045,7 +1014,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -1045,7 +1014,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1045 while (this_unit_offset < fbr.buf.len) {1014 while (this_unit_offset < fbr.buf.len) {
1046 try fbr.seekTo(this_unit_offset);1015 try fbr.seekTo(this_unit_offset);
10471016
1048 const unit_header = try readUnitHeader(&fbr, null);1017 const unit_header = try readUnitHeader(&fbr);
1049 if (unit_header.unit_length == 0) return;1018 if (unit_header.unit_length == 0) return;
1050 const next_offset = unit_header.header_length + unit_header.unit_length;1019 const next_offset = unit_header.header_length + unit_header.unit_length;
10511020
...@@ -1427,7 +1396,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1427,7 +1396,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1427 };1396 };
1428 try fbr.seekTo(line_info_offset);1397 try fbr.seekTo(line_info_offset);
14291398
1430 const unit_header = try readUnitHeader(&fbr, null);1399 const unit_header = try readUnitHeader(&fbr);
1431 if (unit_header.unit_length == 0) return missing();1400 if (unit_header.unit_length == 0) return missing();
14321401
1433 const next_offset = unit_header.header_length + unit_header.unit_length;1402 const next_offset = unit_header.header_length + unit_header.unit_length;
...@@ -1815,7 +1784,7 @@ pub fn scanCieFdeInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !vo...@@ -1815,7 +1784,7 @@ pub fn scanCieFdeInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !vo
1815 if (di.section(frame_section)) |section_data| {1784 if (di.section(frame_section)) |section_data| {
1816 var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian };1785 var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian };
1817 while (fbr.pos < fbr.buf.len) {1786 while (fbr.pos < fbr.buf.len) {
1818 const entry_header = try EntryHeader.read(&fbr, null, frame_section);1787 const entry_header = try EntryHeader.read(&fbr, frame_section);
1819 switch (entry_header.type) {1788 switch (entry_header.type) {
1820 .cie => {1789 .cie => {
1821 const cie = try CommonInformationEntry.parse(1790 const cie = try CommonInformationEntry.parse(
...@@ -1988,8 +1957,8 @@ const UnitHeader = struct {...@@ -1988,8 +1957,8 @@ const UnitHeader = struct {
1988 unit_length: u64,1957 unit_length: u64,
1989};1958};
19901959
1991fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!UnitHeader {1960fn readUnitHeader(fbr: *FixedBufferReader) ScanError!UnitHeader {
1992 return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) {1961 return switch (try fbr.readInt(u32)) {
1993 0...0xfffffff0 - 1 => |unit_length| .{1962 0...0xfffffff0 - 1 => |unit_length| .{
1994 .format = .@"32",1963 .format = .@"32",
1995 .header_length = 4,1964 .header_length = 4,
...@@ -1999,7 +1968,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!U...@@ -1999,7 +1968,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!U
1999 0xffffffff => .{1968 0xffffffff => .{
2000 .format = .@"64",1969 .format = .@"64",
2001 .header_length = 12,1970 .header_length = 12,
2002 .unit_length = try if (opt_ma) |ma| fbr.readIntChecked(u64, ma) else fbr.readInt(u64),1971 .unit_length = try fbr.readInt(u64),
2003 },1972 },
2004 };1973 };
2005}1974}
lib/std/debug/Dwarf/expression.zig-12
...@@ -15,8 +15,6 @@ const assert = std.debug.assert;...@@ -15,8 +15,6 @@ const assert = std.debug.assert;
15pub const Context = struct {15pub const Context = struct {
16 /// The dwarf format of the section this expression is in16 /// The dwarf format of the section this expression is in
17 format: std.dwarf.Format = .@"32",17 format: std.dwarf.Format = .@"32",
18 /// If specified, any addresses will pass through before being accessed
19 memory_accessor: ?*std.debug.MemoryAccessor = null,
20 /// The compilation unit this expression relates to, if any18 /// The compilation unit this expression relates to, if any
21 compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,19 compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,
22 /// When evaluating a user-presented expression, this is the address of the object being evaluated20 /// When evaluating a user-presented expression, this is the address of the object being evaluated
...@@ -465,16 +463,6 @@ pub fn StackMachine(comptime options: Options) type {...@@ -465,16 +463,6 @@ pub fn StackMachine(comptime options: Options) type {
465 else => unreachable,463 else => unreachable,
466 };464 };
467465
468 if (context.memory_accessor) |memory_accessor| {
469 if (!switch (size) {
470 1 => memory_accessor.load(u8, addr) != null,
471 2 => memory_accessor.load(u16, addr) != null,
472 4 => memory_accessor.load(u32, addr) != null,
473 8 => memory_accessor.load(u64, addr) != null,
474 else => return error.InvalidExpression,
475 }) return error.InvalidExpression;
476 }
477
478 const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) {466 const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) {
479 1 => @as(*const u8, @ptrFromInt(addr)).*,467 1 => @as(*const u8, @ptrFromInt(addr)).*,
480 2 => @as(*const u16, @ptrFromInt(addr)).*,468 2 => @as(*const u16, @ptrFromInt(addr)).*,
lib/std/debug/FixedBufferReader.zig-23
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1//! Optimized for performance in debug builds.1//! Optimized for performance in debug builds.
22
3const std = @import("../std.zig");3const std = @import("../std.zig");
4const MemoryAccessor = std.debug.MemoryAccessor;
54
6const FixedBufferReader = @This();5const FixedBufferReader = @This();
76
...@@ -38,17 +37,6 @@ pub fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T {...@@ -38,17 +37,6 @@ pub fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T {
38 return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian);37 return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian);
39}38}
4039
41pub fn readIntChecked(
42 fbr: *FixedBufferReader,
43 comptime T: type,
44 ma: *MemoryAccessor,
45) Error!T {
46 if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null)
47 return error.InvalidBuffer;
48
49 return fbr.readInt(T);
50}
51
52pub fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {40pub fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
53 return std.leb.readUleb128(T, fbr);41 return std.leb.readUleb128(T, fbr);
54}42}
...@@ -64,17 +52,6 @@ pub fn readAddress(fbr: *FixedBufferReader, format: std.dwarf.Format) Error!u64...@@ -64,17 +52,6 @@ pub fn readAddress(fbr: *FixedBufferReader, format: std.dwarf.Format) Error!u64
64 };52 };
65}53}
6654
67pub fn readAddressChecked(
68 fbr: *FixedBufferReader,
69 format: std.dwarf.Format,
70 ma: *MemoryAccessor,
71) Error!u64 {
72 return switch (format) {
73 .@"32" => try fbr.readIntChecked(u32, ma),
74 .@"64" => try fbr.readIntChecked(u64, ma),
75 };
76}
77
78pub fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 {55pub fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 {
79 if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer;56 if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer;
80 defer fbr.pos += len;57 defer fbr.pos += len;
lib/std/debug/MemoryAccessor.zig deleted-141
...@@ -1,141 +0,0 @@
1//! Reads memory from any address of the current location using OS-specific
2//! syscalls, bypassing memory page protection. Useful for stack unwinding.
3
4const builtin = @import("builtin");
5const native_os = builtin.os.tag;
6
7const std = @import("../std.zig");
8const posix = std.posix;
9const File = std.fs.File;
10const page_size_min = std.heap.page_size_min;
11
12const MemoryAccessor = @This();
13
14var cached_pid: posix.pid_t = -1;
15
16mem: switch (native_os) {
17 .linux => File,
18 else => void,
19},
20
21pub const init: MemoryAccessor = .{
22 .mem = switch (native_os) {
23 .linux => .{ .handle = -1 },
24 else => {},
25 },
26};
27
28pub fn deinit(ma: *MemoryAccessor) void {
29 switch (native_os) {
30 .linux => switch (ma.mem.handle) {
31 -2, -1 => {},
32 else => ma.mem.close(),
33 },
34 else => {},
35 }
36 ma.* = undefined;
37}
38
39fn read(ma: *MemoryAccessor, address: usize, buf: []u8) bool {
40 switch (native_os) {
41 .linux => while (true) switch (ma.mem.handle) {
42 -2 => break,
43 -1 => {
44 const linux = std.os.linux;
45 const pid = switch (@atomicLoad(posix.pid_t, &cached_pid, .monotonic)) {
46 -1 => pid: {
47 const pid = linux.getpid();
48 @atomicStore(posix.pid_t, &cached_pid, pid, .monotonic);
49 break :pid pid;
50 },
51 else => |pid| pid,
52 };
53 const bytes_read = linux.process_vm_readv(
54 pid,
55 &.{.{ .base = buf.ptr, .len = buf.len }},
56 &.{.{ .base = @ptrFromInt(address), .len = buf.len }},
57 0,
58 );
59 switch (linux.E.init(bytes_read)) {
60 .SUCCESS => return bytes_read == buf.len,
61 .FAULT => return false,
62 .INVAL, .SRCH => unreachable, // own pid is always valid
63 .PERM => {}, // Known to happen in containers.
64 .NOMEM => {},
65 .NOSYS => {}, // QEMU is known not to implement this syscall.
66 else => unreachable, // unexpected
67 }
68 var path_buf: [
69 std.fmt.count("/proc/{d}/mem", .{std.math.minInt(posix.pid_t)})
70 ]u8 = undefined;
71 const path = std.fmt.bufPrint(&path_buf, "/proc/{d}/mem", .{pid}) catch
72 unreachable;
73 ma.mem = std.fs.openFileAbsolute(path, .{}) catch {
74 ma.mem.handle = -2;
75 break;
76 };
77 },
78 else => return (ma.mem.pread(buf, address) catch return false) == buf.len,
79 },
80 else => {},
81 }
82 if (!isValidMemory(address)) return false;
83 @memcpy(buf, @as([*]const u8, @ptrFromInt(address)));
84 return true;
85}
86
87pub fn load(ma: *MemoryAccessor, comptime Type: type, address: usize) ?Type {
88 var result: Type = undefined;
89 return if (ma.read(address, std.mem.asBytes(&result))) result else null;
90}
91
92pub fn isValidMemory(address: usize) bool {
93 // We are unable to determine validity of memory for freestanding targets
94 if (native_os == .freestanding or native_os == .other or native_os == .uefi) return true;
95
96 const page_size = std.heap.pageSize();
97 const aligned_address = address & ~(page_size - 1);
98 if (aligned_address == 0) return false;
99 const aligned_memory = @as([*]align(page_size_min) u8, @ptrFromInt(aligned_address))[0..page_size];
100
101 if (native_os == .windows) {
102 const windows = std.os.windows;
103
104 var memory_info: windows.MEMORY_BASIC_INFORMATION = undefined;
105
106 // The only error this function can throw is ERROR_INVALID_PARAMETER.
107 // supply an address that invalid i'll be thrown.
108 const rc = windows.VirtualQuery(@ptrCast(aligned_memory), &memory_info, aligned_memory.len) catch {
109 return false;
110 };
111
112 // Result code has to be bigger than zero (number of bytes written)
113 if (rc == 0) {
114 return false;
115 }
116
117 // Free pages cannot be read, they are unmapped
118 if (memory_info.State == windows.MEM_FREE) {
119 return false;
120 }
121
122 return true;
123 } else if (have_msync) {
124 posix.msync(aligned_memory, posix.MSF.ASYNC) catch |err| {
125 switch (err) {
126 error.UnmappedMemory => return false,
127 else => unreachable,
128 }
129 };
130
131 return true;
132 } else {
133 // We are unable to determine validity of memory on this target.
134 return true;
135 }
136}
137
138const have_msync = switch (native_os) {
139 .wasi, .emscripten, .windows => false,
140 else => true,
141};
lib/std/debug/SelfInfo.zig+11-36
...@@ -1159,7 +1159,6 @@ pub fn unwindFrameMachO(...@@ -1159,7 +1159,6 @@ pub fn unwindFrameMachO(
1159 allocator: Allocator,1159 allocator: Allocator,
1160 base_address: usize,1160 base_address: usize,
1161 context: *UnwindContext,1161 context: *UnwindContext,
1162 ma: *std.debug.MemoryAccessor,
1163 unwind_info: []const u8,1162 unwind_info: []const u8,
1164 eh_frame: ?[]const u8,1163 eh_frame: ?[]const u8,
1165) !usize {1164) !usize {
...@@ -1323,9 +1322,6 @@ pub fn unwindFrameMachO(...@@ -1323,9 +1322,6 @@ pub fn unwindFrameMachO(
1323 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;1322 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
1324 const new_sp = fp + 2 * @sizeOf(usize);1323 const new_sp = fp + 2 * @sizeOf(usize);
13251324
1326 // Verify the stack range we're about to read register values from
1327 if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo;
1328
1329 const ip_ptr = fp + @sizeOf(usize);1325 const ip_ptr = fp + @sizeOf(usize);
1330 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;1326 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
1331 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;1327 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
...@@ -1355,7 +1351,6 @@ pub fn unwindFrameMachO(...@@ -1355,7 +1351,6 @@ pub fn unwindFrameMachO(
1355 base_address +1351 base_address +
1356 entry.function_offset +1352 entry.function_offset +
1357 encoding.value.x86_64.frameless.stack.indirect.sub_offset;1353 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
1358 if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
13591354
1360 // `sub_offset_addr` points to the offset of the literal within the instruction1355 // `sub_offset_addr` points to the offset of the literal within the instruction
1361 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;1356 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
...@@ -1397,7 +1392,6 @@ pub fn unwindFrameMachO(...@@ -1397,7 +1392,6 @@ pub fn unwindFrameMachO(
1397 }1392 }
13981393
1399 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);1394 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
1400 if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo;
1401 for (0..reg_count) |i| {1395 for (0..reg_count) |i| {
1402 const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]);1396 const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]);
1403 (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;1397 (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
...@@ -1409,7 +1403,6 @@ pub fn unwindFrameMachO(...@@ -1409,7 +1403,6 @@ pub fn unwindFrameMachO(
14091403
1410 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;1404 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
1411 const new_sp = ip_ptr + @sizeOf(usize);1405 const new_sp = ip_ptr + @sizeOf(usize);
1412 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
14131406
1414 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;1407 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
1415 (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;1408 (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
...@@ -1417,7 +1410,7 @@ pub fn unwindFrameMachO(...@@ -1417,7 +1410,7 @@ pub fn unwindFrameMachO(
1417 break :blk new_ip;1410 break :blk new_ip;
1418 },1411 },
1419 .DWARF => {1412 .DWARF => {
1420 return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));1413 return unwindFrameMachODwarf(allocator, base_address, context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
1421 },1414 },
1422 },1415 },
1423 .aarch64, .aarch64_be => switch (encoding.mode.arm64) {1416 .aarch64, .aarch64_be => switch (encoding.mode.arm64) {
...@@ -1426,25 +1419,16 @@ pub fn unwindFrameMachO(...@@ -1426,25 +1419,16 @@ pub fn unwindFrameMachO(
1426 const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;1419 const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
1427 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;1420 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
1428 const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*;1421 const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*;
1429 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
1430 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;1422 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
1431 break :blk new_ip;1423 break :blk new_ip;
1432 },1424 },
1433 .DWARF => {1425 .DWARF => {
1434 return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));1426 return unwindFrameMachODwarf(allocator, base_address, context, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
1435 },1427 },
1436 .FRAME => blk: {1428 .FRAME => blk: {
1437 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;1429 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
1438 const new_sp = fp + 16;
1439 const ip_ptr = fp + @sizeOf(usize);1430 const ip_ptr = fp + @sizeOf(usize);
14401431
1441 const num_restored_pairs: usize =
1442 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
1443 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
1444 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
1445
1446 if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo;
1447
1448 var reg_addr = fp - @sizeOf(usize);1432 var reg_addr = fp - @sizeOf(usize);
1449 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| {1433 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| {
1450 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {1434 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
...@@ -1566,7 +1550,6 @@ pub fn unwindFrameDwarf(...@@ -1566,7 +1550,6 @@ pub fn unwindFrameDwarf(
1566 di: *Dwarf,1550 di: *Dwarf,
1567 base_address: usize,1551 base_address: usize,
1568 context: *UnwindContext,1552 context: *UnwindContext,
1569 ma: *std.debug.MemoryAccessor,
1570 explicit_fde_offset: ?usize,1553 explicit_fde_offset: ?usize,
1571) !usize {1554) !usize {
1572 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;1555 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;
...@@ -1584,14 +1567,14 @@ pub fn unwindFrameDwarf(...@@ -1584,14 +1567,14 @@ pub fn unwindFrameDwarf(
1584 .endian = di.endian,1567 .endian = di.endian,
1585 };1568 };
15861569
1587 const fde_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section);1570 const fde_entry_header = try Dwarf.EntryHeader.read(&fbr, dwarf_section);
1588 if (fde_entry_header.type != .fde) return error.MissingFDE;1571 if (fde_entry_header.type != .fde) return error.MissingFDE;
15891572
1590 const cie_offset = fde_entry_header.type.fde;1573 const cie_offset = fde_entry_header.type.fde;
1591 try fbr.seekTo(cie_offset);1574 try fbr.seekTo(cie_offset);
15921575
1593 fbr.endian = native_endian;1576 fbr.endian = native_endian;
1594 const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section);1577 const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, dwarf_section);
1595 if (cie_entry_header.type != .cie) return Dwarf.bad();1578 if (cie_entry_header.type != .cie) return Dwarf.bad();
15961579
1597 const cie = try Dwarf.CommonInformationEntry.parse(1580 const cie = try Dwarf.CommonInformationEntry.parse(
...@@ -1619,13 +1602,16 @@ pub fn unwindFrameDwarf(...@@ -1619,13 +1602,16 @@ pub fn unwindFrameDwarf(
1619 // back to loading `.eh_frame`/`.debug_frame` and using those from that point on.1602 // back to loading `.eh_frame`/`.debug_frame` and using those from that point on.
16201603
1621 if (di.eh_frame_hdr) |header| hdr: {1604 if (di.eh_frame_hdr) |header| hdr: {
1622 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;1605 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else {
1606 try di.scanCieFdeInfo(allocator, base_address);
1607 di.eh_frame_hdr = null;
1608 break :hdr;
1609 };
16231610
1624 var cie: Dwarf.CommonInformationEntry = undefined;1611 var cie: Dwarf.CommonInformationEntry = undefined;
1625 var fde: Dwarf.FrameDescriptionEntry = undefined;1612 var fde: Dwarf.FrameDescriptionEntry = undefined;
16261613
1627 header.findEntry(1614 header.findEntry(
1628 ma,
1629 eh_frame_len,1615 eh_frame_len,
1630 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),1616 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1631 context.pc,1617 context.pc,
...@@ -1669,7 +1655,6 @@ pub fn unwindFrameDwarf(...@@ -1669,7 +1655,6 @@ pub fn unwindFrameDwarf(
16691655
1670 var expression_context: Dwarf.expression.Context = .{1656 var expression_context: Dwarf.expression.Context = .{
1671 .format = cie.format,1657 .format = cie.format,
1672 .memory_accessor = ma,
1673 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,1658 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1674 .thread_context = context.thread_context,1659 .thread_context = context.thread_context,
1675 .reg_context = context.reg_context,1660 .reg_context = context.reg_context,
...@@ -1704,7 +1689,6 @@ pub fn unwindFrameDwarf(...@@ -1704,7 +1689,6 @@ pub fn unwindFrameDwarf(
1704 else => return error.InvalidCFARule,1689 else => return error.InvalidCFARule,
1705 };1690 };
17061691
1707 if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA;
1708 expression_context.cfa = context.cfa;1692 expression_context.cfa = context.cfa;
17091693
1710 // Buffering the modifications is done because copying the thread context is not portable,1694 // Buffering the modifications is done because copying the thread context is not portable,
...@@ -1740,12 +1724,7 @@ pub fn unwindFrameDwarf(...@@ -1740,12 +1724,7 @@ pub fn unwindFrameDwarf(
1740 .prev = prev,1724 .prev = prev,
1741 };1725 };
17421726
1743 try column.resolveValue(1727 try column.resolveValue(context, expression_context, src);
1744 context,
1745 expression_context,
1746 ma,
1747 src,
1748 );
1749 }1728 }
1750 }1729 }
17511730
...@@ -1833,7 +1812,6 @@ fn unwindFrameMachODwarf(...@@ -1833,7 +1812,6 @@ fn unwindFrameMachODwarf(
1833 allocator: Allocator,1812 allocator: Allocator,
1834 base_address: usize,1813 base_address: usize,
1835 context: *UnwindContext,1814 context: *UnwindContext,
1836 ma: *std.debug.MemoryAccessor,
1837 eh_frame: []const u8,1815 eh_frame: []const u8,
1838 fde_offset: usize,1816 fde_offset: usize,
1839) !usize {1817) !usize {
...@@ -1848,7 +1826,7 @@ fn unwindFrameMachODwarf(...@@ -1848,7 +1826,7 @@ fn unwindFrameMachODwarf(
1848 .owned = false,1826 .owned = false,
1849 };1827 };
18501828
1851 return unwindFrameDwarf(allocator, &di, base_address, context, ma, fde_offset);1829 return unwindFrameDwarf(allocator, &di, base_address, context, fde_offset);
1852}1830}
18531831
1854/// This is a virtual machine that runs DWARF call frame instructions.1832/// This is a virtual machine that runs DWARF call frame instructions.
...@@ -1898,7 +1876,6 @@ pub const VirtualMachine = struct {...@@ -1898,7 +1876,6 @@ pub const VirtualMachine = struct {
1898 self: Column,1876 self: Column,
1899 context: *SelfInfo.UnwindContext,1877 context: *SelfInfo.UnwindContext,
1900 expression_context: std.debug.Dwarf.expression.Context,1878 expression_context: std.debug.Dwarf.expression.Context,
1901 ma: *std.debug.MemoryAccessor,
1902 out: []u8,1879 out: []u8,
1903 ) !void {1880 ) !void {
1904 switch (self.rule) {1881 switch (self.rule) {
...@@ -1919,7 +1896,6 @@ pub const VirtualMachine = struct {...@@ -1919,7 +1896,6 @@ pub const VirtualMachine = struct {
1919 .offset => |offset| {1896 .offset => |offset| {
1920 if (context.cfa) |cfa| {1897 if (context.cfa) |cfa| {
1921 const addr = try applyOffset(cfa, offset);1898 const addr = try applyOffset(cfa, offset);
1922 if (ma.load(usize, addr) == null) return error.InvalidAddress;
1923 const ptr: *const usize = @ptrFromInt(addr);1899 const ptr: *const usize = @ptrFromInt(addr);
1924 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);1900 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
1925 } else return error.InvalidCFA;1901 } else return error.InvalidCFA;
...@@ -1942,7 +1918,6 @@ pub const VirtualMachine = struct {...@@ -1942,7 +1918,6 @@ pub const VirtualMachine = struct {
1942 break :blk v.generic;1918 break :blk v.generic;
1943 } else return error.NoExpressionValue;1919 } else return error.NoExpressionValue;
19441920
1945 if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress;
1946 const ptr: *usize = @ptrFromInt(addr);1921 const ptr: *usize = @ptrFromInt(addr);
1947 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);1922 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
1948 },1923 },