mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
debug: fixup base address calculations for macho
dwarf: fixup x86 register mapping logic dwarf: change the register context update to update in-place instead of copying debug: always print the unwind error type
This commit is contained in:
parent
b85f84061a
commit
412cd789bf
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 .{
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user