From d3d48017e7bf92ea757bcd7b015a04175b4dab8d Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 24 Jan 2026 22:58:15 -0700 Subject: [PATCH] Support small TLS directories & implement GlobalMemoryStatus --- dll/kernel32/winbase.cpp | 119 +++++++++++++++++++++++++++++++++++++++ dll/kernel32/winbase.h | 14 +++++ src/modules.cpp | 27 +++++---- 3 files changed, 150 insertions(+), 10 deletions(-) diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index 947392a..91db64c 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -24,6 +24,12 @@ #include #include #include +#if defined(__APPLE__) +#include +#include +#elif defined(__linux__) +#include +#endif #include #include #include @@ -42,6 +48,71 @@ constexpr ATOM kMaxIntegerAtom = 0xBFFF; constexpr ATOM kMinStringAtom = 0xC000; constexpr ATOM kMaxStringAtom = 0xFFFF; +SIZE_T clampToSizeT(uint64_t value) { + constexpr uint64_t kMaxSizeT = static_cast(std::numeric_limits::max()); + return value > kMaxSizeT ? static_cast(kMaxSizeT) : static_cast(value); +} + +struct MemorySnapshot { + uint64_t totalPhys = 0; + uint64_t availPhys = 0; + uint64_t totalPageFile = 0; + uint64_t availPageFile = 0; +}; + +bool queryHostMemory(MemorySnapshot &out) { +#if defined(__linux__) + struct sysinfo info {}; + if (sysinfo(&info) != 0) { + return false; + } + const uint64_t unit = info.mem_unit ? static_cast(info.mem_unit) : 1; + out.totalPhys = static_cast(info.totalram) * unit; + out.availPhys = static_cast(info.freeram) * unit; + out.totalPageFile = (static_cast(info.totalram) + static_cast(info.totalswap)) * unit; + out.availPageFile = (static_cast(info.freeram) + static_cast(info.freeswap)) * unit; + if (info.bufferram > 0) { + uint64_t buffers = static_cast(info.bufferram) * unit; + out.availPhys += buffers; + out.availPageFile += buffers; + } + return true; +#elif defined(__APPLE__) + uint64_t totalPhys = 0; + size_t totalPhysSize = sizeof(totalPhys); + if (sysctlbyname("hw.memsize", &totalPhys, &totalPhysSize, nullptr, 0) != 0) { + return false; + } + vm_size_t pageSize = 0; + if (host_page_size(mach_host_self(), &pageSize) != KERN_SUCCESS || pageSize == 0) { + return false; + } + vm_statistics64_data_t vmstat {}; + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + if (host_statistics64(mach_host_self(), HOST_VM_INFO64, reinterpret_cast(&vmstat), &count) != + KERN_SUCCESS) { + return false; + } + uint64_t freePages = static_cast(vmstat.free_count) + static_cast(vmstat.inactive_count) + + static_cast(vmstat.speculative_count); + out.totalPhys = totalPhys; + out.availPhys = freePages * static_cast(pageSize); + + struct xsw_usage swap {}; + size_t swapSize = sizeof(swap); + if (sysctlbyname("vm.swapusage", &swap, &swapSize, nullptr, 0) == 0 && swapSize == sizeof(swap)) { + out.totalPageFile = swap.xsu_total; + out.availPageFile = swap.xsu_avail; + } else { + out.totalPageFile = totalPhys; + out.availPageFile = out.availPhys; + } + return true; +#else + return false; +#endif +} + struct AtomData { uint16_t refCount = 0; std::string original; @@ -780,6 +851,54 @@ UINT WINAPI GlobalFlags(HGLOBAL hMem) { return 0; } +void WINAPI GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("GlobalMemoryStatus(%p)\n", lpBuffer); + if (!lpBuffer) { + return; + } + + std::memset(lpBuffer, 0, sizeof(*lpBuffer)); + lpBuffer->dwLength = sizeof(*lpBuffer); + + MemorySnapshot snapshot; + if (!queryHostMemory(snapshot)) { + return; + } + + uint64_t totalPhys = snapshot.totalPhys; + uint64_t availPhys = snapshot.availPhys; + uint64_t totalPageFile = snapshot.totalPageFile; + uint64_t availPageFile = snapshot.availPageFile; + + constexpr uint64_t kMinAppAddr = 0x00010000ULL; + constexpr uint64_t kMaxAppAddr = 0x7FFEFFFFULL; + uint64_t totalVirtual = kMaxAppAddr - kMinAppAddr + 1; + uint64_t availVirtual = totalVirtual; + + constexpr uint64_t kMaxLegacy = static_cast(std::numeric_limits::max()); + totalPhys = std::min(totalPhys, kMaxLegacy); + availPhys = std::min(availPhys, kMaxLegacy); + totalVirtual = std::min(totalVirtual, kMaxLegacy); + availVirtual = std::min(availVirtual, kMaxLegacy); + availPhys = std::min(availPhys, totalPhys); + availPageFile = std::min(availPageFile, totalPageFile); + availVirtual = std::min(availVirtual, totalVirtual); + + if (totalPhys > 0) { + uint64_t used = totalPhys > availPhys ? totalPhys - availPhys : 0; + uint64_t load = (used * 100) / totalPhys; + lpBuffer->dwMemoryLoad = static_cast(std::min(load, 100)); + } + + lpBuffer->dwTotalPhys = clampToSizeT(totalPhys); + lpBuffer->dwAvailPhys = clampToSizeT(availPhys); + lpBuffer->dwTotalPageFile = clampToSizeT(totalPageFile); + lpBuffer->dwAvailPageFile = clampToSizeT(availPageFile); + lpBuffer->dwTotalVirtual = clampToSizeT(totalVirtual); + lpBuffer->dwAvailVirtual = clampToSizeT(availVirtual); +} + HLOCAL WINAPI LocalAlloc(UINT uFlags, SIZE_T uBytes) { HOST_CONTEXT_GUARD(); VERBOSE_LOG("LocalAlloc(%x, %zu)\n", uFlags, static_cast(uBytes)); diff --git a/dll/kernel32/winbase.h b/dll/kernel32/winbase.h index 6cf1bab..0803910 100644 --- a/dll/kernel32/winbase.h +++ b/dll/kernel32/winbase.h @@ -57,6 +57,19 @@ struct ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT { ULONG Offset; }; +struct MEMORYSTATUS { + DWORD dwLength; + DWORD dwMemoryLoad; + SIZE_T dwTotalPhys; + SIZE_T dwAvailPhys; + SIZE_T dwTotalPageFile; + SIZE_T dwAvailPageFile; + SIZE_T dwTotalVirtual; + SIZE_T dwAvailVirtual; +}; + +using LPMEMORYSTATUS = MEMORYSTATUS *; + namespace kernel32 { BOOL WINAPI IsBadReadPtr(LPCVOID lp, UINT_PTR ucb); @@ -86,6 +99,7 @@ HGLOBAL WINAPI GlobalAlloc(UINT uFlags, SIZE_T dwBytes); HGLOBAL WINAPI GlobalFree(HGLOBAL hMem); HGLOBAL WINAPI GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags); UINT WINAPI GlobalFlags(HGLOBAL hMem); +void WINAPI GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer); HLOCAL WINAPI LocalAlloc(UINT uFlags, SIZE_T uBytes); HLOCAL WINAPI LocalFree(HLOCAL hMem); diff --git a/src/modules.cpp b/src/modules.cpp index 67622d8..5f15978 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -281,6 +282,8 @@ struct ImageTlsDirectory32 { uint32_t Characteristics; }; +constexpr size_t kMinTlsDirectorySize = offsetof(ImageTlsDirectory32, SizeOfZeroFill); + uintptr_t resolveModuleAddress(const wibo::Executable &exec, uintptr_t address) { if (address == 0) { return 0; @@ -918,24 +921,28 @@ bool initializeModuleTls(ModuleInfo &module) { return true; } Executable &exec = *module.executable; - if (exec.tlsDirectoryRVA == 0 || exec.tlsDirectorySize < sizeof(ImageTlsDirectory32)) { + if (exec.tlsDirectoryRVA == 0 || exec.tlsDirectorySize < kMinTlsDirectorySize) { return true; } - auto tlsDirectory = exec.fromRVA(exec.tlsDirectoryRVA); - if (!tlsDirectory) { + auto *tlsDirectoryRaw = exec.fromRVA(exec.tlsDirectoryRVA); + if (!tlsDirectoryRaw) { return false; } + ImageTlsDirectory32 tlsDirectory{}; + // TLS directory may be smaller than ImageTlsDirectory32; remaining fields are zero-initialized + size_t copySize = std::min(exec.tlsDirectorySize, sizeof(tlsDirectory)); + std::memcpy(&tlsDirectory, tlsDirectoryRaw, copySize); auto &info = module.tlsInfo; - info.templateSize = (tlsDirectory->EndAddressOfRawData > tlsDirectory->StartAddressOfRawData) - ? tlsDirectory->EndAddressOfRawData - tlsDirectory->StartAddressOfRawData + info.templateSize = (tlsDirectory.EndAddressOfRawData > tlsDirectory.StartAddressOfRawData) + ? tlsDirectory.EndAddressOfRawData - tlsDirectory.StartAddressOfRawData : 0; - info.zeroFillSize = tlsDirectory->SizeOfZeroFill; - info.characteristics = tlsDirectory->Characteristics; - info.templateData = reinterpret_cast(resolveModuleAddress(exec, tlsDirectory->StartAddressOfRawData)); - info.indexLocation = reinterpret_cast(resolveModuleAddress(exec, tlsDirectory->AddressOfIndex)); + info.zeroFillSize = tlsDirectory.SizeOfZeroFill; + info.characteristics = tlsDirectory.Characteristics; + info.templateData = reinterpret_cast(resolveModuleAddress(exec, tlsDirectory.StartAddressOfRawData)); + info.indexLocation = reinterpret_cast(resolveModuleAddress(exec, tlsDirectory.AddressOfIndex)); info.callbacks.clear(); - uintptr_t callbacksArray = resolveModuleAddress(exec, tlsDirectory->AddressOfCallBacks); + uintptr_t callbacksArray = resolveModuleAddress(exec, tlsDirectory.AddressOfCallBacks); if (callbacksArray) { auto callbackPtr = reinterpret_cast(callbacksArray); while (callbackPtr && *callbackPtr) {