mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
debug: fix initialization of the optional fields on StackIterator
dwarf: documentation fixups target: enable unwind tables on macho
This commit is contained in:
parent
5dfb159e15
commit
891fa3b8b5
@ -159,7 +159,7 @@ pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void {
|
||||
relocateContext(dest);
|
||||
}
|
||||
|
||||
/// Updates any internal points in the context to reflect its current location
|
||||
/// Updates any internal pointers in the context to reflect its current location
|
||||
pub fn relocateContext(context: *ThreadContext) void {
|
||||
return switch (native_os) {
|
||||
.macos => {
|
||||
@ -176,7 +176,7 @@ pub const have_getcontext = @hasDecl(os.system, "getcontext") and
|
||||
});
|
||||
|
||||
/// Capture the current context. The register values in the context will reflect the
|
||||
/// state after the platform `getcontext` function returned.
|
||||
/// state after the platform `getcontext` function returns.
|
||||
///
|
||||
/// It is valid to call this if the platform doesn't have context capturing support,
|
||||
/// in that case false will be returned.
|
||||
@ -229,7 +229,7 @@ pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {
|
||||
|
||||
var it = StackIterator.initWithContext(null, debug_info, context) catch return;
|
||||
defer it.deinit();
|
||||
printSourceAtAddress(debug_info, stderr, it.dwarf_context.pc, tty_config) catch return;
|
||||
printSourceAtAddress(debug_info, stderr, it.unwind_state.?.dwarf_context.pc, tty_config) catch return;
|
||||
|
||||
while (it.next()) |return_address| {
|
||||
if (it.getLastError()) |unwind_error|
|
||||
@ -487,11 +487,13 @@ pub const StackIterator = struct {
|
||||
fp: usize,
|
||||
|
||||
// When DebugInfo and a register context is available, this iterator can unwind
|
||||
// stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).
|
||||
debug_info: ?*DebugInfo,
|
||||
dwarf_context: if (have_ucontext) DW.UnwindContext else void = undefined,
|
||||
last_error: if (have_ucontext) ?UnwindError else void = undefined,
|
||||
last_error_address: if (have_ucontext) usize else void = undefined,
|
||||
// stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer),
|
||||
// using DWARF and MachO unwind info.
|
||||
unwind_state: if (have_ucontext) ?struct {
|
||||
debug_info: *DebugInfo,
|
||||
dwarf_context: DW.UnwindContext,
|
||||
last_error: ?UnwindError = null,
|
||||
} else void = if (have_ucontext) null else {},
|
||||
|
||||
pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
|
||||
if (native_arch == .sparc64) {
|
||||
@ -504,32 +506,33 @@ pub const StackIterator = struct {
|
||||
return StackIterator{
|
||||
.first_address = first_address,
|
||||
.fp = fp orelse @frameAddress(),
|
||||
.debug_info = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator {
|
||||
var iterator = init(first_address, null);
|
||||
iterator.debug_info = debug_info;
|
||||
iterator.dwarf_context = try DW.UnwindContext.init(debug_info.allocator, context, &isValidMemory);
|
||||
iterator.last_error = null;
|
||||
iterator.unwind_state = .{
|
||||
.debug_info = debug_info,
|
||||
.dwarf_context = try DW.UnwindContext.init(debug_info.allocator, context, &isValidMemory),
|
||||
};
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *StackIterator) void {
|
||||
if (have_ucontext and self.debug_info != null) self.dwarf_context.deinit();
|
||||
if (have_ucontext and self.unwind_state != null) self.unwind_state.?.dwarf_context.deinit();
|
||||
}
|
||||
|
||||
pub fn getLastError(self: *StackIterator) ?struct {
|
||||
address: usize,
|
||||
err: UnwindError,
|
||||
address: usize,
|
||||
} {
|
||||
if (have_ucontext) {
|
||||
if (self.last_error) |err| {
|
||||
self.last_error = null;
|
||||
if (!have_ucontext) return null;
|
||||
if (self.unwind_state) |*unwind_state| {
|
||||
if (unwind_state.last_error) |err| {
|
||||
return .{
|
||||
.address = self.last_error_address,
|
||||
.err = err,
|
||||
.address = unwind_state.dwarf_context.pc,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -620,13 +623,14 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
fn next_unwind(self: *StackIterator) !usize {
|
||||
const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
|
||||
const unwind_state = &self.unwind_state.?;
|
||||
const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);
|
||||
switch (native_os) {
|
||||
.macos, .ios, .watchos, .tvos => {
|
||||
// __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
|
||||
// via DWARF before attempting to use the compact unwind info will produce incorrect results.
|
||||
if (module.unwind_info) |unwind_info| {
|
||||
if (macho.unwindFrame(&self.dwarf_context, unwind_info, module.base_address)) |return_address| {
|
||||
if (macho.unwindFrame(&unwind_state.dwarf_context, unwind_info, module.base_address)) |return_address| {
|
||||
return return_address;
|
||||
} else |err| {
|
||||
if (err != error.RequiresDWARFUnwind) return err;
|
||||
@ -636,23 +640,25 @@ pub const StackIterator = struct {
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
|
||||
return di.unwindFrame(&self.dwarf_context, module.base_address);
|
||||
if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {
|
||||
return di.unwindFrame(&unwind_state.dwarf_context, module.base_address);
|
||||
} else return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
fn next_internal(self: *StackIterator) ?usize {
|
||||
if (have_ucontext and self.debug_info != null) {
|
||||
if (self.dwarf_context.pc == 0) return null;
|
||||
if (self.next_unwind()) |return_address| {
|
||||
return return_address;
|
||||
} else |err| {
|
||||
self.last_error = err;
|
||||
self.last_error_address = self.dwarf_context.pc;
|
||||
if (have_ucontext) {
|
||||
if (self.unwind_state) |*unwind_state| {
|
||||
if (unwind_state.dwarf_context.pc == 0) return null;
|
||||
if (unwind_state.last_error == null) {
|
||||
if (self.next_unwind()) |return_address| {
|
||||
return return_address;
|
||||
} else |err| {
|
||||
unwind_state.last_error = err;
|
||||
|
||||
// Fall back to fp unwinding on the first failure, as the register context won't have been updated
|
||||
self.fp = self.dwarf_context.getFp() catch 0;
|
||||
self.debug_info = null;
|
||||
// Fall back to fp-based unwinding on the first failure
|
||||
self.fp = unwind_state.dwarf_context.getFp() catch 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -862,16 +868,12 @@ pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: us
|
||||
pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void {
|
||||
const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
|
||||
else => {
|
||||
return err;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
|
||||
const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
|
||||
else => {
|
||||
return err;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
defer symbol_info.deinit(debug_info.allocator);
|
||||
|
||||
|
||||
@ -1639,7 +1639,7 @@ pub const DwarfInfo = struct {
|
||||
// In order to support reading .eh_frame from the ELF file (vs using the already-mapped section),
|
||||
// scanAllUnwindInfo has already mapped any pc-relative offsets such that they we be relative to zero
|
||||
// instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly
|
||||
// as pointers will be decoded relative to the alreayd-mapped .eh_frame.
|
||||
// as pointers will be decoded relative to the already-mapped .eh_frame.
|
||||
var mapped_pc: usize = undefined;
|
||||
if (di.eh_frame_hdr) |header| {
|
||||
const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
|
||||
@ -1766,8 +1766,8 @@ pub const DwarfInfo = struct {
|
||||
mem.writeIntSliceNative(usize, try abi.regBytes(context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?);
|
||||
|
||||
// The call instruction will have pushed the address of the instruction that follows the call as the return address
|
||||
// However, this return address may be past the end of the function if the caller was `noreturn`.
|
||||
// TODO: Check this on non-x86_64
|
||||
// However, this return address may be past the end of the function if the caller was `noreturn`. By subtracting one,
|
||||
// then `context.pc` will always point to an instruction within the FDE for the previous function.
|
||||
const return_address = context.pc;
|
||||
if (context.pc > 0) context.pc -= 1;
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ pub fn ipRegNum() u8 {
|
||||
|
||||
pub fn fpRegNum(reg_context: RegisterContext) u8 {
|
||||
return switch (builtin.cpu.arch) {
|
||||
// GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
|
||||
// GCC on OS X historicaly did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
|
||||
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
|
||||
.x86_64 => 6,
|
||||
.arm => 11,
|
||||
@ -75,6 +75,7 @@ fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a pointer to a register stored in a ThreadContext, preserving the pointer attributes of the context.
|
||||
pub fn regValueNative(
|
||||
comptime T: type,
|
||||
thread_context_ptr: anytype,
|
||||
@ -343,9 +344,11 @@ pub fn regBytes(
|
||||
|
||||
/// Returns the ABI-defined default value this register has in the unwinding table
|
||||
/// before running any of the CIE instructions. The DWARF spec defines these values
|
||||
// to be undefined, but allows ABI authors to override that default.
|
||||
/// to be undefined, but allows ABI authors to override that default.
|
||||
pub fn getRegDefaultValue(reg_number: u8, out: []u8) void {
|
||||
// TODO: Implement any ABI-specific rules for the default value for registers
|
||||
|
||||
// Implement any ABI-specific rules here
|
||||
|
||||
_ = reg_number;
|
||||
@memset(out, undefined);
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ pub const ExpressionContext = struct {
|
||||
/// This expression is from a DWARF64 section
|
||||
is_64: bool = false,
|
||||
|
||||
/// If specified, any addresses will pass through this function before being
|
||||
/// If specified, any addresses will pass through this function before being acccessed
|
||||
isValidMemory: ?*const fn (address: usize) bool = null,
|
||||
|
||||
/// The compilation unit this expression relates to, if any
|
||||
@ -1024,9 +1024,6 @@ pub fn Builder(comptime options: ExpressionOptions) type {
|
||||
try writer.writeAll(value_bytes);
|
||||
}
|
||||
|
||||
// pub fn writeImplicitPointer(writer: anytype, ) void {
|
||||
// }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -4695,7 +4695,7 @@ else
|
||||
/// processes.
|
||||
RTPRIO,
|
||||
|
||||
/// Maximum CPU time in µs that a process scheduled under a real-time
|
||||
/// Maximum CPU time in µs that a process scheduled under a real-time
|
||||
/// scheduling policy may consume without making a blocking system
|
||||
/// call before being forcibly descheduled.
|
||||
RTTIME,
|
||||
|
||||
@ -510,7 +510,7 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {
|
||||
}
|
||||
|
||||
pub fn needUnwindTables(target: std.Target) bool {
|
||||
return target.os.tag == .windows;
|
||||
return target.os.tag == .windows or target.ofmt == .macho;
|
||||
}
|
||||
|
||||
pub fn defaultAddressSpace(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user