From ab14882761ce1e509af9240194fb2b394bf302de Mon Sep 17 00:00:00 2001 From: Simon Scherer Date: Mon, 27 Apr 2026 16:17:16 +0200 Subject: [PATCH 1/2] FEXCore: Fix 2byte stack access for 0x66 PUSH/POP FS/GS --- .../Source/Interface/Core/X86Tables/SecondaryTables.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp b/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp index eb19c8bf5..7a317c7c3 100644 --- a/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp +++ b/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp @@ -31,19 +31,19 @@ constexpr std::array Secondary_ArchSelect_LUT = {{ }, { {"PUSH FS", TYPE_INST, GenFlagsSrcSize(SIZE_16BIT) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX> } }, - {"PUSH FS", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX> } }, + {"PUSH FS", TYPE_INST, GenFlagsSameSize(SIZE_64BITDEF) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX> } }, }, { {"POP FS", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_DEF) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX> } }, - {"POP FS", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_64BIT) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX> } }, + {"POP FS", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_64BITDEF) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX> } }, }, { {"PUSH GS", TYPE_INST, GenFlagsSrcSize(SIZE_16BIT) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX> } }, - {"PUSH GS", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX> } }, + {"PUSH GS", TYPE_INST, GenFlagsSameSize(SIZE_64BITDEF) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX> } }, }, { {"POP GS", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_DEF) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX> } }, - {"POP GS", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_64BIT) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX> } }, + {"POP GS", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_64BITDEF) | FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .OpDispatch = &IR::OpDispatchBuilder::Bind<&IR::OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX> } }, }, }}; From 34b3adc23df98aad0d619d5ee13e129ebe37824b Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 27 Apr 2026 15:21:47 -0700 Subject: [PATCH 2/2] unittests/ASM: Adds unit test to ensure push/pop segment of o16 works Only ensures we are pushing and popping the correct size, not any of the selector data within it, as 64-bit systems with the FSGSBase extension don't use them selectors anyway. Can't test the 32-bit side currently because we would corrupt FS/GS in CI and the host testharnessrunner can't fix that right now. --- unittests/ASM/FEX_bugs/o16_segmentpushpop.asm | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 unittests/ASM/FEX_bugs/o16_segmentpushpop.asm diff --git a/unittests/ASM/FEX_bugs/o16_segmentpushpop.asm b/unittests/ASM/FEX_bugs/o16_segmentpushpop.asm new file mode 100644 index 000000000..4dac54477 --- /dev/null +++ b/unittests/ASM/FEX_bugs/o16_segmentpushpop.asm @@ -0,0 +1,78 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1" + } +} +%endif + +; FEX-Emu had a bug where segment registers prefixed with `o16` were still pushing and popping a full 64-bits to the stack. +; With o16 it is supposed to only operate on 16-bits. Run through the instructions and just ensure it is pushing/popping the correct amount. + +; Need to save and restore fs/gs base on this test. +rdfsbase rax +mov [rel .data_segment], rax + +rdgsbase rax +mov [rel .data_segment + 8], rax + +mov rax, 0 +mov rbx, 0xe000_0ffe +mov rcx, 0xe000_1000 +mov rsp, 0xe000_1000 + +; Ensure 16-bit FS/GS push/pops are the correct size. +o16 push fs +cmp rsp, rbx +jne .bad + +o16 pop fs +cmp rsp, rcx +jne .bad + +o16 push gs +cmp rsp, rbx +jne .bad + +o16 pop gs +cmp rsp, rcx +jne .bad + +; Check 64-bit as well. +mov rbx, 0xe000_0ff8 + +push fs +cmp rsp, rbx +jne .bad + +pop fs +cmp rsp, rcx +jne .bad + +push gs +cmp rsp, rbx +jne .bad + +pop gs +cmp rsp, rcx +jne .bad + +mov rax, 1 +jmp .end + +.bad: +mov rax, 0 + +.end: +mov rbx, [rel .data_segment] +wrfsbase rbx + +mov rbx, [rel .data_segment + 8] +wrgsbase rbx + +hlt + +align 4096 +.data_segment: +dq 0 +dq 0