diff --git a/CMakeLists.txt b/CMakeLists.txt index 250494c..7deb182 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -495,6 +495,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_tls SOURCES test/test_tls.c) wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c) wibo_add_fixture_bin(NAME test_fls SOURCES test/test_fls.c) + wibo_add_fixture_bin(NAME test_fpu_state SOURCES test/test_fpu_state.c) wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c) wibo_add_fixture_bin(NAME test_findfile SOURCES test/test_findfile.c) wibo_add_fixture_bin(NAME test_getfileattributesex SOURCES test/test_getfileattributesex.c) diff --git a/src/macros.S b/src/macros.S index d8d8613..ef7ac45 100644 --- a/src/macros.S +++ b/src/macros.S @@ -31,6 +31,12 @@ .intel_syntax noprefix +#ifdef __x86_64__ +#define SP rsp +#else +#define SP esp +#endif + #ifdef __x86_64__ .macro LJMP32 teb_reg diff --git a/src/main.cpp b/src/main.cpp index bccce60..4964da9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -86,7 +86,11 @@ bool wibo::installTibForCurrentThread(TEB *tibPtr) { return false; } currentThreadTeb = tibPtr; - return tebThreadSetup(tibPtr); + if (!tebThreadSetup(tibPtr)) { + return false; + } + initFpState(); + return true; } void wibo::uninstallTebForCurrentThread() { diff --git a/src/setup.S b/src/setup.S index addc8da..e7b77a9 100644 --- a/src/setup.S +++ b/src/setup.S @@ -6,6 +6,19 @@ .no_dead_strip _wibo_reserve #endif +# void initFpState() +# Configures 53-bit x87 precision and default SSE control state +ASM_GLOBAL(initFpState, @function) + sub SP, 8 + mov word ptr [SP], 0x027f + mov dword ptr [SP+4], 0x1f80 + fninit + fldcw word ptr [SP] + ldmxcsr dword ptr [SP+4] + add SP, 8 + ret +ASM_END(initFpState) + #if defined(__x86_64__) && defined(__linux__) # void installSelectors(TEB *teb) diff --git a/src/setup.h b/src/setup.h index bf4c4dc..df8d880 100644 --- a/src/setup.h +++ b/src/setup.h @@ -10,6 +10,7 @@ extern "C" { bool tebThreadSetup(TEB *teb); bool tebThreadTeardown(TEB *teb); +void initFpState(); #ifdef __cplusplus } diff --git a/test/test_fpu_state.c b/test/test_fpu_state.c new file mode 100644 index 0000000..8a2c314 --- /dev/null +++ b/test/test_fpu_state.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "test_assert.h" + +static uint16_t g_x87ControlWord; +static uint32_t g_mxcsr; +static uint16_t g_threadX87ControlWord; +static uint32_t g_threadMxcsr; + +static void NTAPI capture_fpu_state(PVOID module, DWORD reason, PVOID reserved) { + (void)module; + (void)reserved; + if (reason != DLL_PROCESS_ATTACH) { + return; + } + __asm__ volatile("fnstcw %0" : "=m"(g_x87ControlWord)); + __asm__ volatile("stmxcsr %0" : "=m"(g_mxcsr)); +} + +PIMAGE_TLS_CALLBACK g_fpuStateCallback __attribute__((section(".CRT$XLB"), used)) = capture_fpu_state; + +static DWORD WINAPI capture_thread_fpu_state(LPVOID param) { + (void)param; + __asm__ volatile("fnstcw %0" : "=m"(g_threadX87ControlWord)); + __asm__ volatile("stmxcsr %0" : "=m"(g_threadMxcsr)); + return 0; +} + +int main(void) { + TEST_CHECK_EQ(0x027f, g_x87ControlWord); + // Exception status flags may be raised before the process-attach callback runs. + TEST_CHECK_EQ(0x1f80, g_mxcsr & ~0x3f); + + HANDLE thread = CreateThread(NULL, 0, capture_thread_fpu_state, NULL, 0, NULL); + TEST_CHECK(thread != NULL); + TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(thread, INFINITE)); + TEST_CHECK(CloseHandle(thread)); + TEST_CHECK_EQ(0x027f, g_threadX87ControlWord); + TEST_CHECK_EQ(0x1f80, g_threadMxcsr & ~0x3f); + return EXIT_SUCCESS; +}