mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-10 22:00:22 +02:00
Which requires moving LinuxEmulation to its own independent folder as well. Since both IRLoader and FEXLoader rely on it. No functional change, just moves the the code around.
431 lines
12 KiB
C++
431 lines
12 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#pragma once
|
|
|
|
#include <FEXCore/Utils/CompilerDefs.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <signal.h>
|
|
|
|
namespace FEXCore {
|
|
namespace x86_64 {
|
|
// uc_flags flags
|
|
///< Has extended FP state
|
|
constexpr uint64_t UC_FP_XSTATE = (1ULL << 0);
|
|
///< Set when kernel saves SS register from 64bit code
|
|
constexpr uint64_t UC_SIGCONTEXT_SS = (1ULL << 1);
|
|
///< Set when kernel will strictly restore the SS
|
|
constexpr uint64_t UC_STRICT_RESTORE_SS = (1ULL << 2);
|
|
|
|
///< Describes the signal stack
|
|
struct FEX_PACKED stack_t {
|
|
void *ss_sp;
|
|
int32_t ss_flags;
|
|
uint32_t : 32;
|
|
size_t ss_size;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::stack_t) == 24, "This needs to be the right size");
|
|
|
|
/**
|
|
* Describes the software specific bytes added at the end of the
|
|
* fpstate to identify whether or not an extended context area is
|
|
* present and what kind of extended features are present in said
|
|
* context area.
|
|
*/
|
|
struct FEX_PACKED fpx_sw_bytes {
|
|
static constexpr uint32_t FP_XSTATE_MAGIC = 0x46505853;
|
|
|
|
enum FeatureFlag : uint32_t {
|
|
FEATURE_FP = 1U << 0,
|
|
FEATURE_SSE = 1U << 1,
|
|
FEATURE_YMM = 1U << 2,
|
|
FEATURE_BNDREGS = 1U << 3,
|
|
FEATURE_BNDCSR = 1U << 4,
|
|
FEATURE_OPMASK = 1U << 5,
|
|
FEATURE_ZMM_Hi256 = 1U << 6,
|
|
FEATURE_Hi16_ZMM = 1U << 7,
|
|
FEATURE_PT_UNIMPL = 1U << 8,
|
|
FEATURE_PKRU = 1U << 9,
|
|
FEATURE_PASID = 1U << 10,
|
|
FEATURE_RESERVED11 = 1U << 11,
|
|
FEATURE_RESERVED12 = 1U << 12,
|
|
FEATURE_RESERVED13 = 1U << 13,
|
|
FEATURE_RESERVED14 = 1U << 14,
|
|
FEATURE_LBR = 1U << 15,
|
|
FEATURE_RESERVED16 = 1U << 16,
|
|
FEATURE_XTILE_CFG = 1U << 17,
|
|
FEATURE_XTILE_DATA = 1U << 18,
|
|
};
|
|
|
|
bool HasExtendedContext() const {
|
|
return magic1 == FP_XSTATE_MAGIC;
|
|
}
|
|
|
|
bool HasYMMH() const {
|
|
return (xfeatures & FEATURE_YMM) != 0;
|
|
}
|
|
|
|
// If magic1 is set to FP_XSTATE_MAGIC, then the encompassing
|
|
// frame is an xstate frame. If 0, then it's a legacy frame.
|
|
uint32_t magic1;
|
|
|
|
// Total size of the fpstate area
|
|
// - magic1 = 0 -> sizeof(fpstate)
|
|
// - magic1 = FP_XSTATE_MAGIC -> sizeof(xstate) + extensions (if any)
|
|
uint32_t extended_size;
|
|
|
|
// Feature bitmask describing supported features.
|
|
uint64_t xfeatures;
|
|
|
|
// Actual XSAVE state size, based on above xfeatures
|
|
uint32_t xstate_size;
|
|
|
|
// Reserved data
|
|
uint32_t padding[7];
|
|
};
|
|
static_assert(sizeof(fpx_sw_bytes) == 48);
|
|
|
|
struct FEX_PACKED _libc_fpstate {
|
|
// This is in FXSAVE format
|
|
uint16_t fcw;
|
|
uint16_t fsw;
|
|
uint16_t ftw;
|
|
uint16_t fop;
|
|
uint64_t fip;
|
|
uint64_t fdp;
|
|
uint32_t mxcsr;
|
|
uint32_t mxcsr_mask;
|
|
__uint128_t _st[8];
|
|
__uint128_t _xmm[16];
|
|
uint32_t _res[12];
|
|
|
|
// Linux uses 12 of the bytes relegated for software purposes
|
|
// to store info describing any existing XSAVE context data.
|
|
fpx_sw_bytes sw_reserved;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::_libc_fpstate) == 512, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED xstate_header {
|
|
uint64_t xfeatures;
|
|
uint64_t reserved1[2];
|
|
uint64_t reserved2[5];
|
|
};
|
|
static_assert(sizeof(xstate_header) == 64);
|
|
|
|
struct FEX_PACKED ymmh_state {
|
|
__uint128_t ymmh_space[16];
|
|
};
|
|
static_assert(sizeof(ymmh_state) == 256);
|
|
|
|
/**
|
|
* Extended state that includes both the main fpstate
|
|
* and the extended state.
|
|
*/
|
|
struct FEX_PACKED xstate {
|
|
_libc_fpstate fpstate;
|
|
xstate_header xstate_hdr;
|
|
ymmh_state ymmh;
|
|
};
|
|
static_assert(sizeof(xstate) == 832);
|
|
|
|
///< The order of these must match the GNU ordering
|
|
enum ContextRegs {
|
|
FEX_REG_R8 = 0,
|
|
FEX_REG_R9,
|
|
FEX_REG_R10,
|
|
FEX_REG_R11,
|
|
FEX_REG_R12,
|
|
FEX_REG_R13,
|
|
FEX_REG_R14,
|
|
FEX_REG_R15,
|
|
FEX_REG_RDI,
|
|
FEX_REG_RSI,
|
|
FEX_REG_RBP,
|
|
FEX_REG_RBX,
|
|
FEX_REG_RDX,
|
|
FEX_REG_RAX,
|
|
FEX_REG_RCX,
|
|
FEX_REG_RSP,
|
|
FEX_REG_RIP,
|
|
FEX_REG_EFL,
|
|
FEX_REG_CSGSFS,
|
|
FEX_REG_ERR,
|
|
FEX_REG_TRAPNO,
|
|
FEX_REG_OLDMASK,
|
|
FEX_REG_CR2,
|
|
};
|
|
static_assert(FEX_REG_CR2 == 22, "Oops");
|
|
|
|
struct FEX_PACKED mcontext_t {
|
|
uint64_t gregs[23];
|
|
FEXCore::x86_64::_libc_fpstate *fpregs;
|
|
uint64_t __reserved[8];
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::mcontext_t) == 256, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED sigset_t {
|
|
uint64_t val[16];
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::sigset_t) == 128, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED ucontext_t {
|
|
uint64_t uc_flags;
|
|
FEXCore::x86_64::ucontext_t *uc_link;
|
|
FEXCore::x86_64::stack_t uc_stack;
|
|
FEXCore::x86_64::mcontext_t uc_mcontext;
|
|
FEXCore::x86_64::sigset_t uc_sigmask;
|
|
};
|
|
static_assert(offsetof(FEXCore::x86_64::ucontext_t, uc_mcontext) == 40, "Needs to be correct");
|
|
|
|
static_assert(sizeof(FEXCore::x86_64::ucontext_t) == 424, "This needs to be the right size");
|
|
}
|
|
|
|
namespace x86 {
|
|
// uc_flags flags
|
|
///< Has extended FP state
|
|
constexpr uint64_t UC_FP_XSTATE = (1ULL << 0);
|
|
|
|
///< The order of these must match the GNU ordering
|
|
enum ContextRegs {
|
|
FEX_REG_GS = 0,
|
|
FEX_REG_FS,
|
|
FEX_REG_ES,
|
|
FEX_REG_DS,
|
|
FEX_REG_RDI,
|
|
FEX_REG_RSI,
|
|
FEX_REG_RBP,
|
|
FEX_REG_RSP,
|
|
FEX_REG_RBX,
|
|
FEX_REG_RDX,
|
|
FEX_REG_RCX,
|
|
FEX_REG_RAX,
|
|
FEX_REG_TRAPNO,
|
|
FEX_REG_ERR,
|
|
FEX_REG_EIP,
|
|
FEX_REG_CS,
|
|
FEX_REG_EFL,
|
|
FEX_REG_UESP,
|
|
FEX_REG_SS
|
|
};
|
|
static_assert(FEX_REG_SS == 18, "Oops");
|
|
|
|
union sigval_t {
|
|
int sival_int;
|
|
uint32_t sival_ptr; // XXX: Should be compat_ptr<void>
|
|
};
|
|
|
|
struct FEX_PACKED siginfo_t {
|
|
int si_signo;
|
|
int si_errno;
|
|
int si_code;
|
|
union {
|
|
uint32_t pad[29];
|
|
/* tgkill siginfo_t */
|
|
struct {
|
|
int32_t pid;
|
|
int32_t uid;
|
|
} _kill;
|
|
/* SIGPOLL */
|
|
struct {
|
|
int32_t band;
|
|
int32_t fd;
|
|
} _poll;
|
|
/* SIGILL, SIGFPE, SIGSEGV, SIBUS */
|
|
struct {
|
|
uint32_t addr;
|
|
} _sigfault;
|
|
/* SIGCHLD */
|
|
struct {
|
|
int32_t pid;
|
|
int32_t uid;
|
|
int32_t status;
|
|
int32_t utime;
|
|
int32_t stime;
|
|
} _sigchld;
|
|
/* RT signals */
|
|
struct {
|
|
int32_t pid;
|
|
int32_t uid;
|
|
union {
|
|
int32_t sival_int;
|
|
uint32_t sival_ptr; // compat_ptr
|
|
} sigval;
|
|
} _rt;
|
|
/* SIGALRM, SIGVTALRM */
|
|
struct {
|
|
int tid;
|
|
int overrun;
|
|
FEXCore::x86::sigval_t sigval;
|
|
} _timer;
|
|
/* SIGSYS */
|
|
struct {
|
|
uint32_t call_addr; // compat_ptr
|
|
int32_t syscall;
|
|
uint32_t arch;
|
|
} _sigsys;
|
|
} _sifields;
|
|
|
|
union HostSigInfo_t {
|
|
// This anonymous struct needs to match the host definition
|
|
struct {
|
|
uint32_t si_signo;
|
|
uint32_t si_errno;
|
|
uint32_t si_code;
|
|
|
|
uint32_t __pad0;
|
|
|
|
// Pad[28] is a union for all the sifields
|
|
uint32_t _pad[28];
|
|
} FEXDef;
|
|
::siginfo_t host{};
|
|
};
|
|
static_assert(sizeof(HostSigInfo_t) == 128, "This needs to be the right size");
|
|
|
|
siginfo_t() = delete;
|
|
|
|
operator ::siginfo_t() const {
|
|
// The definition of siginfo_t changes depending on the host environment
|
|
// It is guaranteed to be 128 bytes and the kernel interface is the same for all of them
|
|
// Since we only run on Linux
|
|
HostSigInfo_t val{};
|
|
|
|
val.FEXDef.si_signo = si_signo;
|
|
val.FEXDef.si_errno = si_errno;
|
|
val.FEXDef.si_code = si_code;
|
|
|
|
// Host siginfo has a pad member that is set to zeros
|
|
val.FEXDef.__pad0 = 0;
|
|
|
|
// Copy over the union
|
|
// The union is different sizes on 64-bit versus 32-bit
|
|
memcpy(val.FEXDef._pad, _sifields.pad, std::min(sizeof(val.FEXDef._pad), sizeof(_sifields.pad)));
|
|
|
|
return val.host;
|
|
}
|
|
|
|
siginfo_t(::siginfo_t val) {
|
|
HostSigInfo_t host;
|
|
host.host = val;
|
|
|
|
si_signo = host.FEXDef.si_signo;
|
|
si_errno = host.FEXDef.si_errno;
|
|
si_code = host.FEXDef.si_code;
|
|
|
|
// Copy over the union
|
|
// The union is different sizes on 64-bit versus 32-bit
|
|
memcpy(_sifields.pad, host.FEXDef._pad, std::min(sizeof(host.FEXDef._pad), sizeof(_sifields.pad)));
|
|
}
|
|
static_assert(offsetof(::siginfo_t, si_signo) == offsetof(HostSigInfo_t, FEXDef.si_signo), "si_signo in wrong location?");
|
|
static_assert(offsetof(::siginfo_t, si_errno) == offsetof(HostSigInfo_t, FEXDef.si_errno), "si_errno in wrong location?");
|
|
static_assert(offsetof(::siginfo_t, si_code) == offsetof(HostSigInfo_t, FEXDef.si_code), "si_code in wrong location?");
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::siginfo_t) == 128, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED stack_t {
|
|
uint32_t ss_sp; // XXX: should be compat_ptr<void>
|
|
int ss_flags;
|
|
uint32_t ss_size;
|
|
};
|
|
|
|
static_assert(sizeof(FEXCore::x86::stack_t) == 12, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED mcontext_t {
|
|
uint32_t gregs[19];
|
|
uint32_t fpregs; // XXX: should be compat_ptr<FEXCore::x86::_libc_fpstate>
|
|
uint32_t oldmask;
|
|
uint32_t cr2;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::mcontext_t) == 88, "This needs to be the right size");
|
|
|
|
struct _libc_fpreg {
|
|
uint16_t significand[4];
|
|
uint16_t exponent;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::_libc_fpreg) == 10, "This needs to be the right size");
|
|
|
|
// Same layout on both x86 and x86_64
|
|
using fpx_sw_bytes = x86_64::fpx_sw_bytes;
|
|
using xstate_header = x86_64::xstate_header;
|
|
using ymmh_state = x86_64::ymmh_state;
|
|
|
|
enum fpstate_magic {
|
|
// Legacy fpstate
|
|
MAGIC_FPU = 0xFFFF'0000,
|
|
// Contains extended state information
|
|
MAGIC_XFPSTATE = 0x0,
|
|
};
|
|
struct FEX_PACKED _libc_fpstate {
|
|
uint32_t fcw;
|
|
uint32_t fsw;
|
|
uint32_t ftw;
|
|
uint32_t fop;
|
|
uint32_t cssel;
|
|
uint32_t dataoff;
|
|
uint32_t datasel;
|
|
FEXCore::x86::_libc_fpreg _st[8];
|
|
uint32_t status;
|
|
|
|
// Extended FPU data
|
|
uint32_t pad[6]; // Ignored FXSR data
|
|
uint32_t mxcsr;
|
|
uint32_t reserved;
|
|
__uint128_t _st_pad[8]; // Ignored st data
|
|
__uint128_t _xmm[8]; // First 8 XMM registers
|
|
uint32_t pad2[44]; // Second 8 XMM registers plus padding
|
|
fpx_sw_bytes sw_reserved; // extended state encoding
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::_libc_fpstate) == 624, "This needs to be the right size");
|
|
|
|
/**
|
|
* Extended state that includes both the main fpstate
|
|
* and the extended state.
|
|
*/
|
|
struct FEX_PACKED xstate {
|
|
_libc_fpstate fpstate;
|
|
xstate_header xstate_hdr;
|
|
ymmh_state ymmh;
|
|
};
|
|
static_assert(sizeof(xstate) == 944);
|
|
|
|
struct FEX_PACKED ucontext_t {
|
|
uint32_t uc_flags;
|
|
uint32_t uc_link; // XXX: should be a compat_ptr<FEXCore::x86::ucontext_t>
|
|
FEXCore::x86::stack_t uc_stack;
|
|
FEXCore::x86::mcontext_t uc_mcontext;
|
|
FEXCore::x86_64::sigset_t uc_sigmask; // This matches across architectures
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::ucontext_t) == 236, "This needs to be the right size");
|
|
|
|
///< Non-rt signal context.
|
|
//
|
|
// Needs to match the format expected from signal handlers without SA_SIGINFO set.
|
|
struct sigcontext {
|
|
uint32_t gs;
|
|
uint32_t fs;
|
|
uint32_t es;
|
|
uint32_t ds;
|
|
uint32_t di;
|
|
uint32_t si;
|
|
uint32_t bp;
|
|
uint32_t sp;
|
|
uint32_t bx;
|
|
uint32_t dx;
|
|
uint32_t cx;
|
|
uint32_t ax;
|
|
uint32_t trapno;
|
|
uint32_t err;
|
|
uint32_t ip;
|
|
uint32_t cs;
|
|
uint32_t flags;
|
|
uint32_t sp_at_signal;
|
|
uint32_t ss;
|
|
|
|
uint32_t fpstate;
|
|
uint32_t oldmask;
|
|
uint32_t cr2;
|
|
};
|
|
}
|
|
}
|