FEXCore: Accurately store segment descriptors

Previously we were only storing the 32-bit base address which isn't
actually how segment descriptors work.

In reality segment descriptors are 64-bit descriptors that are laid out
in a particular layout depending on the 4-bit type value. In reality we
only care about code and data segment layouts since the rest are
bonkers.

Describe these descriptors correctly and setup a default code descriptor
for the operating mode that FEX is starting in.
This commit is contained in:
Ryan Houdek committed 2025-07-29 12:02:37 -07:00
1 parent c0762bbd82
commit aa871c797b
4 files changed
+81 -24

No files matched your search

+16
View File
@@ -440,6 +440,22 @@ ContextImpl::CreateThread(uint64_t InitialRIP, uint64_t StackPointer, const FEXC
Thread->CurrentFrame->State.gregs[X86State::REG_RSP] = StackPointer;
Thread->CurrentFrame->State.rip = InitialRIP;
// Set up default code segment.
// Default code segment indexes match the numbers that the Linux kernel uses.
Thread->CurrentFrame->State.cs_idx = 6 << 3;
auto &GDT = Thread->CurrentFrame->State.gdt[Thread->CurrentFrame->State.cs_idx >> 3];
Thread->CurrentFrame->State.SetGDTBase(&GDT, 0);
Thread->CurrentFrame->State.SetGDTLimit(&GDT, 0xF'FFFFU);
if (Config.Is64BitMode) {
GDT.L = 1; // L = Long Mode = 64-bit
GDT.D = 0; // D = Default Operand SIze = Reserved
}
else {
GDT.L = 0; // L = Long Mode = 32-bit
GDT.D = 1; // D = Default Operand Size = 32-bit
}
// Copy over the new thread state to the new object
if (NewThreadState) {
memcpy(&Thread->CurrentFrame->State, NewThreadState, sizeof(FEXCore::Core::CPUState));
+42 -2
View File
@@ -121,9 +121,48 @@ struct CPUState {
uint64_t mm[8][2] {};
// 32bit x86 state
struct {
uint32_t base;
struct gdt_segment {
uint16_t Limit0;
uint16_t Base0;
uint16_t Base1 : 8;
uint16_t Type : 4;
uint16_t S : 1;
uint16_t DPL : 2;
uint16_t P : 1;
uint16_t Limit1 : 4;
uint16_t AVL : 1;
uint16_t L : 1;
uint16_t D : 1;
uint16_t G : 1;
uint16_t Base2 : 8;
} gdt[32] {};
static uint32_t CalculateGDTBase(gdt_segment GDT) {
uint32_t Base{};
Base |= GDT.Base2 << 24;
Base |= GDT.Base1 << 16;
Base |= GDT.Base0;
return Base;
}
static uint32_t CalculateGDTLimit(gdt_segment GDT) {
uint32_t Limit{};
Limit |= GDT.Limit1 << 16;
Limit |= GDT.Limit0;
return Limit;
}
static void SetGDTBase(gdt_segment *GDT, uint32_t Base) {
GDT->Base0 = Base;
GDT->Base1 = Base >> 16;
GDT->Base2 = Base >> 24;
}
static void SetGDTLimit(gdt_segment *GDT, uint32_t Limit) {
GDT->Limit0 = Limit;
GDT->Limit1 = Limit >> 16;
}
uint16_t FCW {0x37F};
uint8_t AbridgedFTW {};
@@ -135,6 +174,7 @@ struct CPUState {
static constexpr size_t FLAG_SIZE = sizeof(flags[0]);
static constexpr size_t GDT_SIZE = sizeof(gdt[0]);
static_assert(GDT_SIZE == sizeof(uint64_t), "Segments required to be 8-byte in size.");
static constexpr size_t GPR_REG_SIZE = sizeof(gregs[0]);
static constexpr size_t XMM_AVX_REG_SIZE = sizeof(xmm.avx.data[0]);
static constexpr size_t XMM_SSE_REG_SIZE = XMM_AVX_REG_SIZE / 2;