diff --git a/crates/linux-user/src/aarch64.rs b/crates/linux-user/src/aarch64.rs index 2aacdfd..4b73b73 100644 --- a/crates/linux-user/src/aarch64.rs +++ b/crates/linux-user/src/aarch64.rs @@ -1007,6 +1007,7 @@ pub(crate) static GUEST: Guest = Guest { }, set_tls, cpu_loop, + setup_frame: None, setup_rt_frame, rt_sigreturn: do_rt_sigreturn, }; diff --git a/crates/linux-user/src/arm.rs b/crates/linux-user/src/arm.rs index e0992c3..77a79e7 100644 --- a/crates/linux-user/src/arm.rs +++ b/crates/linux-user/src/arm.rs @@ -460,6 +460,7 @@ pub(crate) static GUEST: Guest = Guest { set_tls, cpu_loop, setup_rt_frame, + setup_frame: None, rt_sigreturn: do_rt_sigreturn, }; diff --git a/crates/linux-user/src/compat.rs b/crates/linux-user/src/compat.rs index 8b93f3a..843bdae 100644 --- a/crates/linux-user/src/compat.rs +++ b/crates/linux-user/src/compat.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use ruvm_jit::Cpu; use ruvm_user_common::page; -use crate::signal::{self, Task, get32, get64, guest_sys, put32, put64}; +use crate::signal::{self, Sigaction, Task, get32, get64, guest_sys, put32, put64}; use crate::syscall::{self, A, Proc}; use crate::syscall::{M, MN, P, R, S, V}; @@ -213,6 +213,67 @@ fn wait4(p: &Proc, pid: u64, status: u64, options: u64, ru: u64) -> Res { Ok(r) } +/// `waitid()`, with the 32-bit `siginfo_t` and `struct rusage`. +fn waitid(p: &Proc, a: [u64; 6]) -> Res { + let mut info = [0u8; 128]; + let mut hru = [0u8; 144]; + let hr = if a[4] != 0 { hru.as_mut_ptr() as u64 } else { 0 }; + let r = host(libc::SYS_waitid, &[a[0], a[1], info.as_mut_ptr() as u64, a[3], hr]); + if r < 0 { + return Err(r); + } + if a[2] != 0 { + p.put(a[2], &signal::guest_siginfo(&signal::host_to_target_siginfo(&info)))?; + } + if a[4] != 0 { + p.put(a[4], &rusage32(&hru))?; + } + Ok(r) +} + +/// `sigaction()` with the old `struct sigaction`: handler, a one-word mask, flags and +/// restorer. +fn old_sigaction(p: &Proc, t: &mut Task, a: [u64; 6]) -> Res { + let act = if a[1] != 0 { + let b: [u8; 16] = read(p, a[1])?; + let f = |i: usize| u64::from(get32(&b, 4 * i)); + Some(Sigaction { handler: f(0), mask: f(1), flags: f(2), restorer: f(3) }) + } else { + None + }; + let old = signal::do_sigaction(t, a[0] as i32, act)?; + if a[2] != 0 { + let mut b = [0u8; 16]; + for (i, v) in [old.handler, old.mask, old.flags, old.restorer].into_iter().enumerate() { + put32(&mut b, 4 * i, v as u32); + } + p.put(a[2], &b)?; + } + Ok(0) +} + +/// `sigprocmask()` with one-word masks, which are read and written as `target_sigset_t`. +fn old_sigprocmask(p: &Proc, t: &mut Task, a: [u64; 6]) -> Res { + let (how, set) = if a[1] != 0 { + let b: [u8; 8] = read(p, a[1])?; + let how = a[0] as i32; + if !matches!(how, libc::SIG_BLOCK | libc::SIG_UNBLOCK | libc::SIG_SETMASK) { + return Err(-EINVAL); + } + (how, Some(u64::from(get32(&b, 0)))) + } else { + (0, None) + }; + let old = signal::old_sigprocmask(t, how, set)?; + if a[2] != 0 { + if !p.space().check(a[2], 8, page::WRITE) { + return Err(-EFAULT); + } + p.put(a[2], &(old as u32).to_le_bytes())?; + } + Ok(0) +} + /// The `fd_set` of `n` descriptors at `addr`, in the host's longs, `copy_from_user_fdset()`. /// Bit `i` is in the same byte either way. fn get_fdset(p: &Proc, addr: u64, n: u64) -> Result>, i64> { @@ -840,6 +901,50 @@ pub(crate) fn do_syscall( .collect(); host(libc::SYS_setgroups, &[n as u64, g.as_ptr() as u64]) } + "waitid" => return waitid(p, a), + "sigaction" => return old_sigaction(p, t, a), + "sigprocmask" => return old_sigprocmask(p, t, a), + "sgetmask" | "ssetmask" => { + let (how, set) = + if name == "sgetmask" { (0, None) } else { (libc::SIG_SETMASK, Some(a[0])) }; + return signal::old_sigprocmask(t, how, set).map(|m| i64::from(m as u32 as i32)); + } + "sigpending" => { + let mut set = 0u64; + let r = host(libc::SYS_rt_sigpending, &[(&raw mut set) as u64, 8]); + if r < 0 { + return Err(r); + } + if !p.space().check(a[0], 8, page::WRITE) { + return Err(-EFAULT); + } + p.put(a[0], &(signal::h2t_set(set) as u32).to_le_bytes())?; + r + } + // QEMU reads a whole target_sigset_t at the first argument, rather than the mask + // in the third the kernel takes. + "sigsuspend" => signal::sigsuspend(p.space(), t, a[0], 8), + "rt_sigtimedwait" | "rt_sigtimedwait_time64" => { + if a[3] != 8 { + return Err(-EINVAL); + } + let mut ts = [0u8; 16]; + if a[2] != 0 { + if name == "rt_sigtimedwait" { + let v = get_time32(p, a[2])?; + put64(&mut ts, 0, v[0] as u64); + put64(&mut ts, 8, v[1] as u64); + } else if !p.space().read(a[2], &mut ts) { + return Err(-EFAULT); + } + } + signal::sigtimedwait(p.space(), a[0], a[1], (a[2] != 0).then_some(&ts), a[3]) + } + "rt_sigreturn" => call(p, t, cpu, libc::SYS_rt_sigreturn, a), + "rt_sigaction" => call(p, t, cpu, libc::SYS_rt_sigaction, a), + "rt_sigqueueinfo" => call(p, t, cpu, libc::SYS_rt_sigqueueinfo, a), + "rt_tgsigqueueinfo" => call(p, t, cpu, libc::SYS_rt_tgsigqueueinfo, a), + "sigaltstack" => call(p, t, cpu, libc::SYS_sigaltstack, a), _ => match host_nr { Some(n) => call(p, t, cpu, n, a), None => -ENOSYS, diff --git a/crates/linux-user/src/guest.rs b/crates/linux-user/src/guest.rs index e2351a0..f926e19 100644 --- a/crates/linux-user/src/guest.rs +++ b/crates/linux-user/src/guest.rs @@ -46,12 +46,17 @@ pub(crate) struct Guest { pub(crate) cpu_loop: fn(&Arc, &mut Task, &mut Cpu<'_>), /// `setup_rt_frame()`: the frame for the handler of a signal on the guest stack, and the /// registers that enter it. The last argument is the guest mask to return to. - pub(crate) setup_rt_frame: - fn(&GuestSpace, &mut Task, &mut Cpu<'_>, i32, &Sigaction, &Info, u64), + pub(crate) setup_rt_frame: SetupFrame, + /// `setup_frame()`, for handlers without `SA_SIGINFO`, on the targets that have the old + /// frame. + pub(crate) setup_frame: Option, /// `do_rt_sigreturn()`. pub(crate) rt_sigreturn: fn(&GuestSpace, &mut Task, &mut Cpu<'_>) -> i64, } +/// `setup_frame()` and `setup_rt_frame()`. +pub(crate) type SetupFrame = fn(&GuestSpace, &mut Task, &mut Cpu<'_>, i32, &Sigaction, &Info, u64); + static GUEST: OnceLock<&'static Guest> = OnceLock::new(); /// Makes `g` the target, once, before the guest runs. diff --git a/crates/linux-user/src/i386.rs b/crates/linux-user/src/i386.rs index 8f8c8a8..69e68b6 100644 --- a/crates/linux-user/src/i386.rs +++ b/crates/linux-user/src/i386.rs @@ -2,10 +2,8 @@ //! The i386 target, `qemu-i386`: the x86 front end with a 32-bit code segment, the //! `TARGET_ABI32` parts of `linux-user/i386/cpu_loop.c` and `do_set_thread_area()`. The -//! system calls go through [`compat`](crate::compat). -//! -//! Signal frames are not there yet: a signal with a handler kills the program with `SIGSEGV`, -//! as a frame that does not fit on the stack would. +//! system calls go through [`compat`](crate::compat), and the signal frames are the +//! `TARGET_X86_64`-less ones of `linux-user/i386/signal.c`. mod syscalls; @@ -22,12 +20,14 @@ use ruvm_target_x86::state::{ R_EAX, R_EBP, R_EBX, R_ECX, R_EDI, R_EDX, R_ES, R_ESI, R_ESP, R_FS, R_GS, R_SS, SegmentCache, }; use ruvm_target_x86::tcg::env::{self, EIP, HF_OSFXSR_MASK, SEG_BASE, SEG_SIZE, SEGS}; -use ruvm_target_x86::tcg::user::cpu_x86_load_seg; +use ruvm_target_x86::tcg::user::{ + cpu_x86_frstor, cpu_x86_fsave, cpu_x86_fxrstor, cpu_x86_load_seg, +}; use ruvm_target_x86::tcg::{ EXCP_SYSCALL, EXCP00_DIVZ, EXCP0B_NOSEG, EXCP0C_STACK, EXCP0D_GPF, EXCP0E_PAGE, EXCP01_DB, EXCP03_INT3, EXCP04_INTO, EXCP05_BOUND, EXCP06_ILLOP, X86, helper_registry, jit_config, }; -use ruvm_user_common::GuestSpace; +use ruvm_user_common::{GuestSpace, MapKind, PAGE_SIZE, page}; use crate::compat; use crate::elf::{Arch, ImageInfo}; @@ -38,7 +38,7 @@ use crate::start; use crate::strace; use crate::syscall::{Proc, THREAD_EXIT}; use crate::x86_64::{ - IF_MASK, USER_DS, UserCpu, cpuid, flat_descriptor, gate64, map_rw, reg, set_reg, + self, IF_MASK, USER_DS, UserCpu, cpuid, flat_descriptor, gate64, map_rw, reg, set_reg, }; use syscalls::SYSCALLS; @@ -262,22 +262,361 @@ fn initial_state( Ok(s) } -/// What a 32-bit program's signal handler would be entered with; the frames are not written -/// yet, so the program gets `SIGSEGV`. -fn setup_rt_frame( - _space: &GuestSpace, +/// The sigreturn trampolines, `setup_sigtramp()`: QEMU has them in the vDSO. +static SIGTRAMP: OnceLock = OnceLock::new(); +/// Whether the CPU has FXSR, for `get_fpstate_kind()`. +static FXSR: OnceLock = OnceLock::new(); + +/// `TARGET_NR_sigreturn` and `TARGET_NR_rt_sigreturn`. +const NR_SIGRETURN: u32 = 119; +const NR_RT_SIGRETURN: u32 = 173; + +/// `install_sigtramp()`: `popl %eax; movl $119, %eax; int $0x80`. +fn sigtramp() -> [u8; 8] { + let mut c = [0x58, 0xb8, 0, 0, 0, 0, 0xcd, 0x80]; + put32(&mut c, 2, NR_SIGRETURN); + c +} + +/// `install_rt_sigtramp()`: `movl $173, %eax; int $0x80`. +fn rt_sigtramp() -> [u8; 8] { + let mut c = [0xb8, 0, 0, 0, 0, 0xcd, 0x80, 0]; + put32(&mut c, 1, NR_RT_SIGRETURN); + c +} + +/// The page of the trampolines, where QEMU loads the vDSO: `sigreturn` at the start and +/// `rt_sigreturn` 8 bytes in. +fn map_sigtramp(space: &GuestSpace) -> Result { + let kind = MapKind { anon: true, ..MapKind::default() }; + let err = |e| format!("mmap: {}", crate::strerror(e)); + let addr = space.mmap(0, PAGE_SIZE, page::READ | page::WRITE, kind, None, 0).map_err(err)?; + space.write_raw(addr, &sigtramp()); + space.write_raw(addr + 8, &rt_sigtramp()); + space.mprotect(addr, PAGE_SIZE, page::READ | page::EXEC).map_err(err)?; + Ok(addr) +} + +/// `struct sigframe`: the return address, the signal, the `sigcontext`, the unused +/// `fpstate`, the high word of the mask and the return code. +const FRAME_SIZE: u32 = 732; +const SIGFRAME_SC: usize = 8; +const EXTRAMASK: usize = 720; +const RETCODE: usize = 724; +/// `struct rt_sigframe`: the return address, the signal, the pointers to the `siginfo` and +/// the `ucontext`, which follow, and the return code. +const RT_FRAME_SIZE: u32 = 268; +const INFO: usize = 16; +const UC: usize = 144; +const UC_STACK: usize = UC + 8; +const UC_MCONTEXT: usize = UC + 20; +const UC_SIGMASK: usize = UC + 108; +const RT_RETCODE: usize = 260; +/// `struct target_sigcontext_32`, after GS, FS, ES and DS: EDI to EAX, in this order. +const SC_REGS: [usize; 8] = [R_EDI, R_ESI, R_EBP, R_ESP, R_EBX, R_EDX, R_ECX, R_EAX]; +const SC_TRAPNO: usize = 48; +const SC_ERR: usize = 52; +const SC_EIP: usize = 56; +const SC_CS: usize = 60; +const SC_EFLAGS: usize = 64; +const SC_ESP_AT_SIGNAL: usize = 68; +const SC_SS: usize = 72; +const SC_FPSTATE: usize = 76; +const SC_OLDMASK: usize = 80; +const SC_CR2: usize = 84; +/// `struct target_fregs_state`, what FSAVE writes with `status` and `magic` after it. +const FREGS_SIZE: u32 = 112; + +/// `FPStateKind`. +#[derive(Clone, Copy, PartialEq)] +enum FpKind { + Fsave, + Fxsave, + Xsave, +} + +/// `get_fpstate_kind()`. +fn fpstate_kind(cpu: &Cpu<'_>) -> FpKind { + if x86_64::uses_xsave(cpu) { + FpKind::Xsave + } else if FXSR.get().copied().unwrap_or(false) { + FpKind::Fxsave + } else { + FpKind::Fsave + } +} + +/// `get_fpstate_size()`. +fn fpstate_size(cpu: &Cpu<'_>, kind: FpKind) -> u32 { + match kind { + FpKind::Xsave => x86_64::fpstate_size(cpu, true) as u32, + FpKind::Fxsave => 512, + FpKind::Fsave => FREGS_SIZE, + } +} + +/// `get_sigframe()`: the frame, the FSAVE state, the FXSAVE or XSAVE state and the end. +fn get_sigframe( + t: &Task, + cpu: &Cpu<'_>, + sa: &Sigaction, + size: u32, + kind: FpKind, +) -> (u32, u32, u32, u32) { + let mut sp = reg(cpu, R_ESP) as u32; + if sa.flags & signal::SA_ONSTACK != 0 { + sp = signal::target_sigsp(t, sp.into(), sa) as u32; + } else if env::ld32(cpu.env, SEGS + R_SS * SEG_SIZE) & 0xffff != USER_DS + && sa.flags & signal::SA_RESTORER == 0 + && sa.restorer != 0 + { + // The legacy stack switch. + sp = sa.restorer as u32; + } + let math = fpstate_size(cpu, kind); + sp = sp.wrapping_sub(math) & !63; + let fpend = sp.wrapping_add(math); + let fxstate = sp; + if kind != FpKind::Fsave { + sp = sp.wrapping_sub(FREGS_SIZE); + } + let fpstate = sp; + sp = sp.wrapping_sub(size).wrapping_add(4) & !15; + (sp.wrapping_sub(4), fpstate, fxstate, fpend) +} + +/// `setup_sigcontext()` into `sc`, and the floating point state into guest memory; false +/// when that does not write. +#[allow(clippy::too_many_arguments)] +fn setup_sigcontext( + space: &GuestSpace, + cpu: &mut Cpu<'_>, + sc: &mut [u8], + mask: u32, + kind: FpKind, + fpstate: u32, + fxstate: u32, + fpend: u32, +) -> bool { + let sel = |cpu: &Cpu<'_>, s: usize| env::ld32(cpu.env, SEGS + s * SEG_SIZE); + for (i, s) in [R_GS, R_FS, R_ES, R_DS].into_iter().enumerate() { + put32(sc, 4 * i, sel(cpu, s)); + } + for (i, r) in SC_REGS.into_iter().enumerate() { + put32(sc, 16 + 4 * i, reg(cpu, r) as u32); + } + put32(sc, SC_TRAPNO, cpu.core.exception_index as u32); + put32(sc, SC_ERR, env::ld32(cpu.env, env::ERROR_CODE)); + put32(sc, SC_EIP, env::ld64(cpu.env, EIP) as u32); + put32(sc, SC_CS, sel(cpu, R_CS)); + put32(sc, SC_EFLAGS, env::ld64(cpu.env, env::EFLAGS) as u32); + put32(sc, SC_ESP_AT_SIGNAL, reg(cpu, R_ESP) as u32); + put32(sc, SC_SS, sel(cpu, R_SS)); + + // FSAVE resets the FPU, and QEMU saves the FXSAVE or XSAVE state after it. + let (fp, fx) = (u64::from(fpstate), u64::from(fxstate)); + // status repeats the status word; magic is 0xffff when FXSAVE or XSAVE state follows. + let mut tail = [0u8; 4]; + if kind != FpKind::Fsave { + tail[2..].fill(0xff); + } + let ok = cpu_x86_fsave(cpu, fp).is_ok() + && space.read(fp + 4, &mut tail[..2]) + && space.write_raw(fp + u64::from(FREGS_SIZE) - 4, &tail) + && (kind == FpKind::Fsave + || x86_64::save_fpstate(space, cpu, kind == FpKind::Xsave, fp, fx, fpend.into())); + put32(sc, SC_FPSTATE, fpstate); + put32(sc, SC_OLDMASK, mask); + put32(sc, SC_CR2, env::ld64(cpu.env, env::cr(2)) as u32); + ok +} + +/// The registers both frames enter the handler with. +fn enter_handler(cpu: &mut Cpu<'_>, frame: u32, handler: u64, sig: i32, rt: bool) { + set_reg(cpu, R_ESP, frame.into()); + env::st64(cpu.env, EIP, handler & 0xffff_ffff); + set_reg(cpu, R_EAX, sig as u64); + let (edx, ecx) = if rt { (frame + INFO as u32, frame + UC as u32) } else { (0, 0) }; + set_reg(cpu, R_EDX, edx.into()); + set_reg(cpu, R_ECX, ecx.into()); + let segs = if rt { + [(R_DS, USER_DS), (R_ES, USER_DS), (R_CS, USER_CS), (R_SS, USER_DS)] + } else { + [(R_DS, USER_DS), (R_ES, USER_DS), (R_SS, USER_DS), (R_CS, USER_CS)] + }; + for (seg, s) in segs { + if cpu_x86_load_seg(cpu, seg, s).is_err() { + break; + } + } + let fl = env::ld64(cpu.env, env::EFLAGS); + env::st64(cpu.env, env::EFLAGS, fl & !x86_64::TF_MASK); +} + +/// `setup_frame()`: the frame of a handler without `SA_SIGINFO`. `old` is the guest mask to +/// return to. +fn setup_frame( + space: &GuestSpace, t: &mut Task, - _cpu: &mut Cpu<'_>, + cpu: &mut Cpu<'_>, sig: i32, - _sa: &Sigaction, + sa: &Sigaction, _info: &signal::Info, - _old: u64, + old: u64, ) { - signal::force_sigsegv(t, sig); + let kind = fpstate_kind(cpu); + let (frame, fpstate, fxstate, fpend) = get_sigframe(t, cpu, sa, FRAME_SIZE, kind); + let mut f = [0u8; FRAME_SIZE as usize]; + let addr = u64::from(frame); + if fpend < frame + || !space.check(addr, u64::from(fpend - frame), page::WRITE) + || !space.read_raw(addr, &mut f) + { + signal::force_sigsegv(t, sig); + return; + } + let sc = &mut f[SIGFRAME_SC..SIGFRAME_SC + 88]; + if !setup_sigcontext(space, cpu, sc, old as u32, kind, fpstate, fxstate, fpend) { + cpu.core.exception_index = -1; + signal::force_sigsegv(t, sig); + return; + } + put32(&mut f, EXTRAMASK, (old >> 32) as u32); + if sa.flags & signal::SA_RESTORER != 0 { + put32(&mut f, 0, sa.restorer as u32); + } else { + f[RETCODE..].copy_from_slice(&sigtramp()); + put32(&mut f, 0, *SIGTRAMP.get().expect("the vCPU is created first") as u32); + } + put32(&mut f, 4, sig as u32); + space.write_raw(addr, &f); + enter_handler(cpu, frame, sa.handler, sig, false); +} + +/// `setup_rt_frame()`. +fn setup_rt_frame( + space: &GuestSpace, + t: &mut Task, + cpu: &mut Cpu<'_>, + sig: i32, + sa: &Sigaction, + info: &signal::Info, + old: u64, +) { + let kind = fpstate_kind(cpu); + let (frame, fpstate, fxstate, fpend) = get_sigframe(t, cpu, sa, RT_FRAME_SIZE, kind); + let mut f = [0u8; RT_FRAME_SIZE as usize]; + let addr = u64::from(frame); + if fpend < frame + || !space.check(addr, u64::from(fpend - frame), page::WRITE) + || !space.read_raw(addr, &mut f) + { + signal::force_sigsegv(t, sig); + return; + } + if sa.flags & signal::SA_SIGINFO != 0 { + f[INFO..UC].copy_from_slice(&signal::guest_siginfo(info)); + } + put32(&mut f, UC, u32::from(kind == FpKind::Xsave)); + put32(&mut f, UC + 4, 0); + let esp = reg(cpu, R_ESP) & 0xffff_ffff; + f[UC_STACK..UC_MCONTEXT].copy_from_slice(&signal::save_altstack(t, esp)[..12]); + let sc = &mut f[UC_MCONTEXT..UC_SIGMASK]; + if !setup_sigcontext(space, cpu, sc, old as u32, kind, fpstate, fxstate, fpend) { + cpu.core.exception_index = -1; + signal::force_sigsegv(t, sig); + return; + } + put32(&mut f, UC_SIGMASK, old as u32); + put32(&mut f, UC_SIGMASK + 4, (old >> 32) as u32); + if sa.flags & signal::SA_RESTORER != 0 { + put32(&mut f, 0, sa.restorer as u32); + } else { + f[RT_RETCODE..].copy_from_slice(&rt_sigtramp()); + put32(&mut f, 0, *SIGTRAMP.get().expect("the vCPU is created first") as u32 + 8); + } + put32(&mut f, 4, sig as u32); + put32(&mut f, 8, frame + INFO as u32); + put32(&mut f, 12, frame + UC as u32); + space.write_raw(addr, &f); + enter_handler(cpu, frame, sa.handler, sig, true); } -fn do_rt_sigreturn(_space: &GuestSpace, t: &mut Task, _cpu: &mut Cpu<'_>) -> i64 { - signal::force_sig(t, signal::SIGSEGV); +/// `restore_sigcontext()` from `sc`, false when the floating point state does not load. +fn restore_sigcontext(space: &GuestSpace, cpu: &mut Cpu<'_>, sc: &[u8]) -> bool { + // A selector that does not load leaves a #GP for the next instruction. + let seg = |i: usize| u32::from(get32(sc, i) as u16); + let _ = [R_GS, R_FS, R_ES, R_DS] + .into_iter() + .enumerate() + .try_for_each(|(i, s)| cpu_x86_load_seg(cpu, s, seg(4 * i))); + for (i, r) in SC_REGS.into_iter().enumerate() { + set_reg(cpu, r, get32(sc, 16 + 4 * i).into()); + } + env::st64(cpu.env, EIP, get32(sc, SC_EIP).into()); + if cpu_x86_load_seg(cpu, R_CS, seg(SC_CS) | 3).is_ok() { + let _ = cpu_x86_load_seg(cpu, R_SS, seg(SC_SS) | 3); + } + let fl = env::ld64(cpu.env, env::EFLAGS); + let tmp = u64::from(get32(sc, SC_EFLAGS)); + let fix = x86_64::FIX_EFLAGS; + env::st64(cpu.env, env::EFLAGS, (fl & !fix) | (tmp & fix)); + + let fp = u64::from(get32(sc, SC_FPSTATE)); + if fp == 0 { + return true; + } + // frstor_sigcontext(): the FXSAVE or XSAVE state, then the FSAVE state over it. + let kind = fpstate_kind(cpu); + let mut math = u64::from(fpstate_size(cpu, kind)); + if kind != FpKind::Fsave { + math += u64::from(FREGS_SIZE); + } + if !space.check(fp, math, page::READ) { + return false; + } + let fx = fp + u64::from(FREGS_SIZE); + let fx_ok = match kind { + FpKind::Xsave => x86_64::restore_fpstate(space, cpu, fx), + FpKind::Fxsave => cpu_x86_fxrstor(cpu, fx).is_ok(), + FpKind::Fsave => true, + }; + fx_ok && cpu_x86_frstor(cpu, fp).is_ok() +} + +/// `do_sigreturn()`. +fn do_sigreturn(space: &GuestSpace, t: &mut Task, cpu: &mut Cpu<'_>) -> i64 { + let frame = reg(cpu, R_ESP).wrapping_sub(8) & 0xffff_ffff; + let mut f = [0u8; FRAME_SIZE as usize]; + if !space.read(frame, &mut f) { + signal::force_sig(t, signal::SIGSEGV); + return signal::ESIGRETURN; + } + let sc = &f[SIGFRAME_SC..SIGFRAME_SC + 88]; + let mask = u64::from(get32(sc, SC_OLDMASK)) | (u64::from(get32(&f, EXTRAMASK)) << 32); + signal::set_sigmask(t, signal::t2h_set(mask)); + if !restore_sigcontext(space, cpu, sc) { + signal::force_sig(t, signal::SIGSEGV); + } + signal::ESIGRETURN +} + +/// `do_rt_sigreturn()`. +fn do_rt_sigreturn(space: &GuestSpace, t: &mut Task, cpu: &mut Cpu<'_>) -> i64 { + let frame = reg(cpu, R_ESP).wrapping_sub(4) & 0xffff_ffff; + let mut f = [0u8; RT_FRAME_SIZE as usize]; + if !space.read(frame, &mut f) { + signal::force_sig(t, signal::SIGSEGV); + return signal::ESIGRETURN; + } + let mask = u64::from(get32(&f, UC_SIGMASK)) | (u64::from(get32(&f, UC_SIGMASK + 4)) << 32); + signal::set_sigmask(t, signal::t2h_set(mask)); + if !restore_sigcontext(space, cpu, &f[UC_MCONTEXT..UC_SIGMASK]) { + signal::force_sig(t, signal::SIGSEGV); + return signal::ESIGRETURN; + } + let sp = reg(cpu, R_ESP); + let _ = signal::restore_altstack(t, &f[UC_STACK..UC_MCONTEXT], sp); signal::ESIGRETURN } @@ -296,6 +635,12 @@ fn do_syscall(p: &Arc, t: &mut Task, cpu: &mut Cpu<'_>, nr: u64, a: [u64; match name { "set_thread_area" => set_thread_area(p.space(), cpu, a[0]), "get_thread_area" => get_thread_area(p.space(), cpu, a[0]), + "sigreturn" => { + if signal::block_signals(t) { + return signal::ERESTARTSYS; + } + do_sigreturn(p.space(), t, cpu) + } _ => compat::do_syscall(p, t, cpu, name, (host >= 0).then_some(i64::from(host)), a), } } @@ -388,6 +733,7 @@ pub(crate) static GUEST: Guest = Guest { }, set_tls, cpu_loop, + setup_frame: Some(setup_frame), setup_rt_frame, rt_sigreturn: do_rt_sigreturn, }; @@ -464,6 +810,8 @@ impl start::Target for Target { info: &ImageInfo, ) -> Result { let model = self.model.take().expect("the CPU is selected first"); + let _ = FXSR.set(cpuid(&model, 1, 0)[3] & (1 << 24) != 0); + let _ = SIGTRAMP.set(map_sigtramp(space)?); let state = initial_state(&model, space, info.entry, info.start_stack)?; let _ = SPACE.set(Arc::clone(space)); let x86 = Arc::new(X86::new(model).with_user_mode()); diff --git a/crates/linux-user/src/lib.rs b/crates/linux-user/src/lib.rs index e196a08..977ff55 100644 --- a/crates/linux-user/src/lib.rs +++ b/crates/linux-user/src/lib.rs @@ -47,15 +47,15 @@ //! FPMR signal frame records, and `rt_sigreturn()` keeps the program at EL0t whatever the //! frame says. Handlers without `SA_RESTORER` return through a page QEMU maps when it has //! no vDSO. -//! - i386 programs cannot set signal handlers yet: `rt_sigaction()`, `sigaltstack()` and the -//! old signal calls are `ENOSYS`, as are the socket, System V IPC, POSIX timer and message -//! queue calls, `vm86()` and `modify_ldt()`. -//! - arm programs are EABI ones, little endian, with the same limits as i386 programs: no -//! signal handlers and `ENOSYS` for the calls i386 does not have. An OABI call, an SVC with -//! a nonzero immediate, is `ENOSYS` but for the private calls (`set_tls` and the others). -//! There is no commpage, so the kuser helpers are not there, no FPA emulation, no FDPIC -//! and no `-g`. The hardware capabilities leave out Advanced SIMD and the crypto -//! instructions. +//! - On i386 the socket, System V IPC, POSIX timer and message queue calls, `vm86()` and +//! `modify_ldt()` are `ENOSYS`. There is no vDSO: the signal return trampolines are on a +//! page where QEMU loads it. +//! - arm programs are EABI ones, little endian. They cannot set signal handlers yet: +//! `rt_sigaction()`, `sigaltstack()` and the old signal calls are `ENOSYS`, as are the calls +//! i386 does not have. An OABI call, an SVC with a nonzero immediate, is `ENOSYS` but for +//! the private calls (`set_tls` and the others). There is no commpage, so the kuser helpers +//! are not there, no FPA emulation, no FDPIC and no `-g`. The hardware capabilities leave +//! out Advanced SIMD and the crypto instructions. //! - The guest space is a 64 TiB reservation, as QEMU lays it out with `-R`, so addresses //! differ from QEMU's default layout. For i386 and arm it is the whole 4 GiB, as in QEMU. diff --git a/crates/linux-user/src/riscv64.rs b/crates/linux-user/src/riscv64.rs index d73872b..e9fe5b5 100644 --- a/crates/linux-user/src/riscv64.rs +++ b/crates/linux-user/src/riscv64.rs @@ -687,6 +687,7 @@ pub(crate) static GUEST: Guest = Guest { }, set_tls, cpu_loop, + setup_frame: None, setup_rt_frame, rt_sigreturn: do_rt_sigreturn, }; diff --git a/crates/linux-user/src/signal.rs b/crates/linux-user/src/signal.rs index b1d5326..91b8dba 100644 --- a/crates/linux-user/src/signal.rs +++ b/crates/linux-user/src/signal.rs @@ -244,10 +244,49 @@ pub(crate) fn host_to_target_siginfo(h: &Info) -> Info { fn target_to_host_siginfo(t: &Info) -> Info { let mut h = [0u8; 128]; h[..12].copy_from_slice(&t[..12]); - h[16..32].copy_from_slice(&t[16..32]); + if crate::guest::guest().abi32 { + // pid, uid and a 32-bit sigval, after three ints rather than four. + put32(&mut h, 16, get32(t, 12)); + put32(&mut h, 20, get32(t, 16)); + put64(&mut h, 24, u64::from(get32(t, 20))); + } else { + h[16..32].copy_from_slice(&t[16..32]); + } h } +/// The guest's `siginfo_t` of `info`, which has the layout of a 64-bit one: on a 32-bit +/// target the union starts at 12 and its longs and pointers are 32 bits. +pub(crate) fn guest_siginfo(info: &Info) -> Info { + if !crate::guest::guest().abi32 { + return *info; + } + let (sig, code) = (get32(info, 0) as i32, get32(info, 8) as i32); + let mut t = [0u8; 128]; + t[..12].copy_from_slice(&info[..12]); + // The ints at 16 and 20 move down by 4 whatever the union member is: pid and uid, + // the timer id and overrun, or a fault address of 32 bits and its zero top half. + put32(&mut t, 12, get32(info, 16)); + put32(&mut t, 16, get32(info, 20)); + let sifields = !matches!(code, SI_USER | SI_TKILL | SI_KERNEL); + match sig { + SIGCHLD if sifields => { + // status, utime and stime. + put32(&mut t, 20, get32(info, 24)); + put32(&mut t, 24, get64(info, 32) as u32); + put32(&mut t, 28, get64(info, 40) as u32); + } + // A long band and the fd. + SIGIO if sifields => { + put32(&mut t, 12, get64(info, 16) as u32); + put32(&mut t, 16, get32(info, 24)); + } + // The value of sigqueue() and the like. + _ => put32(&mut t, 20, get64(info, 24) as u32), + } + t +} + /// One entry of `sigtab`: a pending signal and its information. struct Slot { pending: AtomicBool, @@ -661,7 +700,13 @@ fn handle_pending_signal( let blocked = if t.in_sigsuspend { t.sigsuspend_mask } else { t.signal_mask }; t.signal_mask = blocked | set; t.in_sigsuspend = false; - (crate::guest::guest().setup_rt_frame)(space, t, cpu, sig, &sa, info, old); + let g = crate::guest::guest(); + match g.setup_frame { + Some(setup_frame) if sa.flags & SA_SIGINFO == 0 => { + setup_frame(space, t, cpu, sig, &sa, info, old); + } + _ => (g.setup_rt_frame)(space, t, cpu, sig, &sa, info, old), + } if sa.flags & SA_RESETHAND != 0 { sigact()[sig as usize - 1].handler = SIG_DFL; } @@ -737,18 +782,33 @@ pub(crate) fn target_sigsp(t: &Task, sp: u64, sa: &Sigaction) -> u64 { sp } -/// `target_save_altstack()`: the guest `stack_t` at `sp`. +/// The size of the guest `stack_t`: a pointer, an int and a `size_t`. +pub(crate) fn stack_t_size() -> usize { + if crate::guest::guest().abi32 { 12 } else { 24 } +} + +/// `target_save_altstack()`: the guest `stack_t` at `sp`, in the first [`stack_t_size`] bytes. pub(crate) fn save_altstack(t: &Task, sp: u64) -> [u8; 24] { let mut b = [0u8; 24]; - put64(&mut b, 0, t.altstack.0); - put32(&mut b, 8, sas_ss_flags(t, sp) as u32); - put64(&mut b, 16, t.altstack.1); + if crate::guest::guest().abi32 { + put32(&mut b, 0, t.altstack.0 as u32); + put32(&mut b, 4, sas_ss_flags(t, sp) as u32); + put32(&mut b, 8, t.altstack.1 as u32); + } else { + put64(&mut b, 0, t.altstack.0); + put32(&mut b, 8, sas_ss_flags(t, sp) as u32); + put64(&mut b, 16, t.altstack.1); + } b } /// `target_restore_altstack()` from the guest `stack_t` `uss`. pub(crate) fn restore_altstack(t: &mut Task, uss: &[u8], sp: u64) -> i64 { - let (ss_sp, flags, mut size) = (get64(uss, 0), get32(uss, 8) as i32, get64(uss, 16)); + let (ss_sp, flags, mut size) = if crate::guest::guest().abi32 { + (u64::from(get32(uss, 0)), get32(uss, 4) as i32, u64::from(get32(uss, 8))) + } else { + (get64(uss, 0), get32(uss, 8) as i32, get64(uss, 16)) + }; if on_sig_stack(t, sp) { return -i64::from(libc::EPERM); } @@ -771,13 +831,14 @@ pub(crate) fn restore_altstack(t: &mut Task, uss: &[u8], sp: u64) -> i64 { /// `do_sigaltstack()`. fn do_sigaltstack(space: &GuestSpace, t: &mut Task, uss: u64, uoss: u64, sp: u64) -> i64 { - if uoss != 0 && !space.check(uoss, 24, ruvm_user_common::page::WRITE) { + let size = stack_t_size(); + if uoss != 0 && !space.check(uoss, size as u64, ruvm_user_common::page::WRITE) { return -EFAULT; } let old = save_altstack(t, sp); if uss != 0 { let mut b = [0u8; 24]; - if !space.read(uss, &mut b) { + if !space.read(uss, &mut b[..size]) { return -EFAULT; } let r = restore_altstack(t, &b, sp); @@ -785,21 +846,30 @@ fn do_sigaltstack(space: &GuestSpace, t: &mut Task, uss: u64, uoss: u64, sp: u64 return r; } } - if uoss != 0 && !space.write(uoss, &old) { + if uoss != 0 && !space.write(uoss, &old[..size]) { return -EFAULT; } 0 } +/// An `abi_ulong` of the guest at `off`. +fn get_ulong(b: &[u8], off: usize) -> u64 { + if crate::guest::guest().abi32 { u64::from(get32(b, off)) } else { get64(b, off) } +} + +fn put_ulong(b: &mut [u8], off: usize, v: u64) { + if crate::guest::guest().abi32 { put32(b, off, v as u32) } else { put64(b, off, v) } +} + /// `do_sigaction()` for `rt_sigaction(sig, act, oact, sigsetsize)`. -fn do_sigaction(space: &GuestSpace, t: &Task, a: [u64; 6]) -> i64 { - let sig = a[0] as i32; +fn do_rt_sigaction(space: &GuestSpace, t: &Task, a: [u64; 6]) -> i64 { if a[3] != 8 { return -EINVAL; } // handler, flags, restorer when the target has it, and mask. - let restorer = crate::guest::guest().sa_restorer; - let size = if restorer { 32 } else { 24 }; + let g = crate::guest::guest(); + let w = if g.abi32 { 4 } else { 8 }; + let size = if g.sa_restorer { 3 * w + 8 } else { 2 * w + 8 }; let mut act = [0u8; 32]; if a[1] != 0 && !space.read(a[1], &mut act[..size]) { return -EFAULT; @@ -807,53 +877,117 @@ fn do_sigaction(space: &GuestSpace, t: &Task, a: [u64; 6]) -> i64 { if a[2] != 0 && !space.check(a[2], size as u64, ruvm_user_common::page::WRITE) { return -EFAULT; } - if !(1..=NSIG).contains(&sig) { - return -EINVAL; - } - if a[1] != 0 && (sig == SIGKILL || sig == SIGSTOP) { - return -EINVAL; - } - if block_signals(t) { - return ERESTARTSYS; - } - let mut tab = sigact(); - let k = &mut tab[sig as usize - 1]; + let new = (a[1] != 0).then(|| Sigaction { + handler: get_ulong(&act, 0), + flags: get_ulong(&act, w), + restorer: if g.sa_restorer { get_ulong(&act, 2 * w) } else { 0 }, + mask: get64(&act, size - 8), + }); + let old = match do_sigaction(t, a[0] as i32, new) { + Ok(old) => old, + Err(e) => return e, + }; if a[2] != 0 { let mut b = [0u8; 32]; - put64(&mut b, 0, k.handler); - put64(&mut b, 8, k.flags); - if restorer { - put64(&mut b, 16, k.restorer); + put_ulong(&mut b, 0, old.handler); + put_ulong(&mut b, w, old.flags); + if g.sa_restorer { + put_ulong(&mut b, 2 * w, old.restorer); } - put64(&mut b, size - 8, k.mask); + put64(&mut b, size - 8, old.mask); if !space.write(a[2], &b[..size]) { return -EFAULT; } } - if a[1] == 0 { - return 0; + 0 +} + +/// `do_sigaction()`: makes `act` the action of `sig`, when there is one, and returns the +/// action before. +pub(crate) fn do_sigaction(t: &Task, sig: i32, act: Option) -> Result { + if !(1..=NSIG).contains(&sig) { + return Err(-EINVAL); } - *k = Sigaction { - handler: get64(&act, 0), - flags: get64(&act, 8), - restorer: if restorer { get64(&act, 16) } else { 0 }, - mask: get64(&act, size - 8), - }; - let host_sig = t2h(sig); - if host_sig > NSIG { - // Not enough host signals for every target one; programs that register handlers for - // all of them must not fail. - return 0; + if act.is_some() && (sig == SIGKILL || sig == SIGSTOP) { + return Err(-EINVAL); } - if host_sig == libc::SIGSEGV || host_sig == libc::SIGBUS { - return 0; + if block_signals(t) { + return Err(ERESTARTSYS); } - match k.handler { + let mut tab = sigact(); + let k = &mut tab[sig as usize - 1]; + let old = *k; + let Some(act) = act else { return Ok(old) }; + *k = act; + let host_sig = t2h(sig); + // Not enough host signals for every target one; programs that register handlers for all + // of them must not fail. + if host_sig > NSIG || host_sig == libc::SIGSEGV || host_sig == libc::SIGBUS { + return Ok(old); + } + let r = match k.handler { SIG_IGN => host::set_disposition(host_sig, SIG_IGN), SIG_DFL if !core_dump_signal(sig) => host::set_disposition(host_sig, SIG_DFL), SIG_DFL => host::install_handler(host_sig, false), _ => host::install_handler(host_sig, k.flags & SA_RESTART != 0), + }; + if r < 0 { Err(r) } else { Ok(old) } +} + +/// `do_sigprocmask()` for the calls of the old one-word masks, `sigprocmask()`, `sgetmask()` +/// and `ssetmask()`: the old guest mask, as one. +pub(crate) fn old_sigprocmask(t: &mut Task, how: i32, set: Option) -> Result { + let old = t.signal_mask; + let r = do_sigprocmask(t, how, set.map(t2h_set)); + if r < 0 { Err(r) } else { Ok(h2t_set(old) & 0xffff_ffff) } +} + +/// `sigsuspend()` with the guest mask at `set`, which is `size` bytes. +pub(crate) fn sigsuspend(space: &GuestSpace, t: &mut Task, set: u64, size: u64) -> i64 { + if let Err(e) = sigsuspend_mask(space, t, set, size) { + return e; + } + let mask = t.sigsuspend_mask; + let r = guest_sys(libc::SYS_rt_sigsuspend, &[(&raw const mask) as u64, 8]); + finish_sigsuspend(t, r); + r +} + +/// `rt_sigtimedwait()` with the guest mask at `set` and the host `timespec` `ts`, which +/// writes the guest's `siginfo_t` to `uinfo`. +pub(crate) fn sigtimedwait( + space: &GuestSpace, + set: u64, + uinfo: u64, + ts: Option<&[u8; 16]>, + size: u64, +) -> i64 { + if size != 8 { + return -EINVAL; + } + let set = match read_u64(space, set) { + Ok(s) => t2h_set(s), + Err(e) => return e, + }; + let mut hinfo = [0u64; 16]; + let tsp = ts.map_or(0, |ts| ts.as_ptr() as u64); + let r = guest_sys( + libc::SYS_rt_sigtimedwait, + &[(&raw const set) as u64, hinfo.as_mut_ptr() as u64, tsp, 8], + ); + if r < 0 { + return r; } + if uinfo != 0 { + let mut b = [0u8; 128]; + for (i, w) in hinfo.iter().enumerate() { + put64(&mut b, 8 * i, *w); + } + if !space.write(uinfo, &guest_siginfo(&host_to_target_siginfo(&b))) { + return -EFAULT; + } + } + i64::from(h2t(r as i32)) } /// `process_sigsuspend_mask()`: the host form of the guest mask at `set`, `size` bytes. @@ -922,7 +1056,7 @@ pub(crate) fn do_signal_syscall( a: [u64; 6], ) -> Option { let r = match n { - libc::SYS_rt_sigaction => do_sigaction(space, t, a), + libc::SYS_rt_sigaction => do_rt_sigaction(space, t, a), libc::SYS_rt_sigprocmask => { if a[3] != 8 { return Some(-EINVAL); @@ -961,15 +1095,7 @@ pub(crate) fn do_signal_syscall( } r } - libc::SYS_rt_sigsuspend => { - if let Err(e) = sigsuspend_mask(space, t, a[0], a[1]) { - return Some(e); - } - let mask = t.sigsuspend_mask; - let r = guest_sys(libc::SYS_rt_sigsuspend, &[(&raw const mask) as u64, 8]); - finish_sigsuspend(t, r); - r - } + libc::SYS_rt_sigsuspend => sigsuspend(space, t, a[0], a[1]), libc::SYS_pause => { if !block_signals(t) { let mask = t.signal_mask; @@ -981,33 +1107,11 @@ pub(crate) fn do_signal_syscall( if a[3] != 8 { return Some(-EINVAL); } - let set = match read_u64(space, a[0]) { - Ok(s) => t2h_set(s), - Err(e) => return Some(e), - }; let mut ts = [0u8; 16]; if a[2] != 0 && !space.read(a[2], &mut ts) { return Some(-EFAULT); } - let mut hinfo = [0u64; 16]; - let tsp = if a[2] != 0 { ts.as_ptr() as u64 } else { 0 }; - let r = guest_sys( - libc::SYS_rt_sigtimedwait, - &[(&raw const set) as u64, hinfo.as_mut_ptr() as u64, tsp, 8], - ); - if r < 0 { - return Some(r); - } - if a[1] != 0 { - let mut b = [0u8; 128]; - for (i, w) in hinfo.iter().enumerate() { - put64(&mut b, 8 * i, *w); - } - if !space.write(a[1], &host_to_target_siginfo(&b)) { - return Some(-EFAULT); - } - } - i64::from(h2t(r as i32)) + sigtimedwait(space, a[0], a[1], (a[2] != 0).then_some(&ts), a[3]) } libc::SYS_rt_sigqueueinfo | libc::SYS_rt_tgsigqueueinfo => { let at = if n == libc::SYS_rt_sigqueueinfo { 2 } else { 3 }; diff --git a/crates/linux-user/src/x86_64.rs b/crates/linux-user/src/x86_64.rs index c8d69c7..fea0335 100644 --- a/crates/linux-user/src/x86_64.rs +++ b/crates/linux-user/src/x86_64.rs @@ -425,20 +425,47 @@ const FP_XSTATE_MAGIC2: u32 = 0x4650_5845; /// The legacy area and the XSAVE header. const XSAVE_MIN: u64 = 512 + 64; /// `TF_MASK`. -const TF_MASK: u64 = 1 << 8; +pub(crate) const TF_MASK: u64 = 1 << 8; /// The flags `sigreturn()` takes from the frame: CF, PF, AF, ZF, SF, TF, DF, OF and AC. -const FIX_EFLAGS: u64 = 0x40DD5; +pub(crate) const FIX_EFLAGS: u64 = 0x40DD5; /// `get_fpstate_kind()`: whether frames carry XSAVE state rather than FXSAVE state. -fn uses_xsave(cpu: &Cpu<'_>) -> bool { +pub(crate) fn uses_xsave(cpu: &Cpu<'_>) -> bool { env::ld64(cpu.env, env::cr(4)) & CR4_OSXSAVE_MASK != 0 } /// `get_fpstate_size()`. -fn fpstate_size(cpu: &Cpu<'_>, xsave: bool) -> u64 { +pub(crate) fn fpstate_size(cpu: &Cpu<'_>, xsave: bool) -> u64 { if xsave { xsave_area_size(cpu, env::ld64(cpu.env, env::XCR0)) as u64 + 4 } else { 512 } } +/// `xsave_sigcontext()` or `fxsave_sigcontext()`: the state at `fxstate`, in the frame's +/// floating point area from `fpstate` to `fpend`, false when it does not write. +pub(crate) fn save_fpstate( + space: &GuestSpace, + cpu: &mut Cpu<'_>, + xsave: bool, + fpstate: u64, + fxstate: u64, + fpend: u64, +) -> bool { + if !xsave { + return cpu_x86_fxsave(cpu, fxstate).is_ok() && space.write_raw(fxstate + FP_SW, &[0u8; 4]); + } + // XSAVE adds to the header, so it starts out zero. + let xcr0 = env::ld64(cpu.env, env::XCR0); + let xstate_size = fpend - fxstate - 4; + space.write_raw(fxstate + 512, &[0u8; 64]) && cpu_x86_xsave(cpu, fxstate, xcr0).is_ok() && { + let mut sw = [0u8; 24]; + put32(&mut sw, 0, FP_XSTATE_MAGIC1); + put32(&mut sw, 4, (fpend - fpstate) as u32); + put64(&mut sw, 8, xcr0); + put32(&mut sw, 16, xstate_size as u32); + space.write_raw(fxstate + FP_SW, &sw) + && space.write_raw(fxstate + xstate_size, &FP_XSTATE_MAGIC2.to_le_bytes()) + } +} + /// `setup_rt_frame()`: the frame for the handler of `sig` on the guest stack, and the /// registers that enter it. `old` is the guest mask to return to. fn setup_rt_frame( @@ -488,23 +515,7 @@ fn setup_rt_frame( f[SC_CS..SC_CS + 2].copy_from_slice(&sel(cpu, R_CS).to_le_bytes()); f[SC_CS + 2..SC_SS].fill(0); f[SC_SS..SC_SS + 2].copy_from_slice(&sel(cpu, R_SS).to_le_bytes()); - let fp_ok = if xsave { - // xsave_sigcontext(): XSAVE adds to the header, so it starts out zero. - let xcr0 = env::ld64(cpu.env, env::XCR0); - let xstate_size = fpend - fpstate - 4; - space.write_raw(fpstate + 512, &[0u8; 64]) && cpu_x86_xsave(cpu, fpstate, xcr0).is_ok() && { - let mut sw = [0u8; 24]; - put32(&mut sw, 0, FP_XSTATE_MAGIC1); - put32(&mut sw, 4, (fpend - fpstate) as u32); - put64(&mut sw, 8, xcr0); - put32(&mut sw, 16, xstate_size as u32); - space.write_raw(fpstate + FP_SW, &sw) - && space.write_raw(fpstate + xstate_size, &FP_XSTATE_MAGIC2.to_le_bytes()) - } - } else { - cpu_x86_fxsave(cpu, fpstate).is_ok() && space.write_raw(fpstate + FP_SW, &[0u8; 4]) - }; - if !fp_ok { + if !save_fpstate(space, cpu, xsave, fpstate, fpstate, fpend) { cpu.core.exception_index = -1; signal::force_sigsegv(t, sig); return; @@ -538,7 +549,7 @@ fn setup_rt_frame( } /// `xrstor_sigcontext()` of the state at `fp`, false when the frame is bad. -fn restore_fpstate(space: &GuestSpace, cpu: &mut Cpu<'_>, fp: u64) -> bool { +pub(crate) fn restore_fpstate(space: &GuestSpace, cpu: &mut Cpu<'_>, fp: u64) -> bool { let xsave = uses_xsave(cpu); let math = fpstate_size(cpu, xsave); let mut img = vec![0u8; 512]; @@ -698,6 +709,7 @@ pub(crate) static GUEST: Guest = Guest { }, set_tls, cpu_loop, + setup_frame: None, setup_rt_frame, rt_sigreturn: do_rt_sigreturn, };