| author | |
| committer | |
| log | 83f773fc64971160c526107da4590c1583eb84cf |
| tree | e3c10b765a27d5f1948dac05b80320710f8d1991 |
| parent | 77c09d16f9d992b19bc7cb2d2497f8009e0250ba |
| parent | e4a7b15852f1bfa9d4106164e5b7de0f1a877c3a |
| signature |
std.debug: delete MemoryAccessor6 files changed, 33 insertions(+), 267 deletions(-)
lib/std/debug.zig+8-10| ... | ... | @@ -14,7 +14,6 @@ const native_os = builtin.os.tag; |
| 14 | 14 | const native_endian = native_arch.endian(); |
| 15 | 15 | const Writer = std.io.Writer; |
| 16 | 16 | |
| 17 | pub const MemoryAccessor = @import("debug/MemoryAccessor.zig"); | |
| 18 | 17 | pub const FixedBufferReader = @import("debug/FixedBufferReader.zig"); |
| 19 | 18 | pub const Dwarf = @import("debug/Dwarf.zig"); |
| 20 | 19 | pub const Pdb = @import("debug/Pdb.zig"); |
| ... | ... | @@ -501,6 +500,11 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT |
| 501 | 500 | // TODO: This should use the DWARF unwinder if .eh_frame_hdr is available (so that full debug info parsing isn't required). |
| 502 | 501 | // A new path for loading SelfInfo needs to be created which will only attempt to parse in-memory sections, because |
| 503 | 502 | // stopping to load other debug info (ie. source line info) from disk here is not required for unwinding. |
| 503 | if (builtin.cpu.arch == .powerpc64) { | |
| 504 | // https://github.com/ziglang/zig/issues/24970 | |
| 505 | stack_trace.index = 0; | |
| 506 | return; | |
| 507 | } | |
| 504 | 508 | var it = StackIterator.init(first_address, null); |
| 505 | 509 | defer it.deinit(); |
| 506 | 510 | for (stack_trace.instruction_addresses, 0..) |*addr, i| { |
| ... | ... | @@ -773,7 +777,6 @@ pub const StackIterator = struct { |
| 773 | 777 | first_address: ?usize, |
| 774 | 778 | // Last known value of the frame pointer register. |
| 775 | 779 | fp: usize, |
| 776 | ma: MemoryAccessor = MemoryAccessor.init, | |
| 777 | 780 | |
| 778 | 781 | // When SelfInfo and a register context is available, this iterator can unwind |
| 779 | 782 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer), |
| ... | ... | @@ -795,7 +798,7 @@ pub const StackIterator = struct { |
| 795 | 798 | ::: .{ .memory = true }); |
| 796 | 799 | } |
| 797 | 800 | |
| 798 | return StackIterator{ | |
| 801 | return .{ | |
| 799 | 802 | .first_address = first_address, |
| 800 | 803 | // TODO: this is a workaround for #16876 |
| 801 | 804 | //.fp = fp orelse @frameAddress(), |
| ... | ... | @@ -825,7 +828,6 @@ pub const StackIterator = struct { |
| 825 | 828 | } |
| 826 | 829 | |
| 827 | 830 | pub fn deinit(it: *StackIterator) void { |
| 828 | it.ma.deinit(); | |
| 829 | 831 | if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit(); |
| 830 | 832 | } |
| 831 | 833 | |
| ... | ... | @@ -896,7 +898,6 @@ pub const StackIterator = struct { |
| 896 | 898 | unwind_state.debug_info.allocator, |
| 897 | 899 | module.base_address, |
| 898 | 900 | &unwind_state.dwarf_context, |
| 899 | &it.ma, | |
| 900 | 901 | unwind_info, |
| 901 | 902 | module.eh_frame, |
| 902 | 903 | )) |return_address| { |
| ... | ... | @@ -915,7 +916,6 @@ pub const StackIterator = struct { |
| 915 | 916 | di, |
| 916 | 917 | module.base_address, |
| 917 | 918 | &unwind_state.dwarf_context, |
| 918 | &it.ma, | |
| 919 | 919 | null, |
| 920 | 920 | ); |
| 921 | 921 | } else return error.MissingDebugInfo; |
| ... | ... | @@ -951,7 +951,7 @@ pub const StackIterator = struct { |
| 951 | 951 | |
| 952 | 952 | // Sanity check. |
| 953 | 953 | 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) catch | |
| 954 | const new_fp = math.add(usize, @as(*usize, @ptrFromInt(fp)).*, fp_bias) catch | |
| 955 | 955 | return null; |
| 956 | 956 | |
| 957 | 957 | // Sanity check: the stack grows down thus all the parent frames must be |
| ... | ... | @@ -959,8 +959,7 @@ pub const StackIterator = struct { |
| 959 | 959 | // A zero frame pointer often signals this is the last frame, that case |
| 960 | 960 | // is gracefully handled by the next call to next_internal. |
| 961 | 961 | 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) orelse | |
| 963 | return null; | |
| 962 | const new_pc = @as(*usize, @ptrFromInt(math.add(usize, fp, pc_offset) catch return null)).*; | |
| 964 | 963 | |
| 965 | 964 | it.fp = new_fp; |
| 966 | 965 | |
| ... | ... | @@ -1774,7 +1773,6 @@ pub inline fn inValgrind() bool { |
| 1774 | 1773 | |
| 1775 | 1774 | test { |
| 1776 | 1775 | _ = &Dwarf; |
| 1777 | _ = &MemoryAccessor; | |
| 1778 | 1776 | _ = &FixedBufferReader; |
| 1779 | 1777 | _ = &Pdb; |
| 1780 | 1778 | _ = &SelfInfo; |
lib/std/debug/Dwarf.zig+14-45| ... | ... | @@ -24,7 +24,6 @@ const UT = DW.UT; |
| 24 | 24 | const assert = std.debug.assert; |
| 25 | 25 | const cast = std.math.cast; |
| 26 | 26 | const maxInt = std.math.maxInt; |
| 27 | const MemoryAccessor = std.debug.MemoryAccessor; | |
| 28 | 27 | const Path = std.Build.Cache.Path; |
| 29 | 28 | const FixedBufferReader = std.debug.FixedBufferReader; |
| 30 | 29 | const ArrayList = std.ArrayList; |
| ... | ... | @@ -349,29 +348,9 @@ pub const ExceptionFrameHeader = struct { |
| 349 | 348 | }; |
| 350 | 349 | } |
| 351 | 350 | |
| 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 | 351 | pub fn findEntry( |
| 372 | 352 | self: ExceptionFrameHeader, |
| 373 | ma: *MemoryAccessor, | |
| 374 | eh_frame_len: ?usize, | |
| 353 | eh_frame_len: usize, | |
| 375 | 354 | eh_frame_hdr_ptr: usize, |
| 376 | 355 | pc: usize, |
| 377 | 356 | cie: *CommonInformationEntry, |
| ... | ... | @@ -421,8 +400,7 @@ pub const ExceptionFrameHeader = struct { |
| 421 | 400 | |
| 422 | 401 | if (fde_ptr < self.eh_frame_ptr) return bad(); |
| 423 | 402 | |
| 424 | // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor | |
| 425 | const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)]; | |
| 403 | const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0..eh_frame_len]; | |
| 426 | 404 | |
| 427 | 405 | const fde_offset = fde_ptr - self.eh_frame_ptr; |
| 428 | 406 | var eh_frame_fbr: FixedBufferReader = .{ |
| ... | ... | @@ -431,15 +409,13 @@ pub const ExceptionFrameHeader = struct { |
| 431 | 409 | .endian = native_endian, |
| 432 | 410 | }; |
| 433 | 411 | |
| 434 | const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .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(); | |
| 412 | const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame); | |
| 436 | 413 | if (fde_entry_header.type != .fde) return bad(); |
| 437 | 414 | |
| 438 | 415 | // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable |
| 439 | 416 | const cie_offset = fde_entry_header.type.fde; |
| 440 | 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); | |
| 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(); | |
| 418 | const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame); | |
| 443 | 419 | if (cie_entry_header.type != .cie) return bad(); |
| 444 | 420 | |
| 445 | 421 | cie.* = try CommonInformationEntry.parse( |
| ... | ... | @@ -486,15 +462,11 @@ pub const EntryHeader = struct { |
| 486 | 462 | |
| 487 | 463 | /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure. |
| 488 | 464 | /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections. |
| 489 | pub fn read( | |
| 490 | fbr: *FixedBufferReader, | |
| 491 | opt_ma: ?*MemoryAccessor, | |
| 492 | dwarf_section: Section.Id, | |
| 493 | ) !EntryHeader { | |
| 465 | pub fn read(fbr: *FixedBufferReader, dwarf_section: Section.Id) !EntryHeader { | |
| 494 | 466 | assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); |
| 495 | 467 | |
| 496 | 468 | const length_offset = fbr.pos; |
| 497 | const unit_header = try readUnitHeader(fbr, opt_ma); | |
| 469 | const unit_header = try readUnitHeader(fbr); | |
| 498 | 470 | const unit_length = cast(usize, unit_header.unit_length) orelse return bad(); |
| 499 | 471 | if (unit_length == 0) return .{ |
| 500 | 472 | .length_offset = length_offset, |
| ... | ... | @@ -506,10 +478,7 @@ pub const EntryHeader = struct { |
| 506 | 478 | const end_offset = start_offset + unit_length; |
| 507 | 479 | defer fbr.pos = end_offset; |
| 508 | 480 | |
| 509 | const id = try if (opt_ma) |ma| | |
| 510 | fbr.readAddressChecked(unit_header.format, ma) | |
| 511 | else | |
| 512 | fbr.readAddress(unit_header.format); | |
| 481 | const id = try fbr.readAddress(unit_header.format); | |
| 513 | 482 | const entry_bytes = fbr.buf[fbr.pos..end_offset]; |
| 514 | 483 | const cie_id: u64 = switch (dwarf_section) { |
| 515 | 484 | .eh_frame => CommonInformationEntry.eh_id, |
| ... | ... | @@ -856,7 +825,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void { |
| 856 | 825 | while (this_unit_offset < fbr.buf.len) { |
| 857 | 826 | try fbr.seekTo(this_unit_offset); |
| 858 | 827 | |
| 859 | const unit_header = try readUnitHeader(&fbr, null); | |
| 828 | const unit_header = try readUnitHeader(&fbr); | |
| 860 | 829 | if (unit_header.unit_length == 0) return; |
| 861 | 830 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 862 | 831 | |
| ... | ... | @@ -1045,7 +1014,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void { |
| 1045 | 1014 | while (this_unit_offset < fbr.buf.len) { |
| 1046 | 1015 | try fbr.seekTo(this_unit_offset); |
| 1047 | 1016 | |
| 1048 | const unit_header = try readUnitHeader(&fbr, null); | |
| 1017 | const unit_header = try readUnitHeader(&fbr); | |
| 1049 | 1018 | if (unit_header.unit_length == 0) return; |
| 1050 | 1019 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 1051 | 1020 | |
| ... | ... | @@ -1427,7 +1396,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) ! |
| 1427 | 1396 | }; |
| 1428 | 1397 | try fbr.seekTo(line_info_offset); |
| 1429 | 1398 | |
| 1430 | const unit_header = try readUnitHeader(&fbr, null); | |
| 1399 | const unit_header = try readUnitHeader(&fbr); | |
| 1431 | 1400 | if (unit_header.unit_length == 0) return missing(); |
| 1432 | 1401 | |
| 1433 | 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 | 1784 | if (di.section(frame_section)) |section_data| { |
| 1816 | 1785 | var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian }; |
| 1817 | 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 | 1788 | switch (entry_header.type) { |
| 1820 | 1789 | .cie => { |
| 1821 | 1790 | const cie = try CommonInformationEntry.parse( |
| ... | ... | @@ -1988,8 +1957,8 @@ const UnitHeader = struct { |
| 1988 | 1957 | unit_length: u64, |
| 1989 | 1958 | }; |
| 1990 | 1959 | |
| 1991 | fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!UnitHeader { | |
| 1992 | return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) { | |
| 1960 | fn readUnitHeader(fbr: *FixedBufferReader) ScanError!UnitHeader { | |
| 1961 | return switch (try fbr.readInt(u32)) { | |
| 1993 | 1962 | 0...0xfffffff0 - 1 => |unit_length| .{ |
| 1994 | 1963 | .format = .@"32", |
| 1995 | 1964 | .header_length = 4, |
| ... | ... | @@ -1999,7 +1968,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*MemoryAccessor) ScanError!U |
| 1999 | 1968 | 0xffffffff => .{ |
| 2000 | 1969 | .format = .@"64", |
| 2001 | 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 | 15 | pub const Context = struct { |
| 16 | 16 | /// The dwarf format of the section this expression is in |
| 17 | 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 | 18 | /// The compilation unit this expression relates to, if any |
| 21 | 19 | compile_unit: ?*const std.debug.Dwarf.CompileUnit = null, |
| 22 | 20 | /// 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 | 463 | else => unreachable, |
| 466 | 464 | }; |
| 467 | 465 | |
| 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 | 466 | const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) { |
| 479 | 467 | 1 => @as(*const u8, @ptrFromInt(addr)).*, |
| 480 | 468 | 2 => @as(*const u16, @ptrFromInt(addr)).*, |
lib/std/debug/FixedBufferReader.zig-23| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | //! Optimized for performance in debug builds. |
| 2 | 2 | |
| 3 | 3 | const std = @import("../std.zig"); |
| 4 | const MemoryAccessor = std.debug.MemoryAccessor; | |
| 5 | 4 | |
| 6 | 5 | const FixedBufferReader = @This(); |
| 7 | 6 | |
| ... | ... | @@ -38,17 +37,6 @@ pub fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T { |
| 38 | 37 | return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian); |
| 39 | 38 | } |
| 40 | 39 | |
| 41 | pub 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 | ||
| 52 | 40 | pub fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T { |
| 53 | 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 | 52 | }; |
| 65 | 53 | } |
| 66 | 54 | |
| 67 | pub 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 | ||
| 78 | 55 | pub fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 { |
| 79 | 56 | if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer; |
| 80 | 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 | ||
| 4 | const builtin = @import("builtin"); | |
| 5 | const native_os = builtin.os.tag; | |
| 6 | ||
| 7 | const std = @import("../std.zig"); | |
| 8 | const posix = std.posix; | |
| 9 | const File = std.fs.File; | |
| 10 | const page_size_min = std.heap.page_size_min; | |
| 11 | ||
| 12 | const MemoryAccessor = @This(); | |
| 13 | ||
| 14 | var cached_pid: posix.pid_t = -1; | |
| 15 | ||
| 16 | mem: switch (native_os) { | |
| 17 | .linux => File, | |
| 18 | else => void, | |
| 19 | }, | |
| 20 | ||
| 21 | pub const init: MemoryAccessor = .{ | |
| 22 | .mem = switch (native_os) { | |
| 23 | .linux => .{ .handle = -1 }, | |
| 24 | else => {}, | |
| 25 | }, | |
| 26 | }; | |
| 27 | ||
| 28 | pub 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 | ||
| 39 | fn 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 | ||
| 87 | pub 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 | ||
| 92 | pub 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 | ||
| 138 | const 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 | 1159 | allocator: Allocator, |
| 1160 | 1160 | base_address: usize, |
| 1161 | 1161 | context: *UnwindContext, |
| 1162 | ma: *std.debug.MemoryAccessor, | |
| 1163 | 1162 | unwind_info: []const u8, |
| 1164 | 1163 | eh_frame: ?[]const u8, |
| 1165 | 1164 | ) !usize { |
| ... | ... | @@ -1323,9 +1322,6 @@ pub fn unwindFrameMachO( |
| 1323 | 1322 | const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*; |
| 1324 | 1323 | const new_sp = fp + 2 * @sizeOf(usize); |
| 1325 | 1324 | |
| 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 | 1325 | const ip_ptr = fp + @sizeOf(usize); |
| 1330 | 1326 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 1331 | 1327 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; |
| ... | ... | @@ -1355,7 +1351,6 @@ pub fn unwindFrameMachO( |
| 1355 | 1351 | base_address + |
| 1356 | 1352 | entry.function_offset + |
| 1357 | 1353 | encoding.value.x86_64.frameless.stack.indirect.sub_offset; |
| 1358 | if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo; | |
| 1359 | 1354 | |
| 1360 | 1355 | // `sub_offset_addr` points to the offset of the literal within the instruction |
| 1361 | 1356 | const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*; |
| ... | ... | @@ -1397,7 +1392,6 @@ pub fn unwindFrameMachO( |
| 1397 | 1392 | } |
| 1398 | 1393 | |
| 1399 | 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 | 1395 | for (0..reg_count) |i| { |
| 1402 | 1396 | const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]); |
| 1403 | 1397 | (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| ... | ... | @@ -1409,7 +1403,6 @@ pub fn unwindFrameMachO( |
| 1409 | 1403 | |
| 1410 | 1404 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 1411 | 1405 | const new_sp = ip_ptr + @sizeOf(usize); |
| 1412 | if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo; | |
| 1413 | 1406 | |
| 1414 | 1407 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; |
| 1415 | 1408 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; |
| ... | ... | @@ -1417,7 +1410,7 @@ pub fn unwindFrameMachO( |
| 1417 | 1410 | break :blk new_ip; |
| 1418 | 1411 | }, |
| 1419 | 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 | 1416 | .aarch64, .aarch64_be => switch (encoding.mode.arm64) { |
| ... | ... | @@ -1426,25 +1419,16 @@ pub fn unwindFrameMachO( |
| 1426 | 1419 | const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*; |
| 1427 | 1420 | const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; |
| 1428 | 1421 | const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*; |
| 1429 | if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo; | |
| 1430 | 1422 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; |
| 1431 | 1423 | break :blk new_ip; |
| 1432 | 1424 | }, |
| 1433 | 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 | 1428 | .FRAME => blk: { |
| 1437 | 1429 | const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*; |
| 1438 | const new_sp = fp + 16; | |
| 1439 | 1430 | const ip_ptr = fp + @sizeOf(usize); |
| 1440 | 1431 | |
| 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 | 1432 | var reg_addr = fp - @sizeOf(usize); |
| 1449 | 1433 | inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| { |
| 1450 | 1434 | if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) { |
| ... | ... | @@ -1566,7 +1550,6 @@ pub fn unwindFrameDwarf( |
| 1566 | 1550 | di: *Dwarf, |
| 1567 | 1551 | base_address: usize, |
| 1568 | 1552 | context: *UnwindContext, |
| 1569 | ma: *std.debug.MemoryAccessor, | |
| 1570 | 1553 | explicit_fde_offset: ?usize, |
| 1571 | 1554 | ) !usize { |
| 1572 | 1555 | if (!supports_unwinding) return error.UnsupportedCpuArchitecture; |
| ... | ... | @@ -1584,14 +1567,14 @@ pub fn unwindFrameDwarf( |
| 1584 | 1567 | .endian = di.endian, |
| 1585 | 1568 | }; |
| 1586 | 1569 | |
| 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 | 1571 | if (fde_entry_header.type != .fde) return error.MissingFDE; |
| 1589 | 1572 | |
| 1590 | 1573 | const cie_offset = fde_entry_header.type.fde; |
| 1591 | 1574 | try fbr.seekTo(cie_offset); |
| 1592 | 1575 | |
| 1593 | 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 | 1578 | if (cie_entry_header.type != .cie) return Dwarf.bad(); |
| 1596 | 1579 | |
| 1597 | 1580 | const cie = try Dwarf.CommonInformationEntry.parse( |
| ... | ... | @@ -1619,13 +1602,16 @@ pub fn unwindFrameDwarf( |
| 1619 | 1602 | // back to loading `.eh_frame`/`.debug_frame` and using those from that point on. |
| 1620 | 1603 | |
| 1621 | 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 | }; | |
| 1623 | 1610 | |
| 1624 | 1611 | var cie: Dwarf.CommonInformationEntry = undefined; |
| 1625 | 1612 | var fde: Dwarf.FrameDescriptionEntry = undefined; |
| 1626 | 1613 | |
| 1627 | 1614 | header.findEntry( |
| 1628 | ma, | |
| 1629 | 1615 | eh_frame_len, |
| 1630 | 1616 | @intFromPtr(di.section(.eh_frame_hdr).?.ptr), |
| 1631 | 1617 | context.pc, |
| ... | ... | @@ -1669,7 +1655,6 @@ pub fn unwindFrameDwarf( |
| 1669 | 1655 | |
| 1670 | 1656 | var expression_context: Dwarf.expression.Context = .{ |
| 1671 | 1657 | .format = cie.format, |
| 1672 | .memory_accessor = ma, | |
| 1673 | 1658 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, |
| 1674 | 1659 | .thread_context = context.thread_context, |
| 1675 | 1660 | .reg_context = context.reg_context, |
| ... | ... | @@ -1704,7 +1689,6 @@ pub fn unwindFrameDwarf( |
| 1704 | 1689 | else => return error.InvalidCFARule, |
| 1705 | 1690 | }; |
| 1706 | 1691 | |
| 1707 | if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA; | |
| 1708 | 1692 | expression_context.cfa = context.cfa; |
| 1709 | 1693 | |
| 1710 | 1694 | // Buffering the modifications is done because copying the thread context is not portable, |
| ... | ... | @@ -1740,12 +1724,7 @@ pub fn unwindFrameDwarf( |
| 1740 | 1724 | .prev = prev, |
| 1741 | 1725 | }; |
| 1742 | 1726 | |
| 1743 | try column.resolveValue( | |
| 1744 | context, | |
| 1745 | expression_context, | |
| 1746 | ma, | |
| 1747 | src, | |
| 1748 | ); | |
| 1727 | try column.resolveValue(context, expression_context, src); | |
| 1749 | 1728 | } |
| 1750 | 1729 | } |
| 1751 | 1730 | |
| ... | ... | @@ -1833,7 +1812,6 @@ fn unwindFrameMachODwarf( |
| 1833 | 1812 | allocator: Allocator, |
| 1834 | 1813 | base_address: usize, |
| 1835 | 1814 | context: *UnwindContext, |
| 1836 | ma: *std.debug.MemoryAccessor, | |
| 1837 | 1815 | eh_frame: []const u8, |
| 1838 | 1816 | fde_offset: usize, |
| 1839 | 1817 | ) !usize { |
| ... | ... | @@ -1848,7 +1826,7 @@ fn unwindFrameMachODwarf( |
| 1848 | 1826 | .owned = false, |
| 1849 | 1827 | }; |
| 1850 | 1828 | |
| 1851 | return unwindFrameDwarf(allocator, &di, base_address, context, ma, fde_offset); | |
| 1829 | return unwindFrameDwarf(allocator, &di, base_address, context, fde_offset); | |
| 1852 | 1830 | } |
| 1853 | 1831 | |
| 1854 | 1832 | /// This is a virtual machine that runs DWARF call frame instructions. |
| ... | ... | @@ -1898,7 +1876,6 @@ pub const VirtualMachine = struct { |
| 1898 | 1876 | self: Column, |
| 1899 | 1877 | context: *SelfInfo.UnwindContext, |
| 1900 | 1878 | expression_context: std.debug.Dwarf.expression.Context, |
| 1901 | ma: *std.debug.MemoryAccessor, | |
| 1902 | 1879 | out: []u8, |
| 1903 | 1880 | ) !void { |
| 1904 | 1881 | switch (self.rule) { |
| ... | ... | @@ -1919,7 +1896,6 @@ pub const VirtualMachine = struct { |
| 1919 | 1896 | .offset => |offset| { |
| 1920 | 1897 | if (context.cfa) |cfa| { |
| 1921 | 1898 | const addr = try applyOffset(cfa, offset); |
| 1922 | if (ma.load(usize, addr) == null) return error.InvalidAddress; | |
| 1923 | 1899 | const ptr: *const usize = @ptrFromInt(addr); |
| 1924 | 1900 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); |
| 1925 | 1901 | } else return error.InvalidCFA; |
| ... | ... | @@ -1942,7 +1918,6 @@ pub const VirtualMachine = struct { |
| 1942 | 1918 | break :blk v.generic; |
| 1943 | 1919 | } else return error.NoExpressionValue; |
| 1944 | 1920 | |
| 1945 | if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress; | |
| 1946 | 1921 | const ptr: *usize = @ptrFromInt(addr); |
| 1947 | 1922 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); |
| 1948 | 1923 | }, |