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

@@ -199,12 +199,12 @@ void SignalDelegator::RestoreFrame_ia32(FEXCore::Core::InternalThreadState* Thre
Frame->State.gs_idx = guest_uctx->sc.gs;
Frame->State.ss_idx = guest_uctx->sc.ss;
Frame->State.cs_cached = Frame->State.gdt[Frame->State.cs_idx >> 3].base;
Frame->State.ds_cached = Frame->State.gdt[Frame->State.ds_idx >> 3].base;
Frame->State.es_cached = Frame->State.gdt[Frame->State.es_idx >> 3].base;
Frame->State.fs_cached = Frame->State.gdt[Frame->State.fs_idx >> 3].base;
Frame->State.gs_cached = Frame->State.gdt[Frame->State.gs_idx >> 3].base;
Frame->State.ss_cached = Frame->State.gdt[Frame->State.ss_idx >> 3].base;
Frame->State.cs_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.cs_idx >> 3]);
Frame->State.ds_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.ds_idx >> 3]);
Frame->State.es_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.es_idx >> 3]);
Frame->State.fs_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.fs_idx >> 3]);
Frame->State.gs_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.gs_idx >> 3]);
Frame->State.ss_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.ss_idx >> 3]);
#define COPY_REG(x, y) Frame->State.gregs[FEXCore::X86State::REG_##x] = guest_uctx->sc.y;
COPY_REG(RDI, di);
@@ -272,12 +272,12 @@ void SignalDelegator::RestoreRTFrame_ia32(FEXCore::Core::InternalThreadState* Th
Frame->State.gs_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_GS];
Frame->State.ss_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_SS];
Frame->State.cs_cached = Frame->State.gdt[Frame->State.cs_idx >> 3].base;
Frame->State.ds_cached = Frame->State.gdt[Frame->State.ds_idx >> 3].base;
Frame->State.es_cached = Frame->State.gdt[Frame->State.es_idx >> 3].base;
Frame->State.fs_cached = Frame->State.gdt[Frame->State.fs_idx >> 3].base;
Frame->State.gs_cached = Frame->State.gdt[Frame->State.gs_idx >> 3].base;
Frame->State.ss_cached = Frame->State.gdt[Frame->State.ss_idx >> 3].base;
Frame->State.cs_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.cs_idx >> 3]);
Frame->State.ds_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.ds_idx >> 3]);
Frame->State.es_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.es_idx >> 3]);
Frame->State.fs_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.fs_idx >> 3]);
Frame->State.gs_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.gs_idx >> 3]);
Frame->State.ss_cached = Frame->State.CalculateGDTBase(Frame->State.gdt[Frame->State.ss_idx >> 3]);
#define COPY_REG(x) Frame->State.gregs[FEXCore::X86State::REG_##x] = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_##x];
COPY_REG(RDI);
@@ -54,8 +54,8 @@ uint64_t SetThreadArea(FEXCore::Core::CpuStateFrame* Frame, void* tls) {
if (u_info->entry_number == -1) {
for (uint32_t i = TLS_NextEntry; i < TLS_MaxEntry; ++i) {
auto GDT = &Frame->State.gdt[i];
if (GDT->base == 0) {
// If the base is zero then it isn't present with our setup
if (Frame->State.CalculateGDTLimit(*GDT) == 0) {
// If the limit is zero then it isn't present with our setup
u_info->entry_number = i;
break;
}
@@ -69,29 +69,30 @@ uint64_t SetThreadArea(FEXCore::Core::CpuStateFrame* Frame, void* tls) {
// Now we need to update the thread's GDT to handle this change
auto GDT = &Frame->State.gdt[u_info->entry_number];
GDT->base = u_info->base_addr;
Frame->State.SetGDTBase(GDT, u_info->base_addr);
Frame->State.SetGDTLimit(GDT, 0xF'FFFFU);
// With the segment register optimization we need to check all of the segment registers and update.
const auto GetEntry = [](auto value) {
return value >> 3;
};
if (GetEntry(Frame->State.cs_idx) == u_info->entry_number) {
Frame->State.cs_cached = GDT->base;
Frame->State.cs_cached = Frame->State.CalculateGDTBase(*GDT);
}
if (GetEntry(Frame->State.ds_idx) == u_info->entry_number) {
Frame->State.ds_cached = GDT->base;
Frame->State.ds_cached = Frame->State.CalculateGDTBase(*GDT);
}
if (GetEntry(Frame->State.es_idx) == u_info->entry_number) {
Frame->State.es_cached = GDT->base;
Frame->State.es_cached = Frame->State.CalculateGDTBase(*GDT);
}
if (GetEntry(Frame->State.fs_idx) == u_info->entry_number) {
Frame->State.fs_cached = GDT->base;
Frame->State.fs_cached = Frame->State.CalculateGDTBase(*GDT);
}
if (GetEntry(Frame->State.gs_idx) == u_info->entry_number) {
Frame->State.gs_cached = GDT->base;
Frame->State.gs_cached = Frame->State.CalculateGDTBase(*GDT);
}
if (GetEntry(Frame->State.ss_idx) == u_info->entry_number) {
Frame->State.ss_cached = GDT->base;
Frame->State.ss_cached = Frame->State.CalculateGDTBase(*GDT);
}
return 0;
}
@@ -164,7 +165,7 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
memset(u_info, 0, sizeof(*u_info));
// FEX only stores base instead of the full GDT
u_info->base_addr = GDT->base;
u_info->base_addr = Frame->State.CalculateGDTBase(*GDT);
// Fill the rest of the structure with expected data (even if wrong at the moment)
if (u_info->base_addr) {