Windows/UnixLib: Adds remaining helpers

Centralizes all the nasty behaviour that will end up breaking when WINE
eventually turns on userspace syscall dispatch. Pushes all of the logic
in to the UnixLib. Support both paths until everyone is migrated to
supporting the UnixLib, then we can delete the bit of code duplication
between the PE side and UnixLib side.

Helpful that everything that gets punched through the UnixLib is
optional, so worst case some optional bits can break for a while.
This commit is contained in:
Ryan Houdek committed 2026-06-28 12:58:24 -07:00
1 parent 24da43f823
commit 954581c750
9 files changed
+389 -293

No files matched your search

+5 -219
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
#include <FEXCore/Utils/AllocatorHooks.h>
#include <FEXCore/Utils/TypeDefines.h>
#include "Windows/Common/FEXUnixLib.h"
#include <array>
#include <chrono>
@@ -13,228 +14,13 @@
#include <wine/debug.h>
namespace FEX::Windows::Allocator {
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#define MADV_HUGEPAGE 14
#define MADV_NOHUGEPAGE 15
namespace Trampoline {
struct madvise_data {
const void* addr;
size_t size;
int advise;
};
struct prctl_data {
int op;
uint64_t attr;
const void* addr;
size_t size;
const char* name;
uint64_t ret;
};
__attribute__((naked)) uint64_t wine_prctl(prctl_data* d) {
asm volatile(
R"(
.globl wine_prctl_begin
wine_prctl_begin:
mov x19, x0;
mov x8, 167; // prctl
ldr x0, [x19]; // op
ldp x1, x2, [x19, %[attr_offset]]; // {attr, addr}
ldp x3, x4, [x19, %[size_offset]]; // {size, name}
svc #0;
str x0, [x19, %[ret_offset]];
// Tell wine it was all groovy.
mov x0, 0;
ret;
.globl wine_prctl_end
wine_prctl_end:
)"
:
: [attr_offset] "i"(offsetof(prctl_data, attr)), [size_offset] "i"(offsetof(prctl_data, size)), [ret_offset] "i"(offsetof(prctl_data, ret))
: "memory");
};
__attribute__((naked)) uint64_t wine_madvise(madvise_data* d) {
asm volatile(R"(
.globl wine_madvise_begin
wine_madvise_begin:
mov x8, 233; // madvise
ldr x2, [x0, %[advise_offset]]; // advise
ldp x0, x1, [x0]; // {addr, size}
svc #0;
// Tell wine it was all groovy.
mov x0, 0;
ret;
.globl wine_madvise_end
wine_madvise_end:
)" ::[advise_offset] "i"(offsetof(madvise_data, advise))
: "memory");
}
extern "C" uint64_t wine_madvise_begin;
extern "C" uint64_t wine_madvise_end;
void* const wine_madvise_begin_loc = &wine_madvise_begin;
void* const wine_madvise_end_loc = &wine_madvise_end;
extern "C" uint64_t wine_prctl_begin;
extern "C" uint64_t wine_prctl_end;
void* const wine_prctl_begin_loc = &wine_prctl_begin;
void* const wine_prctl_end_loc = &wine_prctl_end;
extern NTSTATUS(WINAPI* __wine_unix_call_dispatcher)(uint64_t, unsigned int, void*);
decltype(__wine_unix_call_dispatcher) WineUnixCall;
enum unix_function_indexes {
INDEX_PRCTL = 0,
INDEX_MADVISE = 1,
INDEX_MAX,
};
static std::array<void*, INDEX_MAX> unix_functions {};
static uint64_t wine_prctl_trampoline(int op, uint64_t attr, const void* addr, size_t size, const char* name) {
prctl_data d {
.op = op,
.attr = attr,
.addr = addr,
.size = size,
.name = name,
};
WineUnixCall(reinterpret_cast<uint64_t>(unix_functions.data()), INDEX_PRCTL, &d);
return d.ret;
}
static uint64_t wine_madvise_trampoline(const void* addr, size_t size, int advice) {
madvise_data d {
.addr = addr,
.size = size,
.advise = advice,
};
WineUnixCall(reinterpret_cast<uint64_t>(unix_functions.data()), INDEX_MADVISE, &d);
return 0;
}
void VirtualName(const char* Name, const void* Ptr, size_t Size) {
static bool Supports {true};
if (Supports) {
auto Result = wine_prctl_trampoline(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name);
if (Result != 0) {
// Disable any additional attempts.
Supports = false;
}
}
}
void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) {
wine_madvise_trampoline(Ptr, Size, Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE);
}
} // namespace Trampoline
// This code path will eventually crash once Wine and the kernel implements `userspace syscall dispatch`.
// FEX will need to switch over to using WINE's unixlib syscall approach then.
// See `SetupHooks` for why unixlib doesn't work today.
namespace Illegal {
__attribute__((naked)) uint64_t prctl(int op, uint64_t attr, const void* addr, size_t size, const char* Name) {
asm volatile(R"(
mov x8, 167; // prctl
svc #0;
ret;
)" ::
: "memory");
}
__attribute__((naked)) uint64_t madvise(const void* addr, size_t size, int advice) {
asm volatile(R"(
mov x8, 233; // madvise
svc #0;
ret;
)" ::
: "memory");
}
void VirtualName(const char* Name, const void* Ptr, size_t Size) {
static bool Supports {true};
if (Supports) {
auto Result = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name);
if (Result != 0) {
// Disable any additional attempts.
Supports = false;
}
}
}
void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) {
madvise(Ptr, Size, Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE);
}
} // namespace Illegal
void SetupHooks(HMODULE ntdll) {
// If this symbol doesn't exist, then we aren't running under WINE.
const auto Sym = GetProcAddress(ntdll, "__wine_unix_call_dispatcher");
if (!Sym) {
return;
}
FEXCore::Allocator::HookPtrs Ptrs {};
// Wine will soon require us to use unixlib for calling helper routines that use Linux syscalls.
// It needs this for `userspace syscall dispatch` to capture rogue applications doing raw Windows syscalls.
// If FEX doesn't use unixlib, then when WINE and a kernel implements this, then our "illegal" path will start crashing.
//
// This code currently conflicts with FEX's `Call Checker` since the latter captures uses of `unixlib`.
// We hence have to stick to the `Illegal::` functions until a workaround is implemented.
if constexpr (false) {
// NTSTATUS __wine_unix_call_dispatcher( unixlib_handle_t, unsigned int, void * );
// - unixlib_handle_t is just an array of functions
// - uint32_t is just an index in to that
// - void* is the user provided pointer, gets loaded in to x0 in for the unix_function called.
// - Return value - SUCCESS or other error.
Trampoline::WineUnixCall = *reinterpret_cast<decltype(Trampoline::WineUnixCall)*>(Sym);
// This code must be copied over to allocated memory from top down allocations apparently.
auto Code = reinterpret_cast<uint8_t*>(
::VirtualAlloc(nullptr, FEXCore::Utils::FEX_PAGE_SIZE, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE));
if (!Code) {
return;
}
size_t CurrentOffset {};
const size_t prctl_size =
reinterpret_cast<uintptr_t>(Trampoline::wine_prctl_end_loc) - reinterpret_cast<uintptr_t>(Trampoline::wine_prctl_begin_loc);
const size_t madvise_size =
reinterpret_cast<uintptr_t>(Trampoline::wine_madvise_end_loc) - reinterpret_cast<uintptr_t>(Trampoline::wine_madvise_begin_loc);
// Copy prctl.
memcpy(Code + CurrentOffset, Trampoline::wine_prctl_begin_loc, prctl_size);
Trampoline::unix_functions[Trampoline::INDEX_PRCTL] = Code + CurrentOffset;
CurrentOffset += prctl_size;
// Copy madvise.
memcpy(Code + CurrentOffset, Trampoline::wine_madvise_begin_loc, madvise_size);
Trampoline::unix_functions[Trampoline::INDEX_MADVISE] = Code + CurrentOffset;
// Protect the page now.
FEXCore::Allocator::VirtualProtect(Code, FEXCore::Utils::FEX_PAGE_SIZE,
FEXCore::Allocator::ProtectOptions::Read | FEXCore::Allocator::ProtectOptions::Exec);
Ptrs = {
.VirtualName = Trampoline::VirtualName,
.VirtualTHPControl = Trampoline::VirtualTHPControl,
};
} else {
Ptrs = {
.VirtualName = Illegal::VirtualName,
.VirtualTHPControl = Illegal::VirtualTHPControl,
};
}
Ptrs = {
.VirtualName = UnixLib::VirtualName,
.VirtualTHPControl = UnixLib::VirtualTHPControl,
};
SYSTEM_INFO system_info {};
GetSystemInfo(&system_info);
+187
View File
@@ -1,16 +1,26 @@
// SPDX-License-Identifier: MIT
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/Utils/LogManager.h>
#include <cstdint>
#include <windef.h>
#include <winbase.h>
#define __WINESRC__
#include <winternl.h>
#include <libloaderapi.h>
#include "FEXUnixLib.h"
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#define MADV_HUGEPAGE 14
#define MADV_NOHUGEPAGE 15
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace FEX::Windows::UnixLib {
static bool UsingNTQueryPath {};
static bool SupportsVirtualName {true};
unixlib_handle_t UnixLibHandle {};
decltype(__wine_unix_call_dispatcher) UnixCallDispatcher {};
@@ -30,6 +40,50 @@ static NTSTATUS __attribute__((naked)) TrampolineCall(unixlib_handle_t, unsigned
: "memory");
}
#endif
// This code path will eventually crash once Wine and the kernel implements `userspace syscall dispatch`.
// FEX will need to switch over to using WINE's unixlib syscall approach then.
namespace Illegal {
__attribute__((naked)) uint64_t prctl(int op, uint64_t attr, const void* addr, size_t size, const char* Name) {
asm volatile(R"(
mov x8, 167; // prctl
svc #0;
ret;
)" ::
: "memory");
}
__attribute__((naked)) uint64_t madvise(const void* addr, size_t size, int advice) {
asm volatile(R"(
mov x8, 233; // madvise
svc #0;
ret;
)" ::
: "memory");
}
__attribute__((naked)) uint64_t linux_getpid() {
asm volatile(R"(
mov x8, 172;
svc #0;
ret;
)" ::
: "r0", "r8");
}
void VirtualName(const char* Name, const void* Ptr, size_t Size) {
if (SupportsVirtualName) {
auto Result = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name);
if (Result != 0) {
// Disable any additional attempts.
SupportsVirtualName = false;
}
}
}
void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) {
madvise(Ptr, Size, Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE);
}
} // namespace Illegal
bool Init(HMODULE NtDll) {
const auto Sym = GetProcAddress(NtDll, "__wine_unix_call_dispatcher");
@@ -99,4 +153,137 @@ bool TryEnableHardwareTSO() {
return Status == STATUS_SUCCESS;
}
bool SetKernelUnalignedAtomicControl(uint64_t Flags) {
if (UnixLibAvailable()) {
// UnixLib path.
FEXUnixLib_SetKernelUnalignedAtomicControl Args {
.Flags = Flags,
};
return Call(FEXUnixLibFunctions::SetKernelUnalignedAtomicControl, &Args) == STATUS_SUCCESS;
}
// Legacy Proton path.
return NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)) == STATUS_SUCCESS;
}
void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) {
if (UnixLibAvailable()) {
// UnixLib path.
FEXUnixLib_Madvise Args {
.Addr = Ptr,
.Size = Size,
.Advise = Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE,
};
Call(FEXUnixLibFunctions::Madvise, &Args);
return;
}
// Legacy Proton path.
Illegal::VirtualTHPControl(Ptr, Size, Control);
}
void VirtualName(const char* Name, const void* Ptr, size_t Size) {
if (!SupportsVirtualName) {
return;
}
if (UnixLibAvailable()) {
// UnixLib path.
FEXUnixLib_SetVMAName Args {
.Addr = Ptr,
.Size = Size,
.Name = Name,
};
if (Call(FEXUnixLibFunctions::SetVMAName, &Args) != STATUS_SUCCESS) {
SupportsVirtualName = false;
}
return;
}
// Legacy Proton path.
Illegal::VirtualName(Name, Ptr, Size);
}
SHMSlotResult AllocateSHMSlots(void* SHMBase, uint32_t MapSize, uint32_t MaxSize) {
if (UnixLibAvailable()) {
// UnixLib path.
FEXUnixLib_GetSHMStatsVMA Args {
.SHMBase = SHMBase,
.MapSize = MapSize,
.MaxSize = MaxSize,
};
if (Call(FEXUnixLibFunctions::GetSHMStatsVMA, &Args) == STATUS_SUCCESS) {
return {
.SHMBase = Args.SHMBase,
.MappedSize = Args.MapSize,
};
}
return {};
}
// Legacy Proton path.
// Magic WINE+FEX path.
if (SHMBase == nullptr || UsingNTQueryPath) {
MEMORY_FEX_STATS_SHM_INFORMATION Info {
.shm_base = SHMBase,
.map_size = MapSize,
.max_size = MaxSize,
};
size_t Length {};
auto Result = NtQueryVirtualMemory(NtCurrentProcess(), nullptr, MemoryFexStatsShm, &Info, sizeof(Info), &Length);
if (!Result) {
UsingNTQueryPath = true;
return {
.SHMBase = Info.shm_base,
.MappedSize = MapSize,
};
}
}
// Opaque handle path, doesn't support resizing.
auto handle = CreateFile(fextl::fmt::format("/dev/shm/fex-{}-stats", Illegal::linux_getpid()).c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
// Create the section mapping for the file handle for the full size.
HANDLE SectionMapping;
LARGE_INTEGER SectionSize {{MaxSize}};
auto Result = NtCreateSection(&SectionMapping, SECTION_EXTEND_SIZE | SECTION_MAP_READ | SECTION_MAP_WRITE, nullptr, &SectionSize,
PAGE_READWRITE, SEC_COMMIT, handle);
if (Result != STATUS_SUCCESS) {
CloseHandle(handle);
return {};
}
// Section mapping is used from now on.
CloseHandle(handle);
// Now actually map the view of the section.
void* Base = nullptr;
size_t FullSize = MaxSize;
Result = NtMapViewOfSection(SectionMapping, NtCurrentProcess(), &Base, 0, 0, nullptr, &FullSize, ViewUnmap, MEM_RESERVE | MEM_TOP_DOWN,
PAGE_READWRITE);
if (Result != STATUS_SUCCESS) {
CloseHandle(SectionMapping);
return {};
}
return {.SHMBase = Base, .MappedSize = MaxSize};
}
void DeleteSHMStatsFile() {
if (UnixLibAvailable()) {
// UnixLib path.
Call(FEXUnixLibFunctions::DeleteSHMStatsFile, nullptr);
return;
}
// Legacy Proton path.
DeleteFile(fextl::fmt::format("/dev/shm/fex-{}-stats", Illegal::linux_getpid()).c_str());
}
} // namespace FEX::Windows::UnixLib
+20
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <cstdint>
#include <FEXCore/Utils/AllocatorHooks.h>
#include <FEXCore/Utils/EnumUtils.h>
#include "../UnixLib/FEXUnixLib.h"
#include "wine/unixlib.h"
@@ -25,4 +26,23 @@ bool Init(HMODULE NtDll);
* @return true if hardware TSO is supported and enabled.
*/
bool TryEnableHardwareTSO();
/**
* @brief Tries to enable kernel unaligned atomic handling if supported.
*
* @param Flags Which unaligned types to enable
*
* @return true if the flags were enabled
*/
bool SetKernelUnalignedAtomicControl(uint64_t Flags);
void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control);
void VirtualName(const char* Name, const void* Ptr, size_t Size);
struct SHMSlotResult {
void* SHMBase;
uint32_t MappedSize;
};
SHMSlotResult AllocateSHMSlots(void* SHMBase, uint32_t MapSize, uint32_t MaxSize);
void DeleteSHMStatsFile();
} // namespace FEX::Windows::UnixLib
+13 -66
View File
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT
#include "Windows/Common/FEXUnixLib.h"
#include "Windows/Common/SHMStats.h"
#include <FEXCore/fextl/fmt.h>
@@ -12,88 +13,34 @@
#include <wine/debug.h>
namespace FEX::Windows {
__attribute__((naked)) uint64_t linux_getpid() {
asm volatile(R"(
mov x8, 172;
svc #0;
ret;
)" ::
: "r0", "r8");
}
uint32_t StatAlloc::FrontendAllocateSlots(uint32_t NewSize) {
if (CurrentSize == MAX_STATS_SIZE || !UsingNTQueryPath) {
if (CurrentSize == MAX_STATS_SIZE) {
LogMan::Msg::DFmt("Ran out of slots. Can't allocate more");
return CurrentSize;
}
MEMORY_FEX_STATS_SHM_INFORMATION Info {
.shm_base = nullptr,
.map_size = std::min(CurrentSize * 2, MAX_STATS_SIZE),
.max_size = MAX_STATS_SIZE,
};
size_t Length {};
auto Result = NtQueryVirtualMemory(NtCurrentProcess(), nullptr, MemoryFexStatsShm, &Info, sizeof(Info), &Length);
if (!Result) {
CurrentSize = Info.map_size;
auto Result = UnixLib::AllocateSHMSlots(Base, std::min(CurrentSize * 2, MAX_STATS_SIZE), MAX_STATS_SIZE);
if (Result.SHMBase) {
CurrentSize = Result.MappedSize;
}
return CurrentSize;
}
StatAlloc::StatAlloc(FEXCore::SHMStats::AppType AppType) {
// Try wine+fex magic path.
auto Result = UnixLib::AllocateSHMSlots(Base, FEXCore::Utils::FEX_PAGE_SIZE, MAX_STATS_SIZE);
{
MEMORY_FEX_STATS_SHM_INFORMATION Info {
.shm_base = nullptr,
.map_size = FEXCore::Utils::FEX_PAGE_SIZE,
.max_size = MAX_STATS_SIZE,
};
size_t Length {};
auto Result = NtQueryVirtualMemory(NtCurrentProcess(), nullptr, MemoryFexStatsShm, &Info, sizeof(Info), &Length);
if (!Result) {
UsingNTQueryPath = true;
CurrentSize = Info.map_size;
Base = Info.shm_base;
SaveHeader(AppType);
return;
}
}
CurrentSize = MAX_STATS_SIZE;
auto handle = CreateFile(fextl::fmt::format("/dev/shm/fex-{}-stats", linux_getpid()).c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
// Create the section mapping for the file handle for the full size.
HANDLE SectionMapping;
LARGE_INTEGER SectionSize {{MAX_STATS_SIZE}};
auto Result = NtCreateSection(&SectionMapping, SECTION_EXTEND_SIZE | SECTION_MAP_READ | SECTION_MAP_WRITE, nullptr, &SectionSize,
PAGE_READWRITE, SEC_COMMIT, handle);
if (Result != 0) {
CloseHandle(handle);
if (Result.SHMBase) {
CurrentSize = Result.MappedSize;
Base = Result.SHMBase;
SaveHeader(AppType);
return;
}
// Section mapping is used from now on.
CloseHandle(handle);
// Now actually map the view of the section.
Base = 0;
size_t FullSize = MAX_STATS_SIZE;
Result = NtMapViewOfSection(SectionMapping, NtCurrentProcess(), &Base, 0, 0, nullptr, &FullSize, ViewUnmap, MEM_RESERVE | MEM_TOP_DOWN,
PAGE_READWRITE);
if (Result != 0) {
CloseHandle(SectionMapping);
return;
}
// Once WINE supports NtExtendSection and SECTION_EXTEND_SIZE correctly then we can map/commit a single page, map the full MAX_STATS_SIZE
// view as reserved, and extend the view using NtExtendSection.
SaveHeader(AppType);
}
StatAlloc::~StatAlloc() {
DeleteFile(fextl::fmt::format("/dev/shm/fex-{}-stats", linux_getpid()).c_str());
UnixLib::DeleteSHMStatsFile();
}
} // namespace FEX::Windows
-1
View File
@@ -23,7 +23,6 @@ public:
private:
uint32_t FrontendAllocateSlots(uint32_t NewSize) override;
bool UsingNTQueryPath {};
};
} // namespace FEX::Windows
+1 -1
View File
@@ -24,7 +24,7 @@ public:
uint64_t Flags = (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) |
(KernelUnalignedAtomicBackpatching() ? FEX_UNALIGN_ATOMIC_BACKPATCH : 0) | FEX_UNALIGN_ATOMIC_EMULATE;
if (NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)) == STATUS_SUCCESS) {
if (UnixLib::SetKernelUnalignedAtomicControl(Flags)) {
LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!");
}
}
+125 -1
View File
@@ -1,7 +1,11 @@
// SPDX-License-Identifier: MIT
#include <cstdint>
#include <type_traits>
#include <fcntl.h>
#include <string>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>
#include "FEXUnixLib.h"
@@ -31,6 +35,15 @@ constexpr NTSTATUS STATUS_INTERNAL_ERROR = 0xC00000E5;
#ifndef PR_SET_MEM_MODEL_TSO
#define PR_SET_MEM_MODEL_TSO 1
#endif
#ifndef PR_ARM64_SET_UNALIGN_ATOMIC
#define PR_ARM64_SET_UNALIGN_ATOMIC 0x46455849
#endif
#ifndef PR_SET_VMA
#define PR_SET_VMA 0x53564d41
#endif
#ifndef PR_SET_VMA_ANON_NAME
#define PR_SET_VMA_ANON_NAME 0
#endif
static NTSTATUS FEXUnixLib_SetHardwareTSOControlHandler(void* _Args) {
auto Args = reinterpret_cast<const FEXUnixLib_SetHardwareTSOControlArgs*>(_Args);
@@ -46,6 +59,117 @@ static NTSTATUS FEXUnixLib_SetHardwareTSOControlHandler(void* _Args) {
return STATUS_SUCCESS;
}
extern "C" const unixlib_entry_t __wine_unix_call_funcs[] = {FEXUnixLib_SetHardwareTSOControlHandler};
static NTSTATUS FEXUnixLib_SetKernelUnalignedAtomicControlHandler(void* _Args) {
auto Args = reinterpret_cast<const FEXUnixLib_SetKernelUnalignedAtomicControl*>(_Args);
return prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Args->Flags, 0, 0, 0) == 0 ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
}
static NTSTATUS FEXUnixLib_MadviseHandler(void* _Args) {
auto Args = reinterpret_cast<const FEXUnixLib_Madvise*>(_Args);
return madvise(const_cast<void*>(Args->Addr), Args->Size, Args->Advise) == 0 ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
}
static NTSTATUS FEXUnixLib_SetVMANameHandler(void* _Args) {
auto Args = reinterpret_cast<const FEXUnixLib_SetVMAName*>(_Args);
return prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<unsigned long>(Args->Addr), Args->Size, Args->Name) == 0 ?
STATUS_SUCCESS :
STATUS_INTERNAL_ERROR;
}
constexpr static int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO;
static inline void* InitializeSHM(uint32_t MapSize, uint32_t MaxSize) {
void* Base {};
const std::string Name = "fex-" + std::to_string(getpid()) + "-stats";
int fd = shm_open(Name.c_str(), O_CREAT | O_TRUNC | O_RDWR, USER_PERMS);
if (fd == -1) {
return nullptr;
}
if (ftruncate(fd, MapSize) == -1) {
goto err;
}
// Reserve a region of MAX_STATS_SIZE so we can grow the allocation buffer.
// Number of thread slots when ThreadStatsHeader == 64bytes and ThreadStats == 40bytes:
// 1 page: 99 slots
// 1 MB: 26211 slots
// 128 MB: 3355440 slots
Base = mmap(nullptr, MaxSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
if (Base == MAP_FAILED) {
Base = nullptr;
goto err;
}
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<void*>(Base), MaxSize, "FEXMem_Misc");
// Allocate a small working shared space for now, grow as necessary.
{
auto SharedBase = mmap(Base, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if (SharedBase == MAP_FAILED) {
::munmap(Base, MaxSize);
Base = nullptr;
goto err;
}
}
err:
close(fd);
return Base;
}
static inline bool AllocateMoreSlots(void* SHMBase, uint32_t NewSize) {
bool Result {};
const std::string Name = "fex-" + std::to_string(getpid()) + "-stats";
// When allocating more slots, open the fd without O_TRUNC | O_CREAT.
int fd = shm_open(Name.c_str(), O_RDWR, USER_PERMS);
if (fd == -1) {
return false;
}
if (ftruncate(fd, NewSize) == -1) {
goto err;
}
{
auto SharedBase = mmap(SHMBase, NewSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if (SharedBase == MAP_FAILED) {
goto err;
}
}
Result = true;
err:
close(fd);
return Result;
}
static NTSTATUS FEXUnixLib_GetSHMStatsVMAHandler(void* _Args) {
auto Args = reinterpret_cast<FEXUnixLib_GetSHMStatsVMA*>(_Args);
if (Args->SHMBase == nullptr) {
Args->SHMBase = InitializeSHM(Args->MapSize, Args->MaxSize);
return Args->SHMBase ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
}
return AllocateMoreSlots(Args->SHMBase, Args->MapSize) ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
}
static NTSTATUS FEXUnixLib_DeleteSHMStatsFileHandler(void* _Args) {
const std::string Name = "fex-" + std::to_string(getpid()) + "-stats";
shm_unlink(Name.c_str());
return STATUS_SUCCESS;
}
extern "C" const unixlib_entry_t __wine_unix_call_funcs[] = {
FEXUnixLib_SetHardwareTSOControlHandler,
FEXUnixLib_SetKernelUnalignedAtomicControlHandler,
FEXUnixLib_MadviseHandler,
FEXUnixLib_SetVMANameHandler,
FEXUnixLib_GetSHMStatsVMAHandler,
FEXUnixLib_DeleteSHMStatsFileHandler,
};
static_assert(sizeof(__wine_unix_call_funcs) / sizeof(__wine_unix_call_funcs[0]) == ToUnderlying(FEXUnixLibFunctions::MAX));
+38
View File
@@ -1,9 +1,15 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef>
#include <cstdint>
enum class FEXUnixLibFunctions : uint32_t {
SetHardwareTSOControl,
SetKernelUnalignedAtomicControl,
Madvise,
SetVMAName,
GetSHMStatsVMA,
DeleteSHMStatsFile,
MAX,
};
@@ -13,3 +19,35 @@ struct FEXUnixLib_SetHardwareTSOControlArgs {
bool Enable;
};
static_assert(sizeof(FEXUnixLib_SetHardwareTSOControlArgs) == 1);
// These match the prctl flag values for PR_ARM64_SET_UNALIGN_ATOMIC
static constexpr uint64_t FEX_UNALIGN_ATOMIC_EMULATE = 1ULL << 0;
static constexpr uint64_t FEX_UNALIGN_ATOMIC_BACKPATCH = 1ULL << 1;
static constexpr uint64_t FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS = 1ULL << 2;
struct FEXUnixLib_SetKernelUnalignedAtomicControl {
uint64_t Flags;
};
static_assert(sizeof(FEXUnixLib_SetKernelUnalignedAtomicControl) == 8);
struct FEXUnixLib_Madvise {
const void* Addr;
size_t Size;
int32_t Advise;
uint32_t pad;
};
static_assert(sizeof(FEXUnixLib_Madvise) == 24);
struct FEXUnixLib_SetVMAName {
const void* Addr;
size_t Size;
const char* Name;
};
static_assert(sizeof(FEXUnixLib_SetVMAName) == 24);
struct FEXUnixLib_GetSHMStatsVMA {
void* SHMBase;
uint32_t MapSize;
uint32_t MaxSize;
};
static_assert(sizeof(FEXUnixLib_GetSHMStatsVMA) == 16);
-5
View File
@@ -482,11 +482,6 @@ typedef enum _MEMORY_INFORMATION_CLASS {
#define ProcessFexHardwareTso (PROCESSINFOCLASS)2000
#define ProcessFexUnalignAtomic (PROCESSINFOCLASS)2001
// These match the prctl flag values
#define FEX_UNALIGN_ATOMIC_EMULATE (1ULL << 0)
#define FEX_UNALIGN_ATOMIC_BACKPATCH (1ULL << 1)
#define FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1ULL << 2)
typedef enum _KEY_VALUE_INFORMATION_CLASS {
KeyValueBasicInformation,
KeyValueFullInformation,