Skip to content

Commit 09ff03a

Browse files
kcbannerkubkon
andauthored
debug: replace RtlCaptureStackBackTrace (which was spuriously failing) with a new implementation which uses RtlVirtualUnwind instead (#12740)
windows: add RtlCaptureContext, RtlLookupFunctionEntry, RtlVirtualUnwind and supporting types windows: fix alignment of CONTEXT structs to match winnt.h as required by RtlCaptureContext (fxsave instr) windows aarch64: fix __chkstk being defined twice if libc is not linked on msvc Co-authored-by: Jakub Konka <[email protected]>
1 parent a3e2ee0 commit 09ff03a

File tree

4 files changed

+211
-13
lines changed

4 files changed

+211
-13
lines changed

lib/std/debug.zig

+45-8
Original file line numberDiff line numberDiff line change
@@ -206,17 +206,12 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT
206206
if (native_os == .windows) {
207207
const addrs = stack_trace.instruction_addresses;
208208
const first_addr = first_address orelse {
209-
stack_trace.index = windows.ntdll.RtlCaptureStackBackTrace(
210-
0,
211-
@intCast(u32, addrs.len),
212-
@ptrCast(**anyopaque, addrs.ptr),
213-
null,
214-
);
209+
stack_trace.index = walkStackWindows(addrs[0..]);
215210
return;
216211
};
217212
var addr_buf_stack: [32]usize = undefined;
218213
const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs;
219-
const n = windows.ntdll.RtlCaptureStackBackTrace(0, @intCast(u32, addr_buf.len), @ptrCast(**anyopaque, addr_buf.ptr), null);
214+
const n = walkStackWindows(addr_buf[0..]);
220215
const first_index = for (addr_buf[0..n]) |addr, i| {
221216
if (addr == first_addr) {
222217
break i;
@@ -573,14 +568,56 @@ pub fn writeCurrentStackTrace(
573568
}
574569
}
575570

571+
pub noinline fn walkStackWindows(addresses: []usize) usize {
572+
if (builtin.cpu.arch == .x86) {
573+
// RtlVirtualUnwind doesn't exist on x86
574+
return windows.ntdll.RtlCaptureStackBackTrace(0, addresses.len, @ptrCast(**anyopaque, addresses.ptr), null);
575+
}
576+
577+
const tib = @ptrCast(*const windows.NT_TIB, &windows.teb().Reserved1);
578+
579+
var context: windows.CONTEXT = std.mem.zeroes(windows.CONTEXT);
580+
windows.ntdll.RtlCaptureContext(&context);
581+
582+
var i: usize = 0;
583+
var image_base: usize = undefined;
584+
var history_table: windows.UNWIND_HISTORY_TABLE = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE);
585+
586+
while (i < addresses.len) : (i += 1) {
587+
const current_regs = context.getRegs();
588+
if (windows.ntdll.RtlLookupFunctionEntry(current_regs.ip, &image_base, &history_table)) |runtime_function| {
589+
var handler_data: ?*anyopaque = null;
590+
var establisher_frame: u64 = undefined;
591+
_ = windows.ntdll.RtlVirtualUnwind(windows.UNW_FLAG_NHANDLER, image_base, current_regs.ip, runtime_function, &context, &handler_data, &establisher_frame, null);
592+
} else {
593+
// leaf function
594+
context.setIp(@intToPtr(*u64, current_regs.sp).*);
595+
context.setSp(current_regs.sp + @sizeOf(usize));
596+
}
597+
598+
const next_regs = context.getRegs();
599+
if (next_regs.sp < @ptrToInt(tib.StackLimit) or next_regs.sp > @ptrToInt(tib.StackBase)) {
600+
break;
601+
}
602+
603+
if (next_regs.ip == 0) {
604+
break;
605+
}
606+
607+
addresses[i] = next_regs.ip;
608+
}
609+
610+
return i;
611+
}
612+
576613
pub fn writeCurrentStackTraceWindows(
577614
out_stream: anytype,
578615
debug_info: *DebugInfo,
579616
tty_config: TTY.Config,
580617
start_addr: ?usize,
581618
) !void {
582619
var addr_buf: [1024]usize = undefined;
583-
const n = windows.ntdll.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**anyopaque, &addr_buf), null);
620+
const n = walkStackWindows(addr_buf[0..]);
584621
const addrs = addr_buf[0..n];
585622
var start_i: usize = if (start_addr) |saddr| blk: {
586623
for (addrs) |addr, i| {

lib/std/os/windows.zig

+119-5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub const winmm = @import("windows/winmm.zig");
3131

3232
pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
3333

34+
const Self = @This();
35+
3436
pub const OpenError = error{
3537
IsDir,
3638
NotDir,
@@ -3282,7 +3284,7 @@ pub usingnamespace switch (native_arch) {
32823284
};
32833285

32843286
pub const CONTEXT = extern struct {
3285-
P1Home: DWORD64,
3287+
P1Home: DWORD64 align(16),
32863288
P2Home: DWORD64,
32873289
P3Home: DWORD64,
32883290
P4Home: DWORD64,
@@ -3352,9 +3354,28 @@ pub usingnamespace switch (native_arch) {
33523354
LastExceptionToRip: DWORD64,
33533355
LastExceptionFromRip: DWORD64,
33543356

3355-
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {
3356-
return .{ .bp = ctx.Rbp, .ip = ctx.Rip };
3357+
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
3358+
return .{ .bp = ctx.Rbp, .ip = ctx.Rip, .sp = ctx.Rsp };
3359+
}
3360+
3361+
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
3362+
ctx.Rip = ip;
33573363
}
3364+
3365+
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
3366+
ctx.Rsp = sp;
3367+
}
3368+
};
3369+
3370+
pub const RUNTIME_FUNCTION = extern struct {
3371+
BeginAddress: DWORD,
3372+
EndAddress: DWORD,
3373+
UnwindData: DWORD,
3374+
};
3375+
3376+
pub const KNONVOLATILE_CONTEXT_POINTERS = extern struct {
3377+
FloatingContext: [16]?*M128A,
3378+
IntegerContext: [16]?*ULONG64,
33583379
};
33593380
},
33603381
.aarch64 => struct {
@@ -3370,7 +3391,7 @@ pub usingnamespace switch (native_arch) {
33703391
};
33713392

33723393
pub const CONTEXT = extern struct {
3373-
ContextFlags: ULONG,
3394+
ContextFlags: ULONG align(16),
33743395
Cpsr: ULONG,
33753396
DUMMYUNIONNAME: extern union {
33763397
DUMMYSTRUCTNAME: extern struct {
@@ -3418,12 +3439,60 @@ pub usingnamespace switch (native_arch) {
34183439
Wcr: [2]DWORD,
34193440
Wvr: [2]DWORD64,
34203441

3421-
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {
3442+
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
34223443
return .{
34233444
.bp = ctx.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp,
34243445
.ip = ctx.Pc,
3446+
.sp = ctx.Sp,
34253447
};
34263448
}
3449+
3450+
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
3451+
ctx.Pc = ip;
3452+
}
3453+
3454+
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
3455+
ctx.Sp = sp;
3456+
}
3457+
};
3458+
3459+
pub const RUNTIME_FUNCTION = extern struct {
3460+
BeginAddress: DWORD,
3461+
DUMMYUNIONNAME: extern union {
3462+
UnwindData: DWORD,
3463+
DUMMYSTRUCTNAME: packed struct {
3464+
Flag: u2,
3465+
FunctionLength: u11,
3466+
RegF: u3,
3467+
RegI: u4,
3468+
H: u1,
3469+
CR: u2,
3470+
FrameSize: u9,
3471+
},
3472+
},
3473+
};
3474+
3475+
pub const KNONVOLATILE_CONTEXT_POINTERS = extern struct {
3476+
X19: ?*DWORD64,
3477+
X20: ?*DWORD64,
3478+
X21: ?*DWORD64,
3479+
X22: ?*DWORD64,
3480+
X23: ?*DWORD64,
3481+
X24: ?*DWORD64,
3482+
X25: ?*DWORD64,
3483+
X26: ?*DWORD64,
3484+
X27: ?*DWORD64,
3485+
X28: ?*DWORD64,
3486+
Fp: ?*DWORD64,
3487+
Lr: ?*DWORD64,
3488+
D8: ?*DWORD64,
3489+
D9: ?*DWORD64,
3490+
D10: ?*DWORD64,
3491+
D11: ?*DWORD64,
3492+
D12: ?*DWORD64,
3493+
D13: ?*DWORD64,
3494+
D14: ?*DWORD64,
3495+
D15: ?*DWORD64,
34273496
};
34283497
},
34293498
else => struct {},
@@ -3436,6 +3505,36 @@ pub const EXCEPTION_POINTERS = extern struct {
34363505

34373506
pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long;
34383507

3508+
pub const EXCEPTION_DISPOSITION = i32;
3509+
pub const EXCEPTION_ROUTINE = *const fn (
3510+
ExceptionRecord: ?*EXCEPTION_RECORD,
3511+
EstablisherFrame: PVOID,
3512+
ContextRecord: *(Self.CONTEXT),
3513+
DispatcherContext: PVOID,
3514+
) callconv(WINAPI) EXCEPTION_DISPOSITION;
3515+
3516+
pub const UNWIND_HISTORY_TABLE_SIZE = 12;
3517+
pub const UNWIND_HISTORY_TABLE_ENTRY = extern struct {
3518+
ImageBase: ULONG64,
3519+
FunctionEntry: *Self.RUNTIME_FUNCTION,
3520+
};
3521+
3522+
pub const UNWIND_HISTORY_TABLE = extern struct {
3523+
Count: ULONG,
3524+
LocalHint: BYTE,
3525+
GlobalHint: BYTE,
3526+
Search: BYTE,
3527+
Once: BYTE,
3528+
LowAddress: ULONG64,
3529+
HighAddress: ULONG64,
3530+
Entry: [UNWIND_HISTORY_TABLE_SIZE]UNWIND_HISTORY_TABLE_ENTRY,
3531+
};
3532+
3533+
pub const UNW_FLAG_NHANDLER = 0x0;
3534+
pub const UNW_FLAG_EHANDLER = 0x1;
3535+
pub const UNW_FLAG_UHANDLER = 0x2;
3536+
pub const UNW_FLAG_CHAININFO = 0x4;
3537+
34393538
pub const OBJECT_ATTRIBUTES = extern struct {
34403539
Length: ULONG,
34413540
RootDirectory: ?HANDLE,
@@ -3494,6 +3593,21 @@ pub const TEB = extern struct {
34943593
TlsExpansionSlots: PVOID,
34953594
};
34963595

3596+
pub const EXCEPTION_REGISTRATION_RECORD = extern struct {
3597+
Next: ?*EXCEPTION_REGISTRATION_RECORD,
3598+
Handler: ?*EXCEPTION_DISPOSITION,
3599+
};
3600+
3601+
pub const NT_TIB = extern struct {
3602+
ExceptionList: ?*EXCEPTION_REGISTRATION_RECORD,
3603+
StackBase: PVOID,
3604+
StackLimit: PVOID,
3605+
SubSystemTib: PVOID,
3606+
DUMMYUNIONNAME: extern union { FiberData: PVOID, Version: DWORD },
3607+
ArbitraryUserPointer: PVOID,
3608+
Self: ?*@This(),
3609+
};
3610+
34973611
/// Process Environment Block
34983612
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
34993613
/// - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269

lib/std/os/windows/kernel32.zig

+25
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ const BOOL = windows.BOOL;
55
const BOOLEAN = windows.BOOLEAN;
66
const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
77
const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO;
8+
const CONTEXT = windows.CONTEXT;
89
const COORD = windows.COORD;
910
const DWORD = windows.DWORD;
11+
const DWORD64 = windows.DWORD64;
1012
const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS;
1113
const HANDLE = windows.HANDLE;
1214
const HMODULE = windows.HMODULE;
@@ -60,6 +62,10 @@ const INIT_ONCE_FN = windows.INIT_ONCE_FN;
6062
const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION;
6163
const REGSAM = windows.REGSAM;
6264
const LSTATUS = windows.LSTATUS;
65+
const UNWIND_HISTORY_TABLE = windows.UNWIND_HISTORY_TABLE;
66+
const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;
67+
const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
68+
const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;
6369

6470
pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
6571
pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
@@ -292,6 +298,25 @@ pub extern "kernel32" fn ReadFile(
292298

293299
pub extern "kernel32" fn RemoveDirectoryW(lpPathName: [*:0]const u16) callconv(WINAPI) BOOL;
294300

301+
pub extern "kernel32" fn RtlCaptureContext(ContextRecord: *CONTEXT) callconv(WINAPI) void;
302+
303+
pub extern "kernel32" fn RtlLookupFunctionEntry(
304+
ControlPc: DWORD64,
305+
ImageBase: *DWORD64,
306+
HistoryTable: *UNWIND_HISTORY_TABLE,
307+
) callconv(WINAPI) ?*RUNTIME_FUNCTION;
308+
309+
pub extern "kernel32" fn RtlVirtualUnwind(
310+
HandlerType: DWORD,
311+
ImageBase: DWORD64,
312+
ControlPc: DWORD64,
313+
FunctionEntry: *RUNTIME_FUNCTION,
314+
ContextRecord: *CONTEXT,
315+
HandlerData: *?PVOID,
316+
EstablisherFrame: *DWORD64,
317+
ContextPointers: ?*KNONVOLATILE_CONTEXT_POINTERS,
318+
) callconv(WINAPI) *EXCEPTION_ROUTINE;
319+
295320
pub extern "kernel32" fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) callconv(WINAPI) BOOL;
296321

297322
pub extern "kernel32" fn SetConsoleCtrlHandler(

lib/std/os/windows/ntdll.zig

+22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const windows = std.os.windows;
33

44
const BOOL = windows.BOOL;
55
const DWORD = windows.DWORD;
6+
const DWORD64 = windows.DWORD64;
67
const ULONG = windows.ULONG;
78
const WINAPI = windows.WINAPI;
89
const NTSTATUS = windows.NTSTATUS;
@@ -24,6 +25,11 @@ const SIZE_T = windows.SIZE_T;
2425
const CURDIR = windows.CURDIR;
2526
const PCWSTR = windows.PCWSTR;
2627
const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE;
28+
const CONTEXT = windows.CONTEXT;
29+
const UNWIND_HISTORY_TABLE = windows.UNWIND_HISTORY_TABLE;
30+
const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;
31+
const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
32+
const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;
2733

2834
pub const THREADINFOCLASS = enum(c_int) {
2935
ThreadBasicInformation,
@@ -99,6 +105,22 @@ pub extern "ntdll" fn RtlCaptureStackBackTrace(
99105
BackTrace: **anyopaque,
100106
BackTraceHash: ?*DWORD,
101107
) callconv(WINAPI) WORD;
108+
pub extern "ntdll" fn RtlCaptureContext(ContextRecord: *CONTEXT) callconv(WINAPI) void;
109+
pub extern "ntdll" fn RtlLookupFunctionEntry(
110+
ControlPc: DWORD64,
111+
ImageBase: *DWORD64,
112+
HistoryTable: *UNWIND_HISTORY_TABLE,
113+
) callconv(WINAPI) ?*RUNTIME_FUNCTION;
114+
pub extern "ntdll" fn RtlVirtualUnwind(
115+
HandlerType: DWORD,
116+
ImageBase: DWORD64,
117+
ControlPc: DWORD64,
118+
FunctionEntry: *RUNTIME_FUNCTION,
119+
ContextRecord: *CONTEXT,
120+
HandlerData: *?PVOID,
121+
EstablisherFrame: *DWORD64,
122+
ContextPointers: ?*KNONVOLATILE_CONTEXT_POINTERS,
123+
) callconv(WINAPI) *EXCEPTION_ROUTINE;
102124
pub extern "ntdll" fn NtQueryInformationFile(
103125
FileHandle: HANDLE,
104126
IoStatusBlock: *IO_STATUS_BLOCK,

0 commit comments

Comments
 (0)