authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-06-27 23:53:05-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
logcaa334712fc8f540349e54fa3008aa3fdef64e13
tree5cc39b79cc97a305cfbc981d9e426cf1896947d4
parent89ef004646896a145ec0607678882a395fabda3d

linux: rework getcontext to closer match the specification (saved IP/SP match the state after it would return)

debug: fixup ucontext_t check

3 files changed, 118 insertions(+), 64 deletions(-)

lib/std/debug.zig+1-1
...@@ -136,7 +136,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -136,7 +136,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
136pub const StackTraceContext = blk: {136pub const StackTraceContext = blk: {
137 if (native_os == .windows) {137 if (native_os == .windows) {
138 break :blk std.os.windows.CONTEXT;138 break :blk std.os.windows.CONTEXT;
139 } else if (@hasDecl(os.system, "ucontext_t")) {139 } else if (StackIterator.supports_context) {
140 break :blk os.ucontext_t;140 break :blk os.ucontext_t;
141 } else {141 } else {
142 break :blk void;142 break :blk void;
lib/std/os/linux/x86.zig+57-28
...@@ -394,28 +394,51 @@ fn gpRegisterOffset(comptime reg_index: comptime_int) usize {...@@ -394,28 +394,51 @@ fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
394 return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;394 return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;
395}395}
396396
397pub inline fn getcontext(context: *ucontext_t) usize {397noinline fn getContextReturnAddress() usize {
398 return @returnAddress();
399}
400
401pub fn getContextInternal() callconv(.Naked) void {
398 asm volatile (402 asm volatile (
399 \\ movl %%edi, (%[edi_offset])(%[context])403 \\ movl $0, (%[flags_offset])(%%edx)
400 \\ movl %%esi, (%[esi_offset])(%[context])404 \\ movl $0, (%[link_offset])(%%edx)
401 \\ movl %%ebp, (%[ebp_offset])(%[context])405 \\ movl %%edi, (%[edi_offset])(%%edx)
402 \\ movl %%esp, (%[esp_offset])(%[context])406 \\ movl %%esi, (%[esi_offset])(%%edx)
403 \\ movl %%ebx, (%[ebx_offset])(%[context])407 \\ movl %%ebp, (%[ebp_offset])(%%edx)
404 \\ movl %%edx, (%[edx_offset])(%[context])408 \\ movl %%ebx, (%[ebx_offset])(%%edx)
405 \\ movl %%ecx, (%[ecx_offset])(%[context])409 \\ movl %%edx, (%[edx_offset])(%%edx)
406 \\ movl %%eax, (%[eax_offset])(%[context])410 \\ movl %%ecx, (%[ecx_offset])(%%edx)
411 \\ movl %%eax, (%[eax_offset])(%%edx)
412 \\ movl (%%esp), %%ecx
413 \\ movl %%ecx, (%[eip_offset])(%%edx)
414 \\ leal 4(%%esp), %%ecx
415 \\ movl %%ecx, (%[esp_offset])(%%edx)
407 \\ xorl %%ecx, %%ecx416 \\ xorl %%ecx, %%ecx
408 \\ movw %%fs, %%cx417 \\ movw %%fs, %%cx
409 \\ movl %%ecx, (%[fs_offset])(%[context])418 \\ movl %%ecx, (%[fs_offset])(%%edx)
410 \\ leal (%[regspace_offset])(%[context]), %%ecx419 \\ leal (%[regspace_offset])(%%edx), %%ecx
411 \\ movl %%ecx, (%[fpregs_offset])(%[context])420 \\ movl %%ecx, (%[fpregs_offset])(%%edx)
412 \\ fnstenv (%%ecx)421 \\ fnstenv (%%ecx)
413 \\ fldenv (%%ecx)422 \\ fldenv (%%ecx)
414 \\ call getcontext_read_eip423 \\ pushl %%ebx
415 \\ getcontext_read_eip: pop %%ecx424 \\ pushl %%esi
416 \\ movl %%ecx, (%[eip_offset])(%[context])425 \\ xorl %%ebx, %%ebx
426 \\ movl %[sigaltstack], %%eax
427 \\ leal (%[stack_offset])(%%edx), %%ecx
428 \\ int $0x80
429 \\ cmpl $0, %%eax
430 \\ jne return
431 \\ movl %[sigprocmask], %%eax
432 \\ xorl %%ecx, %%ecx
433 \\ leal (%[sigmask_offset])(%%edx), %%edx
434 \\ movl %[sigset_size], %%esi
435 \\ int $0x80
436 \\ return:
437 \\ popl %%esi
438 \\ popl %%ebx
417 :439 :
418 : [context] "{edi}" (context),440 : [flags_offset] "p" (@offsetOf(ucontext_t, "flags")),
441 [link_offset] "p" (@offsetOf(ucontext_t, "link")),
419 [edi_offset] "p" (comptime gpRegisterOffset(REG.EDI)),442 [edi_offset] "p" (comptime gpRegisterOffset(REG.EDI)),
420 [esi_offset] "p" (comptime gpRegisterOffset(REG.ESI)),443 [esi_offset] "p" (comptime gpRegisterOffset(REG.ESI)),
421 [ebp_offset] "p" (comptime gpRegisterOffset(REG.EBP)),444 [ebp_offset] "p" (comptime gpRegisterOffset(REG.EBP)),
...@@ -428,18 +451,24 @@ pub inline fn getcontext(context: *ucontext_t) usize {...@@ -428,18 +451,24 @@ pub inline fn getcontext(context: *ucontext_t) usize {
428 [fs_offset] "p" (comptime gpRegisterOffset(REG.FS)),451 [fs_offset] "p" (comptime gpRegisterOffset(REG.FS)),
429 [fpregs_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),452 [fpregs_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
430 [regspace_offset] "p" (@offsetOf(ucontext_t, "regspace")),453 [regspace_offset] "p" (@offsetOf(ucontext_t, "regspace")),
431 : "memory", "ecx"454 [sigaltstack] "i" (@intFromEnum(linux.SYS.sigaltstack)),
455 [stack_offset] "p" (@offsetOf(ucontext_t, "stack")),
456 [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
457 [sigmask_offset] "p" (@offsetOf(ucontext_t, "sigmask")),
458 [sigset_size] "i" (linux.NSIG / 8),
459 : "memory", "eax", "ecx", "edx"
432 );460 );
461}
433462
434 // TODO: Read CS/SS registers?463pub inline fn getcontext(context: *ucontext_t) usize {
435 // TODO: Store mxcsr state, need an actual definition of fpstate for that464 // This method is used so that getContextInternal can control
436465 // its prologue in order to read ESP from a constant offset.
437 // TODO: `flags` isn't present in the getcontext man page, figure out what to write here466 // The unused &getContextInternal input is required so the function is included in the binary.
438 context.flags = 0;467 return asm volatile (
439 context.link = null;468 \\ call os.linux.x86.getContextInternal
440469 : [ret] "={eax}" (-> usize),
441 const altstack_result = linux.sigaltstack(null, &context.stack);470 : [context] "{edx}" (context),
442 if (altstack_result != 0) return altstack_result;471 [getContextInternal] "X" (&getContextInternal),
443472 : "memory", "ecx"
444 return linux.sigprocmask(0, null, &context.sigmask);473 );
445}474}
lib/std/os/linux/x86_64.zig+60-35
...@@ -400,35 +400,53 @@ fn gpRegisterOffset(comptime reg_index: comptime_int) usize {...@@ -400,35 +400,53 @@ fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
400 return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;400 return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;
401}401}
402402
403pub inline fn getcontext(context: *ucontext_t) usize {403fn getContextInternal() callconv(.Naked) void {
404 // TODO: Read GS/FS registers?
404 asm volatile (405 asm volatile (
405 \\ movq %%r8, (%[r8_offset])(%[context])406 \\ movq $0, (%[flags_offset])(%%rdi)
406 \\ movq %%r9, (%[r9_offset])(%[context])407 \\ movq $0, (%[link_offset])(%%rdi)
407 \\ movq %%r10, (%[r10_offset])(%[context])408 \\ movq %%r8, (%[r8_offset])(%%rdi)
408 \\ movq %%r11, (%[r11_offset])(%[context])409 \\ movq %%r9, (%[r9_offset])(%%rdi)
409 \\ movq %%r12, (%[r12_offset])(%[context])410 \\ movq %%r10, (%[r10_offset])(%%rdi)
410 \\ movq %%r13, (%[r13_offset])(%[context])411 \\ movq %%r11, (%[r11_offset])(%%rdi)
411 \\ movq %%r14, (%[r14_offset])(%[context])412 \\ movq %%r12, (%[r12_offset])(%%rdi)
412 \\ movq %%r15, (%[r15_offset])(%[context])413 \\ movq %%r13, (%[r13_offset])(%%rdi)
413 \\ movq %%rdi, (%[rdi_offset])(%[context])414 \\ movq %%r14, (%[r14_offset])(%%rdi)
414 \\ movq %%rsi, (%[rsi_offset])(%[context])415 \\ movq %%r15, (%[r15_offset])(%%rdi)
415 \\ movq %%rbp, (%[rbp_offset])(%[context])416 \\ movq %%rdi, (%[rdi_offset])(%%rdi)
416 \\ movq %%rbx, (%[rbx_offset])(%[context])417 \\ movq %%rsi, (%[rsi_offset])(%%rdi)
417 \\ movq %%rdx, (%[rdx_offset])(%[context])418 \\ movq %%rbp, (%[rbp_offset])(%%rdi)
418 \\ movq %%rax, (%[rax_offset])(%[context])419 \\ movq %%rbx, (%[rbx_offset])(%%rdi)
419 \\ movq %%rcx, (%[rcx_offset])(%[context])420 \\ movq %%rdx, (%[rdx_offset])(%%rdi)
420 \\ movq %%rsp, (%[rsp_offset])(%[context])421 \\ movq %%rax, (%[rax_offset])(%%rdi)
421 \\ leaq (%%rip), %%rcx422 \\ movq %%rcx, (%[rcx_offset])(%%rdi)
422 \\ movq %%rcx, (%[rip_offset])(%[context])423 \\ movq (%%rsp), %%rcx
424 \\ movq %%rcx, (%[rip_offset])(%%rdi)
425 \\ leaq 8(%%rsp), %%rcx
426 \\ movq %%rcx, (%[rsp_offset])(%%rdi)
423 \\ pushfq427 \\ pushfq
424 \\ popq (%[efl_offset])(%[context])428 \\ popq (%[efl_offset])(%%rdi)
425 \\ leaq (%[fpmem_offset])(%[context]), %%rcx429 \\ leaq (%[fpmem_offset])(%%rdi), %%rcx
426 \\ movq %%rcx, (%[fpstate_offset])(%[context])430 \\ movq %%rcx, (%[fpstate_offset])(%%rdi)
427 \\ fnstenv (%%rcx)431 \\ fnstenv (%%rcx)
428 \\ fldenv (%%rcx)432 \\ fldenv (%%rcx)
429 \\ stmxcsr (%[mxcsr_offset])(%[context])433 \\ stmxcsr (%[mxcsr_offset])(%%rdi)
434 \\ leaq (%[stack_offset])(%%rdi), %%rsi
435 \\ movq %%rdi, %%r8
436 \\ xorq %%rdi, %%rdi
437 \\ movq %[sigaltstack], %%rax
438 \\ syscall
439 \\ cmpq $0, %%rax
440 \\ jne return
441 \\ movq %[sigprocmask], %%rax
442 \\ xorq %%rsi, %%rsi
443 \\ leaq (%[sigmask_offset])(%%r8), %%rdx
444 \\ movq %[sigset_size], %%r10
445 \\ syscall
446 \\ return:
430 :447 :
431 : [context] "{rdi}" (context),448 : [flags_offset] "p" (@offsetOf(ucontext_t, "flags")),
449 [link_offset] "p" (@offsetOf(ucontext_t, "link")),
432 [r8_offset] "p" (comptime gpRegisterOffset(REG.R8)),450 [r8_offset] "p" (comptime gpRegisterOffset(REG.R8)),
433 [r9_offset] "p" (comptime gpRegisterOffset(REG.R9)),451 [r9_offset] "p" (comptime gpRegisterOffset(REG.R9)),
434 [r10_offset] "p" (comptime gpRegisterOffset(REG.R10)),452 [r10_offset] "p" (comptime gpRegisterOffset(REG.R10)),
...@@ -450,17 +468,24 @@ pub inline fn getcontext(context: *ucontext_t) usize {...@@ -450,17 +468,24 @@ pub inline fn getcontext(context: *ucontext_t) usize {
450 [fpstate_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),468 [fpstate_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
451 [fpmem_offset] "p" (@offsetOf(ucontext_t, "fpregs_mem")),469 [fpmem_offset] "p" (@offsetOf(ucontext_t, "fpregs_mem")),
452 [mxcsr_offset] "p" (@offsetOf(ucontext_t, "fpregs_mem") + @offsetOf(fpstate, "mxcsr")),470 [mxcsr_offset] "p" (@offsetOf(ucontext_t, "fpregs_mem") + @offsetOf(fpstate, "mxcsr")),
453 : "memory", "rcx"471 [sigaltstack] "i" (@intFromEnum(linux.SYS.sigaltstack)),
472 [stack_offset] "p" (@offsetOf(ucontext_t, "stack")),
473 [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
474 [sigmask_offset] "p" (@offsetOf(ucontext_t, "sigmask")),
475 [sigset_size] "i" (linux.NSIG / 8),
476 : "memory", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
454 );477 );
478}
455479
456 // TODO: Read GS/FS registers?480pub inline fn getcontext(context: *ucontext_t) usize {
457481 // This method is used so that getContextInternal can control
458 // TODO: `flags` isn't present in the getcontext man page, figure out what to write here482 // its prologue in order to read RSP from a constant offset
459 context.flags = 0;483 // The unused &getContextInternal input is required so the function is included in the binary.
460 context.link = null;484 return asm volatile (
461485 \\ call os.linux.x86_64.getContextInternal
462 const altstack_result = linux.sigaltstack(null, &context.stack);486 : [ret] "={rax}" (-> usize),
463 if (altstack_result != 0) return altstack_result;487 : [context] "{rdi}" (context),
464488 [getContextInternal] "X" (&getContextInternal),
465 return linux.sigprocmask(0, null, &context.sigmask);489 : "memory", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
490 );
466}491}