| author | |
| committer | |
| log | 09ff03a57a9f2144f0d3e9b0173bac7035d3629b |
| tree | c498aa8f839458e5558f8344893579d16a5954ae |
| parent | a3e2ee091139740d076da1176ac8c487ca20a9a6 |
| signature |
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 <kubkon@jakubkonka.com>4 files changed, 211 insertions(+), 13 deletions(-)
lib/std/debug.zig+45-8| ... | ... | @@ -206,17 +206,12 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT |
| 206 | 206 | if (native_os == .windows) { |
| 207 | 207 | const addrs = stack_trace.instruction_addresses; |
| 208 | 208 | 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..]); | |
| 215 | 210 | return; |
| 216 | 211 | }; |
| 217 | 212 | var addr_buf_stack: [32]usize = undefined; |
| 218 | 213 | 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..]); | |
| 220 | 215 | const first_index = for (addr_buf[0..n]) |addr, i| { |
| 221 | 216 | if (addr == first_addr) { |
| 222 | 217 | break i; |
| ... | ... | @@ -573,6 +568,48 @@ pub fn writeCurrentStackTrace( |
| 573 | 568 | } |
| 574 | 569 | } |
| 575 | 570 | |
| 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 | ||
| 576 | 613 | pub fn writeCurrentStackTraceWindows( |
| 577 | 614 | out_stream: anytype, |
| 578 | 615 | debug_info: *DebugInfo, |
| ... | ... | @@ -580,7 +617,7 @@ pub fn writeCurrentStackTraceWindows( |
| 580 | 617 | start_addr: ?usize, |
| 581 | 618 | ) !void { |
| 582 | 619 | 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..]); | |
| 584 | 621 | const addrs = addr_buf[0..n]; |
| 585 | 622 | var start_i: usize = if (start_addr) |saddr| blk: { |
| 586 | 623 | for (addrs) |addr, i| { |
lib/std/os/windows.zig+119-5| ... | ... | @@ -31,6 +31,8 @@ pub const winmm = @import("windows/winmm.zig"); |
| 31 | 31 | |
| 32 | 32 | pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); |
| 33 | 33 | |
| 34 | const Self = @This(); | |
| 35 | ||
| 34 | 36 | pub const OpenError = error{ |
| 35 | 37 | IsDir, |
| 36 | 38 | NotDir, |
| ... | ... | @@ -3282,7 +3284,7 @@ pub usingnamespace switch (native_arch) { |
| 3282 | 3284 | }; |
| 3283 | 3285 | |
| 3284 | 3286 | pub const CONTEXT = extern struct { |
| 3285 | P1Home: DWORD64, | |
| 3287 | P1Home: DWORD64 align(16), | |
| 3286 | 3288 | P2Home: DWORD64, |
| 3287 | 3289 | P3Home: DWORD64, |
| 3288 | 3290 | P4Home: DWORD64, |
| ... | ... | @@ -3352,9 +3354,28 @@ pub usingnamespace switch (native_arch) { |
| 3352 | 3354 | LastExceptionToRip: DWORD64, |
| 3353 | 3355 | LastExceptionFromRip: DWORD64, |
| 3354 | 3356 | |
| 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; | |
| 3357 | 3363 | } |
| 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, | |
| 3358 | 3379 | }; |
| 3359 | 3380 | }, |
| 3360 | 3381 | .aarch64 => struct { |
| ... | ... | @@ -3370,7 +3391,7 @@ pub usingnamespace switch (native_arch) { |
| 3370 | 3391 | }; |
| 3371 | 3392 | |
| 3372 | 3393 | pub const CONTEXT = extern struct { |
| 3373 | ContextFlags: ULONG, | |
| 3394 | ContextFlags: ULONG align(16), | |
| 3374 | 3395 | Cpsr: ULONG, |
| 3375 | 3396 | DUMMYUNIONNAME: extern union { |
| 3376 | 3397 | DUMMYSTRUCTNAME: extern struct { |
| ... | ... | @@ -3418,12 +3439,60 @@ pub usingnamespace switch (native_arch) { |
| 3418 | 3439 | Wcr: [2]DWORD, |
| 3419 | 3440 | Wvr: [2]DWORD64, |
| 3420 | 3441 | |
| 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 } { | |
| 3422 | 3443 | return .{ |
| 3423 | 3444 | .bp = ctx.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp, |
| 3424 | 3445 | .ip = ctx.Pc, |
| 3446 | .sp = ctx.Sp, | |
| 3425 | 3447 | }; |
| 3426 | 3448 | } |
| 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, | |
| 3427 | 3496 | }; |
| 3428 | 3497 | }, |
| 3429 | 3498 | else => struct {}, |
| ... | ... | @@ -3436,6 +3505,36 @@ pub const EXCEPTION_POINTERS = extern struct { |
| 3436 | 3505 | |
| 3437 | 3506 | pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long; |
| 3438 | 3507 | |
| 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 | ||
| 3439 | 3538 | pub const OBJECT_ATTRIBUTES = extern struct { |
| 3440 | 3539 | Length: ULONG, |
| 3441 | 3540 | RootDirectory: ?HANDLE, |
| ... | ... | @@ -3494,6 +3593,21 @@ pub const TEB = extern struct { |
| 3494 | 3593 | TlsExpansionSlots: PVOID, |
| 3495 | 3594 | }; |
| 3496 | 3595 | |
| 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 | ||
| 3497 | 3611 | /// Process Environment Block |
| 3498 | 3612 | /// Microsoft documentation of this is incomplete, the fields here are taken from various resources including: |
| 3499 | 3613 | /// - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269 |
lib/std/os/windows/kernel32.zig+25| ... | ... | @@ -5,8 +5,10 @@ const BOOL = windows.BOOL; |
| 5 | 5 | const BOOLEAN = windows.BOOLEAN; |
| 6 | 6 | const CONDITION_VARIABLE = windows.CONDITION_VARIABLE; |
| 7 | 7 | const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO; |
| 8 | const CONTEXT = windows.CONTEXT; | |
| 8 | 9 | const COORD = windows.COORD; |
| 9 | 10 | const DWORD = windows.DWORD; |
| 11 | const DWORD64 = windows.DWORD64; | |
| 10 | 12 | const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS; |
| 11 | 13 | const HANDLE = windows.HANDLE; |
| 12 | 14 | const HMODULE = windows.HMODULE; |
| ... | ... | @@ -60,6 +62,10 @@ const INIT_ONCE_FN = windows.INIT_ONCE_FN; |
| 60 | 62 | const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION; |
| 61 | 63 | const REGSAM = windows.REGSAM; |
| 62 | 64 | 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; | |
| 63 | 69 | |
| 64 | 70 | pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; |
| 65 | 71 | pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; |
| ... | ... | @@ -292,6 +298,25 @@ pub extern "kernel32" fn ReadFile( |
| 292 | 298 | |
| 293 | 299 | pub extern "kernel32" fn RemoveDirectoryW(lpPathName: [*:0]const u16) callconv(WINAPI) BOOL; |
| 294 | 300 | |
| 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 | ||
| 295 | 320 | pub extern "kernel32" fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) callconv(WINAPI) BOOL; |
| 296 | 321 | |
| 297 | 322 | pub extern "kernel32" fn SetConsoleCtrlHandler( |
lib/std/os/windows/ntdll.zig+22| ... | ... | @@ -3,6 +3,7 @@ const windows = std.os.windows; |
| 3 | 3 | |
| 4 | 4 | const BOOL = windows.BOOL; |
| 5 | 5 | const DWORD = windows.DWORD; |
| 6 | const DWORD64 = windows.DWORD64; | |
| 6 | 7 | const ULONG = windows.ULONG; |
| 7 | 8 | const WINAPI = windows.WINAPI; |
| 8 | 9 | const NTSTATUS = windows.NTSTATUS; |
| ... | ... | @@ -24,6 +25,11 @@ const SIZE_T = windows.SIZE_T; |
| 24 | 25 | const CURDIR = windows.CURDIR; |
| 25 | 26 | const PCWSTR = windows.PCWSTR; |
| 26 | 27 | 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; | |
| 27 | 33 | |
| 28 | 34 | pub const THREADINFOCLASS = enum(c_int) { |
| 29 | 35 | ThreadBasicInformation, |
| ... | ... | @@ -99,6 +105,22 @@ pub extern "ntdll" fn RtlCaptureStackBackTrace( |
| 99 | 105 | BackTrace: **anyopaque, |
| 100 | 106 | BackTraceHash: ?*DWORD, |
| 101 | 107 | ) 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; | |
| 102 | 124 | pub extern "ntdll" fn NtQueryInformationFile( |
| 103 | 125 | FileHandle: HANDLE, |
| 104 | 126 | IoStatusBlock: *IO_STATUS_BLOCK, |