diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 0f60c2f841..5f28191221 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -148,11 +148,13 @@ pub const ucontext_t = extern struct { link: ?*ucontext_t, mcsize: u64, mcontext: *mcontext_t, + __mcontext_data: mcontext_t, }; pub const mcontext_t = extern struct { es: arch_bits.exception_state, ss: arch_bits.thread_state, + fs: arch_bits.float_state, }; extern "c" fn __error() *c_int; diff --git a/lib/std/c/darwin/x86_64.zig b/lib/std/c/darwin/x86_64.zig index c7671bc23a..db94840d9d 100644 --- a/lib/std/c/darwin/x86_64.zig +++ b/lib/std/c/darwin/x86_64.zig @@ -31,6 +31,29 @@ pub const thread_state = extern struct { gs: u64, }; +const stmm_reg = [16]u8; +const xmm_reg = [16]u8; +pub const float_state = extern struct { + reserved: [2]c_int, + fcw: u16, + fsw: u16, + ftw: u8, + rsrv1: u8, + fop: u16, + ip: u32, + cs: u16, + rsrv2: u16, + dp: u32, + ds: u16, + rsrv3: u16, + mxcsr: u32, + mxcsrmask: u32, + stmm: [8]stmm_reg, + xmm: [16]xmm_reg, + rsrv4: [96]u8, + reserved1: c_int, +}; + pub const THREAD_STATE = 4; pub const THREAD_STATE_COUNT: c.mach_msg_type_number_t = @sizeOf(thread_state) / @sizeOf(c_int); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index f231a4ac47..249674e0d4 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -444,7 +444,10 @@ pub inline fn getContext(context: *StackTraceContext) bool { return true; } - return have_getcontext and os.system.getcontext(context) == 0; + const result = have_getcontext and os.system.getcontext(context) == 0; + if (native_os == .macos) assert(context.mcsize == @sizeOf(std.c.mcontext_t)); + + return result; } pub const UnwindError = if (have_ucontext) @@ -553,6 +556,7 @@ pub const StackIterator = struct { if (native_os == .freestanding) return true; const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1))); + if (aligned_address == 0) return false; const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size]; if (native_os != .windows) { @@ -815,11 +819,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; try tty_config.setColor(out_stream, .dim); - if (err != error.MissingDebugInfo) { - try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); - } else { - try out_stream.print("Unwind information for {s} was not available, trace may be incomplete\n\n", .{module_name}); - } + try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); try tty_config.setColor(out_stream, .reset); } @@ -1309,6 +1309,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn return ModuleDebugInfo{ .base_address = undefined, + .vmaddr_slide = undefined, .mapped_memory = mapped_mem, .ofiles = ModuleDebugInfo.OFileTable.init(allocator), .symbols = symbols, @@ -1514,11 +1515,10 @@ pub const DebugInfo = struct { var i: u32 = 0; while (i < image_count) : (i += 1) { - const base_address = std.c._dyld_get_image_vmaddr_slide(i); - - if (address < base_address) continue; - const header = std.c._dyld_get_image_header(i) orelse continue; + const base_address = @intFromPtr(header); + if (address < base_address) continue; + const vmaddr_slide = std.c._dyld_get_image_vmaddr_slide(i); var it = macho.LoadCommandIterator{ .ncmds = header.ncmds, @@ -1527,14 +1527,16 @@ pub const DebugInfo = struct { @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)), )[0..header.sizeofcmds]), }; + while (it.next()) |cmd| switch (cmd.cmd()) { .SEGMENT_64 => { const segment_cmd = cmd.cast(macho.segment_command_64).?; - const rebased_address = address - base_address; + if (!mem.eql(u8, "__TEXT", segment_cmd.segName())) continue; + + const original_address = address - vmaddr_slide; const seg_start = segment_cmd.vmaddr; const seg_end = seg_start + segment_cmd.vmsize; - - if (rebased_address >= seg_start and rebased_address < seg_end) { + if (original_address >= seg_start and original_address < seg_end) { if (self.address_map.get(base_address)) |obj_di| { return obj_di; } @@ -1551,6 +1553,7 @@ pub const DebugInfo = struct { }; obj_di.* = try readMachODebugInfo(self.allocator, macho_file); obj_di.base_address = base_address; + obj_di.vmaddr_slide = vmaddr_slide; try self.address_map.putNoClobber(base_address, obj_di); @@ -1808,6 +1811,7 @@ pub const DebugInfo = struct { pub const ModuleDebugInfo = switch (native_os) { .macos, .ios, .watchos, .tvos => struct { base_address: usize, + vmaddr_slide: usize, mapped_memory: []align(mem.page_size) const u8, symbols: []const MachoSymbol, strings: [:0]const u8, @@ -1972,7 +1976,7 @@ pub const ModuleDebugInfo = switch (native_os) { } { nosuspend { // Translate the VA into an address into this object - const relocated_address = address - self.base_address; + const relocated_address = address - self.vmaddr_slide; // Find the .o file where this symbol is defined const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{ diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 807bc09a2f..d5f41a74bd 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -1705,28 +1705,53 @@ pub const DwarfInfo = struct { if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; - // Update the context with the previous frame's values - var next_ucontext = context.ucontext; + // Buffering the modifications is done because copying the ucontext is not portable, + // some implementations (ie. darwin) use internal pointers to the mcontext. + var arena = std.heap.ArenaAllocator.init(context.allocator); + defer arena.deinit(); + const update_allocator = arena.allocator(); + const RegisterUpdate = struct { + // Backed by ucontext + old_value: []u8, + // Backed by arena + new_value: []const u8, + prev: ?*@This(), + }; + + var update_tail: ?*RegisterUpdate = null; var has_next_ip = false; for (context.vm.rowColumns(row.*)) |column| { if (column.register) |register| { - const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx); if (register == cie.return_address_register) { has_next_ip = column.rule != .undefined; } + const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx); + const new_value = try update_allocator.alloc(u8, old_value.len); + + const prev = update_tail; + update_tail = try update_allocator.create(RegisterUpdate); + update_tail.?.* = .{ + .old_value = old_value, + .new_value = new_value, + .prev = prev, + }; + try column.resolveValue( context, compile_unit, &context.ucontext, context.reg_ctx, - dest, + new_value, ); } } - context.ucontext = next_ucontext; + while (update_tail) |tail| { + @memcpy(tail.old_value, tail.new_value); + update_tail = tail.prev; + } if (has_next_ip) { context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx)); diff --git a/lib/std/dwarf/abi.zig b/lib/std/dwarf/abi.zig index ab1213eb92..7b1418a293 100644 --- a/lib/std/dwarf/abi.zig +++ b/lib/std/dwarf/abi.zig @@ -68,38 +68,41 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext var m = &ucontext_ptr.mcontext; return switch (builtin.cpu.arch) { - .x86 => switch (reg_number) { - 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), - 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), - 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), - 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), - 4...5 => if (reg_ctx) |r| bytes: { - if (reg_number == 4) { - break :bytes if (r.eh_frame and r.is_macho) - mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) - else - mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); - } else { - break :bytes if (r.eh_frame and r.is_macho) - mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]) - else - mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); - } - } else error.RegisterContextRequired, - 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), - 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), - 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), - 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), - 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), - 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), - 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), - 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), - 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), - 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), - 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs - // TODO: Map TRAPNO, ERR, UESP - 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs - else => error.InvalidRegister, + .x86 => switch (builtin.os.tag) { + .linux, .netbsd, .solaris => switch (reg_number) { + 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), + 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), + 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), + 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), + 4...5 => if (reg_ctx) |r| bytes: { + if (reg_number == 4) { + break :bytes if (r.eh_frame and r.is_macho) + mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) + else + mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); + } else { + break :bytes if (r.eh_frame and r.is_macho) + mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]) + else + mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); + } + } else error.RegisterContextRequired, + 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), + 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), + 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), + 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), + 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), + 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), + 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), + 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), + 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), + 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), + 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs + // TODO: Map TRAPNO, ERR, UESP + 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs + else => error.InvalidRegister, + }, + else => error.UnimplementedOs, }, .x86_64 => switch (builtin.os.tag) { .linux, .netbsd, .solaris => switch (reg_number) {