Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/wine_build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,17 @@ runs:
- name: Install
shell: bash
run: DESTDIR="$PWD"/install cmake --build build_${{ inputs.target }} -t install

- name: Configure Unixlib
shell: bash
run: |
cmake -S Source/Windows/Unixlib -B build_unixlib_${{ inputs.target }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-G Ninja -DCMAKE_INSTALL_LIBDIR=/usr/lib/wine/aarch64-unix -DCMAKE_INSTALL_PREFIX=/usr

- name: Build Unixlib
shell: bash
run: cmake --build build_unixlib_${{ inputs.target }}

- name: Install Unixlib
shell: bash
run: DESTDIR="$PWD"/install cmake --build build_unixlib_${{ inputs.target }} -t install
4 changes: 3 additions & 1 deletion .github/workflows/wine_dll_artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:
with:
overwrite: true
name: wine_dll_artifacts
path: ${{ github.workspace }}/install/usr/lib/wine/aarch64-windows/lib*.dll
path: |
${{ github.workspace }}/install/usr/lib/wine/aarch64-windows/lib*.dll
${{ github.workspace }}/install/usr/lib/wine/aarch64-unix/lib*.so
retention-days: 60
compression-level: 9
16 changes: 5 additions & 11 deletions Source/Windows/ARM64EC/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ desc: Implements the ARM64EC BT module API using FEXCore
#include <winternl.h>
#include <winnt.h>
#include <wine/debug.h>
#include <wine/unixlib.h>

#include "Unixlib/FEXUnixlib.h"

namespace Exception {
class ECSyscallHandler;
Expand Down Expand Up @@ -597,8 +600,8 @@ NTSTATUS ProcessInit() {
OvercommitTracker.emplace(IsWine);

FEX::Windows::SetupEnvironmentVariableValues(NtDll);

FEX::Windows::Allocator::SetupHooks(NtDll);
FEX::Windows::Allocator::SetupHooks(IsWine);
FEX::Windows::Unixlib::Init(NtDll);

{
auto HostFeatures = FEX::Windows::CPUFeatures::FetchHostFeatures(IsWine);
Expand Down Expand Up @@ -627,15 +630,6 @@ NTSTATUS ProcessInit() {
const uintptr_t KiUserExceptionDispatcherFFS = reinterpret_cast<uintptr_t>(GetProcAddress(NtDll, "KiUserExceptionDispatcher"));
Exception::KiUserExceptionDispatcher = NtDllRedirectionLUT[KiUserExceptionDispatcherFFS - NtDllBase] + NtDllBase;

FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
if (TSOEnabled()) {
BOOL Enable = TRUE;
NTSTATUS Status = NtSetInformationProcess(NtCurrentProcess(), ProcessFexHardwareTso, &Enable, sizeof(Enable));
if (Status == STATUS_SUCCESS) {
CTX->SetHardwareTSOSupport(true);
}
}

FEX_CONFIG_OPT(ProfileStats, PROFILESTATS);
FEX_CONFIG_OPT(StartupSleep, STARTUPSLEEP);
FEX_CONFIG_OPT(StartupSleepProcName, STARTUPSLEEPPROCNAME);
Expand Down
245 changes: 27 additions & 218 deletions Source/Windows/Common/Allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,242 +2,51 @@
#include <FEXCore/Utils/AllocatorHooks.h>
#include <FEXCore/Utils/TypeDefines.h>

#include <array>
#include <chrono>
#include <libloaderapi.h>
#include <sysinfoapi.h>
#include <synchapi.h>
#include <windef.h>
#include <winternl.h>
#include <winnt.h>
#include <wine/debug.h>
#include <wine/unixlib.h>

namespace FEX::Windows::Allocator {
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#include "Unixlib/FEXUnixlib.h"

#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;
}
namespace FEX::Windows::Allocator {

static uint64_t wine_madvise_trampoline(const void* addr, size_t size, int advice) {
madvise_data d {
.addr = addr,
.size = size,
.advise = advice,
static void VirtualName(const char* Name, const void* Ptr, size_t Size) {
static bool Supports {true};
if (Supports) {
FexPrctlSetVMAParams Params {
.addr = const_cast<void*>(Ptr),
.size = Size,
.name = Name,
};
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;
}
if (WINE_UNIX_CALL(fex_unix_prctl_set_vma, &Params) || Params.result != 0) {
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");
static void VirtualTHPControl(const void* Ptr, size_t Size, FEXCore::Allocator::THPControl Control) {
FexMadviseParams Params {
.addr = const_cast<void*>(Ptr),
.size = Size,
.advise = Control == FEXCore::Allocator::THPControl::Disable ? MADV_NOHUGEPAGE : MADV_HUGEPAGE,
};
WINE_UNIX_CALL(fex_unix_madvise, &Params);
}

if (!Sym) {
void SetupHooks(bool IsWine) {
if (!IsWine) {
return;
}

FEXCore::Allocator::HookPtrs Ptrs {};

// Wine will soon require us to use unixlib for calling helper routines that use Linux syscalls.
Comment thread
bylaws marked this conversation as resolved.
// 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,
};
}

SYSTEM_INFO system_info {};
GetSystemInfo(&system_info);
FEXCore::Allocator::SetupHooks(system_info.dwPageSize, Ptrs);
FEXCore::Allocator::SetupHooks(system_info.dwPageSize, {
.VirtualName = VirtualName,
.VirtualTHPControl = VirtualTHPControl,
});
}
} // namespace FEX::Windows::Allocator
2 changes: 1 addition & 1 deletion Source/Windows/Common/Allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
#include <winternl.h>

namespace FEX::Windows::Allocator {
void SetupHooks(HMODULE ntdll);
void SetupHooks(bool IsWine);
}
5 changes: 3 additions & 2 deletions Source/Windows/Common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ add_library(CommonWindows STATIC
SHMStats.cpp
InvalidationTracker.cpp
ImageTracker.cpp
Logging.cpp)
Logging.cpp
Unixlib.cpp)

target_link_libraries(CommonWindows FEXCore_Base)
target_include_directories(CommonWindows PRIVATE "${CMAKE_SOURCE_DIR}/Source/Windows/include/")
target_include_directories(CommonWindows PRIVATE "${CMAKE_SOURCE_DIR}/Source/Windows/include/" "${CMAKE_SOURCE_DIR}/Source/Windows/")
Loading
Loading