Merge pull request #5623 from Sonicadvance1/176

Windows/UnixLib: Adds remaining helpers
This commit is contained in:
LC authored and GitHub committed 2026-06-28 18:44:35 -04:00
commit 32b96c259b
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,