mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-08 14:00:18 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae34b1e521 | ||
|
|
c17da25617 | ||
|
|
cc8ef16240 | ||
|
|
30fac81c41 | ||
|
|
bbcca80b60 | ||
|
|
56ae3716bb | ||
|
|
2fba3a4a9a | ||
|
|
8ba0312d40 | ||
|
|
53623ffa72 | ||
|
|
d6199687f4 | ||
|
|
7cb413dde6 | ||
|
|
c5a7fc1e2a | ||
|
|
0869b0aa29 | ||
|
|
4e81f847f2 | ||
|
|
8fa27d2d9f | ||
|
|
e5a8a29efe | ||
|
|
f967f53176 | ||
|
|
31fefaae0d | ||
|
|
7f9edbf39e | ||
|
|
760b9c8e7f | ||
|
|
cc7fb008fc | ||
|
|
98dbfbe654 | ||
|
|
6444726614 | ||
|
|
0a562fbb10 | ||
|
|
7d8950de40 | ||
|
|
3fdde0c90b | ||
|
|
e776f4cd4e | ||
|
|
e7d7dd13d7 | ||
|
|
d5c83a2e45 | ||
|
|
37ccb13917 | ||
|
|
8439cf410f | ||
|
|
614d9448f7 | ||
|
|
70efdbba5d | ||
|
|
12fee91ebb | ||
|
|
bcb7e20619 | ||
|
|
afc5e8a140 | ||
|
|
4be6626c89 | ||
|
|
5f2b6d629b | ||
|
|
1b8f5f08f0 | ||
|
|
79674c697a | ||
|
|
416d8c1df6 | ||
|
|
d03b6a9382 | ||
|
|
df22e0c796 | ||
|
|
79a3bd75cc | ||
|
|
e6acdcc583 | ||
|
|
cd78984228 | ||
|
|
2f007c5f0a | ||
|
|
ae64a1e30c | ||
|
|
097184c3e0 | ||
|
|
84a95adae1 | ||
|
|
123b6728e4 | ||
|
|
69b4fc98ed | ||
|
|
35cf7703b1 | ||
|
|
8c1137543b | ||
|
|
b8e66e56a0 | ||
|
|
fbb008e510 | ||
|
|
04678f8404 | ||
|
|
1b5aaf1fb8 | ||
|
|
d8e4873f43 | ||
|
|
998a3d8353 | ||
|
|
336dedbed5 | ||
|
|
955595be8a | ||
|
|
576bd4f69a | ||
|
|
cd6915917b | ||
|
|
0adbe31112 | ||
|
|
d5138f509c | ||
|
|
1fe6fc3feb | ||
|
|
c03a7fd482 | ||
|
|
31c47f05f4 | ||
|
|
edad24479b | ||
|
|
bba58732e0 | ||
|
|
8d1cc6cf51 | ||
|
|
f03d0be5ef | ||
|
|
a2f4f494a9 | ||
|
|
9de25c200c | ||
|
|
fe1f00aadb | ||
|
|
a847fac4ca | ||
|
|
0496506fb7 | ||
|
|
e544591c9c | ||
|
|
e5237d1149 | ||
|
|
b9c848c5e9 | ||
|
|
b64e61a793 | ||
|
|
249e19bf2a | ||
|
|
baf52ed286 |
No files matched your search
@@ -40,7 +40,6 @@ jobs:
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
|
||||
|
||||
- name : submodule checkout
|
||||
@@ -65,7 +64,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@@ -178,6 +177,17 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXLinuxTests.log || true
|
||||
|
||||
- name: Thunkgen tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target thunkgen_tests
|
||||
|
||||
- name: Thunkgen Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkgenTests.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
|
||||
+1
-1
@@ -476,7 +476,7 @@ endif()
|
||||
set (CPACK_GENERATOR "DEB")
|
||||
set (CPACK_PACKAGE_NAME fex-emu)
|
||||
set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${GIT_DESCRIBE_STRING}_${CMAKE_SYSTEM_PROCESSOR}")
|
||||
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.org>")
|
||||
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.com>")
|
||||
set (CPACK_PACKAGE_VERSION_MAJOR "${FEX_VERSION_MAJOR}")
|
||||
set (CPACK_PACKAGE_VERSION_MINOR "${FEX_VERSION_MINOR}")
|
||||
set (CPACK_PACKAGE_VERSION_PATCH "${FEX_VERSION_PATCH}")
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at team@fex-emu.org. All
|
||||
reported by contacting the project team at team@fex-emu.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
Vendored
+2
-2
@@ -46,14 +46,14 @@ if (OVERRIDE_VERSION STREQUAL "detect")
|
||||
|
||||
if (GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_SHORT_HASH
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe
|
||||
COMMAND ${GIT_EXECUTABLE} describe --abbrev=7
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE_STRING
|
||||
ERROR_QUIET
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ namespace JSON {
|
||||
json_t const* ConfigList = json_getProperty(json, "Config");
|
||||
|
||||
if (!ConfigList) {
|
||||
LogMan::Msg::EFmt("Couldn't get config list");
|
||||
// This is a non-error if the configuration file exists but no Config section
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -351,6 +351,13 @@
|
||||
"Desc": [
|
||||
"Loads an AOT IR cache for the loaded executable."
|
||||
]
|
||||
},
|
||||
"ServerSocketPath": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"Desc": [
|
||||
"Override for a FEXServer socket path. Only useful for chroots."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -368,6 +375,17 @@
|
||||
"Type": "str",
|
||||
"Default": ""
|
||||
},
|
||||
"APP_CONFIG_NAME": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"Desc": [
|
||||
"This is the application config name that has been loaded.",
|
||||
"This differs from APP_FILENAME in two ways",
|
||||
"Where APP_FILENAME always points to the executable path that FEX-Emu is executing.",
|
||||
"This matches what is used to load the AppLayer configuration name.",
|
||||
"When running through a compatibility layer like wine, this will only be the exe name, instead of wine full path."
|
||||
]
|
||||
},
|
||||
"IS64BIT_MODE": {
|
||||
"Type": "bool",
|
||||
"Default": "false"
|
||||
|
||||
@@ -202,6 +202,10 @@ namespace FEXCore::Context {
|
||||
return CTX->AddCustomIREntrypoint(Entrypoint, Handler, Creator, Data);
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
CTX->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
|
||||
CTX->CompileRIP(CTX->ParentThread, RIP);
|
||||
|
||||
@@ -322,6 +322,8 @@ namespace FEXCore::Context {
|
||||
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
|
||||
|
||||
FEXCore::Utils::PooledAllocatorMMap OpDispatcherAllocator;
|
||||
FEXCore::Utils::PooledAllocatorMMap FrontendAllocator;
|
||||
|
||||
|
||||
@@ -1332,6 +1332,11 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Context::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
ThunkHandler->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
|
||||
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
|
||||
@@ -62,8 +62,13 @@ HostFeatures::HostFeatures() {
|
||||
SupportsRCPC = Features.Has(vixl::CPUFeatures::Feature::kRCpc);
|
||||
SupportsTSOImm9 = Features.Has(vixl::CPUFeatures::Feature::kRCpcImm);
|
||||
|
||||
Supports3DNow = true;
|
||||
SupportsSSE4A = true;
|
||||
SupportsAVX = Features.Has(vixl::CPUFeatures::Feature::kSVE2) &&
|
||||
vixl::aarch64::CPU::ReadSVEVectorLengthInBits() >= 256;
|
||||
SupportsSHA = true;
|
||||
SupportsBMI1 = true;
|
||||
SupportsBMI2 = true;
|
||||
|
||||
// We need to get the CPU's cache line size
|
||||
// We expect sane targets that have correct cacheline sizes across clusters
|
||||
@@ -85,7 +90,12 @@ HostFeatures::HostFeatures() {
|
||||
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
|
||||
SupportsRCPC = true;
|
||||
SupportsTSOImm9 = true;
|
||||
Supports3DNow = Features.has(Xbyak::util::Cpu::t3DN) && Features.has(Xbyak::util::Cpu::tE3DN);
|
||||
SupportsSSE4A = Features.has(Xbyak::util::Cpu::tSSE4a);
|
||||
SupportsAVX = true;
|
||||
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
|
||||
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
|
||||
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
|
||||
|
||||
// xbyak doesn't know how to check for CLZero
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
@@ -159,21 +159,26 @@
|
||||
break; \
|
||||
}
|
||||
|
||||
struct InterpVector256 {
|
||||
__uint128_t Lower;
|
||||
__uint128_t Upper;
|
||||
};
|
||||
|
||||
template<typename Res>
|
||||
Res GetDest(void* SSAData, FEXCore::IR::OrderedNodeWrapper Op) {
|
||||
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.ID().Value];
|
||||
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.ID().Value];
|
||||
return reinterpret_cast<Res>(DstPtr);
|
||||
}
|
||||
|
||||
template<typename Res>
|
||||
Res GetDest(void* SSAData, FEXCore::IR::NodeID Op) {
|
||||
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.Value];
|
||||
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.Value];
|
||||
return reinterpret_cast<Res>(DstPtr);
|
||||
}
|
||||
|
||||
|
||||
template<typename Res>
|
||||
Res GetSrc(void* SSAData, FEXCore::IR::OrderedNodeWrapper Src) {
|
||||
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Src.ID().Value];
|
||||
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Src.ID().Value];
|
||||
return reinterpret_cast<Res>(DstPtr);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "InterpreterDefines.h"
|
||||
#include "InterpreterOps.h"
|
||||
#include "F80Ops.h"
|
||||
|
||||
@@ -336,23 +337,28 @@ void InterpreterOps::Op_NoOp(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR:
|
||||
void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::IR::IRListView const *CurrentIR) {
|
||||
volatile void *StackEntry = alloca(0);
|
||||
|
||||
uintptr_t ListSize = CurrentIR->GetSSACount();
|
||||
const uintptr_t ListSize = CurrentIR->GetSSACount();
|
||||
|
||||
static_assert(sizeof(FEXCore::IR::IROp_Header) == 4);
|
||||
static_assert(sizeof(FEXCore::IR::OrderedNode) == 16);
|
||||
|
||||
auto BlockEnd = CurrentIR->GetBlocks().end();
|
||||
|
||||
InterpreterOps::IROpData OpData{};
|
||||
OpData.State = Frame->Thread;
|
||||
OpData.SSAData = alloca(ListSize * 16);
|
||||
OpData.CurrentEntry = Frame->State.rip;
|
||||
OpData.CurrentIR = CurrentIR;
|
||||
OpData.StackEntry = StackEntry;
|
||||
OpData.BlockIterator = CurrentIR->GetBlocks().begin();
|
||||
constexpr size_t ListEntrySizeInBytes = sizeof(InterpVector256);
|
||||
const size_t SSADataSize = ListSize * ListEntrySizeInBytes;
|
||||
|
||||
// Clear them all to zero. Required for Zero-extend semantics
|
||||
memset(OpData.SSAData, 0, ListSize * 16);
|
||||
InterpreterOps::IROpData OpData{
|
||||
.State = Frame->Thread,
|
||||
.CurrentEntry = Frame->State.rip,
|
||||
.CurrentIR = CurrentIR,
|
||||
.StackEntry = StackEntry,
|
||||
.SSAData = alloca(SSADataSize),
|
||||
.BlockResults = {},
|
||||
.BlockIterator = CurrentIR->GetBlocks().begin(),
|
||||
};
|
||||
|
||||
// Clear all SSAData entries to zero. Required for Zero-extend semantics
|
||||
memset(OpData.SSAData, 0, SSADataSize);
|
||||
|
||||
while (1) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
@@ -23,7 +23,7 @@ DEF_OP(VectorImm) {
|
||||
auto Op = IROp->C<IR::IROp_VectorImm>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint8_t Tmp[16];
|
||||
uint8_t Tmp[32];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
uint8_t Imm = Op->Immediate;
|
||||
|
||||
@@ -426,6 +426,7 @@ void Arm64JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
|
||||
, Arm64Emitter(ctx, 0)
|
||||
, HostSupportsSVE{ctx->HostFeatures.SupportsAVX}
|
||||
, CTX {ctx} {
|
||||
|
||||
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
const bool HostSupportsSVE{};
|
||||
|
||||
Label *PendingTargetLabel;
|
||||
FEXCore::Context::Context *CTX;
|
||||
|
||||
+176
-54
@@ -12,17 +12,27 @@ using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
|
||||
DEF_OP(VectorZero) {
|
||||
uint8_t OpSize = IROp->Size;
|
||||
switch (OpSize) {
|
||||
case 8: {
|
||||
eor(GetDst(Node).V8B(), GetDst(Node).V8B(), GetDst(Node).V8B());
|
||||
break;
|
||||
if (HostSupportsSVE) {
|
||||
const auto Dst = GetDst(Node).Z().VnD();
|
||||
eor(Dst, Dst, Dst);
|
||||
} else {
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 8: {
|
||||
const auto Dst = GetDst(Node).V8B();
|
||||
eor(Dst, Dst, Dst);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
const auto Dst = GetDst(Node).V16B();
|
||||
eor(Dst, Dst, Dst);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Op Size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
eor(GetDst(Node).V16B(), GetDst(Node).V16B(), GetDst(Node).V16B());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Element Size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +40,37 @@ DEF_OP(VectorImm) {
|
||||
auto Op = IROp->C<IR::IROp_VectorImm>();
|
||||
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
const uint8_t ElementSize = Op->Header.ElementSize;
|
||||
const uint8_t Elements = OpSize / ElementSize;
|
||||
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
// movi with 64bit element size doesn't do what we want here
|
||||
LoadConstant(TMP1.X(), Op->Immediate);
|
||||
dup(GetDst(Node).V2D(), TMP1.X());
|
||||
}
|
||||
else {
|
||||
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
|
||||
if (HostSupportsSVE) {
|
||||
const auto Dst = [&] {
|
||||
const auto Tmp = GetDst(Node).Z();
|
||||
switch (ElementSize) {
|
||||
case 1:
|
||||
return Tmp.VnB();
|
||||
case 2:
|
||||
return Tmp.VnH();
|
||||
case 4:
|
||||
return Tmp.VnS();
|
||||
case 8:
|
||||
return Tmp.VnD();
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled element size: {}", ElementSize);
|
||||
return Tmp;
|
||||
}
|
||||
}();
|
||||
|
||||
dup(Dst, Op->Immediate);
|
||||
} else {
|
||||
if (ElementSize == 8) {
|
||||
// movi with 64bit element size doesn't do what we want here
|
||||
LoadConstant(TMP1.X(), Op->Immediate);
|
||||
dup(GetDst(Node).V2D(), TMP1.X());
|
||||
}
|
||||
else {
|
||||
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,59 +111,149 @@ DEF_OP(SplatVector4) {
|
||||
}
|
||||
|
||||
DEF_OP(VMov) {
|
||||
auto Op = IROp->C<IR::IROp_VMov>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
auto Op = IROp->C<IR::IROp_VMov>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
|
||||
mov(VTMP1.V16B(), 0, GetSrc(Op->Source.ID()).V16B(), 0);
|
||||
mov(GetDst(Node), VTMP1);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
|
||||
mov(VTMP1.V8H(), 0, GetSrc(Op->Source.ID()).V8H(), 0);
|
||||
mov(GetDst(Node), VTMP1);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
|
||||
mov(VTMP1.V4S(), 0, GetSrc(Op->Source.ID()).V4S(), 0);
|
||||
mov(GetDst(Node), VTMP1);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
mov(GetDst(Node).V8B(), GetSrc(Op->Source.ID()).V8B());
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
if (GetDst(Node).GetCode() != GetSrc(Op->Source.ID()).GetCode())
|
||||
mov(GetDst(Node).V16B(), GetSrc(Op->Source.ID()).V16B());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Element Size: {}", OpSize); break;
|
||||
}
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Source = GetSrc(Op->Source.ID());
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
if (HostSupportsSVE) {
|
||||
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
|
||||
} else {
|
||||
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
|
||||
}
|
||||
|
||||
mov(VTMP1.V16B(), 0, Source.V16B(), 0);
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
mov(Dst.Z().VnD(), VTMP1.Z().VnD());
|
||||
} else {
|
||||
mov(Dst, VTMP1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (HostSupportsSVE) {
|
||||
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
|
||||
} else {
|
||||
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
|
||||
}
|
||||
|
||||
mov(VTMP1.V8H(), 0, Source.V8H(), 0);
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
mov(Dst.Z().VnD(), VTMP1.Z().VnD());
|
||||
} else {
|
||||
mov(Dst, VTMP1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (HostSupportsSVE) {
|
||||
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
|
||||
} else {
|
||||
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
|
||||
}
|
||||
|
||||
mov(VTMP1.V4S(), 0, Source.V4S(), 0);
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
mov(Dst.Z().VnD(), VTMP1.Z().VnD());
|
||||
} else {
|
||||
mov(Dst, VTMP1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (HostSupportsSVE) {
|
||||
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
|
||||
mov(VTMP1.V8B(), Source.V8B());
|
||||
mov(Dst.Z().VnB(), VTMP1.Z().VnB());
|
||||
} else {
|
||||
mov(Dst.V8B(), Source.V8B());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
if (HostSupportsSVE) {
|
||||
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
|
||||
mov(VTMP1.V16B(), Source.V16B());
|
||||
mov(Dst.Z().VnB(), VTMP1.Z().VnB());
|
||||
} else {
|
||||
if (Dst.GetCode() != Source.GetCode()) {
|
||||
mov(Dst.V16B(), Source.V16B());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
if (Dst.GetCode() != Source.GetCode()) {
|
||||
mov(Dst.Z().VnD(), Source.Z().VnD());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Op Size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VAnd) {
|
||||
auto Op = IROp->C<IR::IROp_VAnd>();
|
||||
and_(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Vector1 = GetSrc(Op->Vector1.ID());
|
||||
const auto Vector2 = GetSrc(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
and_(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
|
||||
} else {
|
||||
and_(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VBic) {
|
||||
auto Op = IROp->C<IR::IROp_VBic>();
|
||||
bic(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Vector1 = GetSrc(Op->Vector1.ID());
|
||||
const auto Vector2 = GetSrc(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
bic(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
|
||||
} else {
|
||||
bic(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VOr) {
|
||||
auto Op = IROp->C<IR::IROp_VOr>();
|
||||
orr(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Vector1 = GetSrc(Op->Vector1.ID());
|
||||
const auto Vector2 = GetSrc(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
orr(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
|
||||
} else {
|
||||
orr(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VXor) {
|
||||
auto Op = IROp->C<IR::IROp_VXor>();
|
||||
eor(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Vector1 = GetSrc(Op->Vector1.ID());
|
||||
const auto Vector2 = GetSrc(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
eor(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
|
||||
} else {
|
||||
eor(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VAdd) {
|
||||
|
||||
@@ -25,6 +25,7 @@ $end_info$
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/RegisterAllocationData.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -293,7 +294,8 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
case FABI_UNKNOWN:
|
||||
default:
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}", FEXCore::IR::GetName(IROp->Op), Info.ABI);
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}",
|
||||
IR::GetName(IROp->Op), ToUnderlying(Info.ABI));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -181,6 +181,10 @@ private:
|
||||
[[nodiscard]] Xbyak::Xmm GetSrc(IR::NodeID Node) const;
|
||||
[[nodiscard]] Xbyak::Xmm GetDst(IR::NodeID Node) const;
|
||||
|
||||
[[nodiscard]] static Xbyak::Ymm ToYMM(const Xbyak::Xmm& xmm) {
|
||||
return Xbyak::Ymm{xmm.getIdx()};
|
||||
}
|
||||
|
||||
[[nodiscard]] Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset,
|
||||
IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
|
||||
|
||||
|
||||
+55
-21
@@ -24,10 +24,11 @@ DEF_OP(VectorZero) {
|
||||
|
||||
DEF_OP(VectorImm) {
|
||||
auto Op = IROp->C<IR::IROp_VectorImm>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
auto Dst = GetDst(Node);
|
||||
uint64_t Imm = Op->Immediate;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint64_t Imm = Op->Immediate;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
|
||||
uint64_t Element{};
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -61,9 +62,13 @@ DEF_OP(VectorImm) {
|
||||
|
||||
mov(TMP1, Element);
|
||||
vmovq(Dst, TMP1);
|
||||
if (OpSize == 16) {
|
||||
// Duplicate to the upper 64bits if we are 128bits
|
||||
movddup(Dst, Dst);
|
||||
|
||||
if (OpSize >= 16) {
|
||||
LOGMAN_THROW_AA_FMT(OpSize == 16 || OpSize == 32,
|
||||
"Can't handle a vector of size: {}", OpSize);
|
||||
|
||||
// Duplicate into upper elements
|
||||
vbroadcastsd(ToYMM(Dst), Dst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,61 +103,90 @@ DEF_OP(VMov) {
|
||||
auto Op = IROp->C<IR::IROp_VMov>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Source = GetSrc(Op->Source.ID());
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
vpxor(xmm15, xmm15, xmm15);
|
||||
pextrb(eax, GetSrc(Op->Source.ID()), 0);
|
||||
pextrb(eax, Source, 0);
|
||||
pinsrb(xmm15, eax, 0);
|
||||
movapd(GetDst(Node), xmm15);
|
||||
vmovapd(Dst, xmm15);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
vpxor(xmm15, xmm15, xmm15);
|
||||
pextrw(eax, GetSrc(Op->Source.ID()), 0);
|
||||
pextrw(eax, Source, 0);
|
||||
pinsrw(xmm15, eax, 0);
|
||||
movapd(GetDst(Node), xmm15);
|
||||
vmovapd(Dst, xmm15);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
vpxor(xmm15, xmm15, xmm15);
|
||||
pextrd(eax, GetSrc(Op->Source.ID()), 0);
|
||||
pextrd(eax, Source, 0);
|
||||
pinsrd(xmm15, eax, 0);
|
||||
movapd(GetDst(Node), xmm15);
|
||||
vmovapd(Dst, xmm15);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
movq(GetDst(Node), GetSrc(Op->Source.ID()));
|
||||
vmovq(Dst, Source);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
movaps(GetDst(Node), GetSrc(Op->Source.ID()));
|
||||
vmovaps(Dst, Source);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Element Size: {}", OpSize); break;
|
||||
case 32: {
|
||||
vmovaps(ToYMM(Dst), ToYMM(Source));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Op Size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VAnd) {
|
||||
auto Op = IROp->C<IR::IROp_VAnd>();
|
||||
vpand(GetDst(Node), GetSrc(Op->Vector1.ID()), GetSrc(Op->Vector2.ID()));
|
||||
|
||||
const auto Dst = ToYMM(GetDst(Node));
|
||||
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
|
||||
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
|
||||
|
||||
vpand(Dst, Vector1, Vector2);
|
||||
}
|
||||
|
||||
DEF_OP(VBic) {
|
||||
auto Op = IROp->C<IR::IROp_VBic>();
|
||||
|
||||
const auto Dst = ToYMM(GetDst(Node));
|
||||
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
|
||||
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
|
||||
|
||||
// This doesn't map directly to ARM
|
||||
vpcmpeqd(xmm15, xmm15, xmm15);
|
||||
vpxor(xmm15, GetSrc(Op->Vector2.ID()), xmm15);
|
||||
vpand(GetDst(Node), GetSrc(Op->Vector1.ID()), xmm15);
|
||||
vpcmpeqd(ymm15, ymm15, ymm15);
|
||||
vpxor(ymm15, Vector2, ymm15);
|
||||
vpand(Dst, Vector1, ymm15);
|
||||
}
|
||||
|
||||
DEF_OP(VOr) {
|
||||
auto Op = IROp->C<IR::IROp_VOr>();
|
||||
vpor(GetDst(Node), GetSrc(Op->Vector1.ID()), GetSrc(Op->Vector2.ID()));
|
||||
|
||||
const auto Dst = ToYMM(GetDst(Node));
|
||||
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
|
||||
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
|
||||
|
||||
vpor(Dst, Vector1, Vector2);
|
||||
}
|
||||
|
||||
DEF_OP(VXor) {
|
||||
auto Op = IROp->C<IR::IROp_VXor>();
|
||||
vpxor(GetDst(Node), GetSrc(Op->Vector1.ID()), GetSrc(Op->Vector2.ID()));
|
||||
|
||||
const auto Dst = ToYMM(GetDst(Node));
|
||||
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
|
||||
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
|
||||
|
||||
vpxor(Dst, Vector1, Vector2);
|
||||
}
|
||||
|
||||
DEF_OP(VAdd) {
|
||||
|
||||
+19
-6
@@ -28,7 +28,9 @@ $end_info$
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
#include "jemalloc/jemalloc.h"
|
||||
#endif
|
||||
|
||||
struct LoadlibArgs {
|
||||
const char *Name;
|
||||
@@ -137,7 +139,7 @@ namespace FEXCore {
|
||||
// sha256(fex:allocate_host_trampoline_for_guest_function)
|
||||
{ 0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a },
|
||||
&AllocateHostTrampolineForGuestFunction
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away.
|
||||
@@ -241,12 +243,17 @@ namespace FEXCore {
|
||||
* and host heap pointers.
|
||||
*/
|
||||
static void IsHostHeapAllocation(void* ArgsRV) {
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
struct ArgsRV_t {
|
||||
void* ptr;
|
||||
bool rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = je_is_known_allocation(args->ptr);
|
||||
#else
|
||||
// Thunks usage without jemalloc isn't supported
|
||||
ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void LoadLib(void *ArgsV) {
|
||||
@@ -313,7 +320,7 @@ namespace FEXCore {
|
||||
}
|
||||
}
|
||||
|
||||
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) {
|
||||
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) override {
|
||||
|
||||
std::shared_lock lk(ThunksMutex);
|
||||
|
||||
@@ -326,13 +333,19 @@ namespace FEXCore {
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) override {
|
||||
::Thread = Thread;
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) override {
|
||||
for (auto & Definition : Definitions) {
|
||||
Thunks.emplace(Definition.Sum, Definition.ThunkFunction);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ThunkHandler* ThunkHandler::Create() {
|
||||
return new ThunkHandler_impl();
|
||||
return new ThunkHandler_impl();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +371,7 @@ namespace FEXCore {
|
||||
* is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side,
|
||||
* and provide the HostPacker host-side.
|
||||
*/
|
||||
__attribute__((visibility("default")))
|
||||
FEX_DEFAULT_VISIBILITY
|
||||
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) {
|
||||
LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function");
|
||||
|
||||
@@ -416,7 +429,7 @@ namespace FEXCore {
|
||||
return HostTrampoline;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
FEX_DEFAULT_VISIBILITY
|
||||
void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) {
|
||||
auto& Trampoline = GetInstanceInfo(TrampolineAddress);
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ $end_info$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
@@ -28,5 +32,7 @@ namespace FEXCore {
|
||||
virtual ~ThunkHandler() { }
|
||||
|
||||
static ThunkHandler* Create();
|
||||
|
||||
virtual void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) = 0;
|
||||
};
|
||||
};
|
||||
+102
-79
@@ -6,6 +6,10 @@
|
||||
#include <FEXHeaderUtils/TypeDefines.h>
|
||||
|
||||
#include <array>
|
||||
#include <asm-generic/errno-base.h>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/user.h>
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
@@ -131,91 +135,119 @@ namespace FEXCore::Allocator {
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
PtrCache* StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
|
||||
PtrCache *Cache{};
|
||||
uint64_t CacheSize{};
|
||||
uint64_t CurrentCacheOffset = 0;
|
||||
constexpr std::array<size_t, 10> ReservedVMARegionSizes = {{
|
||||
// Anything larger than 64GB fails out
|
||||
64ULL * 1024 * 1024 * 1024, // 64GB
|
||||
32ULL * 1024 * 1024 * 1024, // 32GB
|
||||
16ULL * 1024 * 1024 * 1024, // 16GB
|
||||
4ULL * 1024 * 1024 * 1024, // 4GB
|
||||
1ULL * 1024 * 1024 * 1024, // 1GB
|
||||
512ULL * 1024 * 1024, // 512MB
|
||||
128ULL * 1024 * 1024, // 128MB
|
||||
32ULL * 1024 * 1024, // 32MB
|
||||
1ULL * 1024 * 1024, // 1MB
|
||||
4096ULL // One page
|
||||
}};
|
||||
constexpr size_t AllocationSizeMaxIndex = ReservedVMARegionSizes.size() - 1;
|
||||
uint64_t CurrentSizeIndex = 0;
|
||||
#define STEAL_LOG(...) // fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
int PROT_FLAGS = PROT_READ | PROT_WRITE;
|
||||
for (size_t MemoryOffset = Begin; MemoryOffset < End;) {
|
||||
size_t AllocationSize = ReservedVMARegionSizes[CurrentSizeIndex];
|
||||
size_t MemoryOffsetUpper = MemoryOffset + AllocationSize;
|
||||
std::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
|
||||
std::vector<MemoryRegion> Regions;
|
||||
|
||||
int MapsFD = open("/proc/self/maps", O_RDONLY);
|
||||
LogMan::Throw::AFmt(MapsFD != -1, "Failed to open /proc/self/maps");
|
||||
|
||||
// If we would go above the upper bound on size then try the next size
|
||||
if (MemoryOffsetUpper > End) {
|
||||
++CurrentSizeIndex;
|
||||
continue;
|
||||
enum {ParseBegin, ParseEnd, ScanEnd} State = ParseBegin;
|
||||
|
||||
uintptr_t RegionBegin = 0;
|
||||
uintptr_t RegionEnd = 0;
|
||||
|
||||
char Buffer[2048];
|
||||
const char *Cursor;
|
||||
ssize_t Remaining = 0;
|
||||
|
||||
for(;;) {
|
||||
|
||||
if (Remaining == 0) {
|
||||
do {
|
||||
Remaining = read(MapsFD, Buffer, sizeof(Buffer));
|
||||
} while ( Remaining == -1 && errno == EAGAIN);
|
||||
|
||||
Cursor = Buffer;
|
||||
}
|
||||
|
||||
void *Ptr = ::mmap(reinterpret_cast<void*>(MemoryOffset), AllocationSize, PROT_FLAGS, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
if (Remaining == 0 && State == ParseBegin) {
|
||||
STEAL_LOG("[%d] EndOfFile; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
|
||||
|
||||
// If we managed to allocate and not get the address we want then unmap it
|
||||
// This happens with kernels older than 4.17
|
||||
if (reinterpret_cast<uintptr_t>(Ptr) + AllocationSize > End) {
|
||||
::munmap(Ptr, AllocationSize);
|
||||
Ptr = reinterpret_cast<void*>(~0ULL);
|
||||
}
|
||||
auto MapBegin = std::max(RegionEnd, Begin);
|
||||
auto MapEnd = End;
|
||||
|
||||
// If we failed to allocate and we are on the smallest allocation size then just continue onward
|
||||
// This page was unmappable
|
||||
if (reinterpret_cast<uintptr_t>(Ptr) == ~0ULL && CurrentSizeIndex == AllocationSizeMaxIndex) {
|
||||
CurrentSizeIndex = 0;
|
||||
MemoryOffset += AllocationSize;
|
||||
continue;
|
||||
}
|
||||
STEAL_LOG(" MapBegin: %016lX MapEnd: %016lX\n", MapBegin, MapEnd);
|
||||
|
||||
// Congratulations we were able to map this bit
|
||||
// Reset and claim it was available
|
||||
if (reinterpret_cast<uintptr_t>(Ptr) != ~0ULL) {
|
||||
if (!Cache) {
|
||||
Cache = reinterpret_cast<PtrCache *>(Ptr);
|
||||
CacheSize = AllocationSize;
|
||||
PROT_FLAGS = PROT_NONE;
|
||||
}
|
||||
else {
|
||||
Cache[CurrentCacheOffset] = {
|
||||
.Ptr = static_cast<uint64_t>(reinterpret_cast<uint64_t>(Ptr)),
|
||||
.Size = static_cast<uint64_t>(AllocationSize)
|
||||
};
|
||||
++CurrentCacheOffset;
|
||||
if (MapEnd > MapBegin) {
|
||||
STEAL_LOG(" Reserving\n");
|
||||
|
||||
auto MapSize = MapEnd - MapBegin;
|
||||
auto Alloc = mmap((void*)MapBegin, MapSize, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
|
||||
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({:x},{:x}) failed", MapBegin, MapSize);
|
||||
LogMan::Throw::AFmt(Alloc == (void*)MapBegin, "mmap({},{:x}) returned {} instead of {:x}", Alloc, MapBegin);
|
||||
|
||||
Regions.push_back({(void*)MapBegin, MapSize});
|
||||
}
|
||||
|
||||
CurrentSizeIndex = 0;
|
||||
MemoryOffset += AllocationSize;
|
||||
close(MapsFD);
|
||||
return Regions;
|
||||
}
|
||||
|
||||
LogMan::Throw::AFmt(Remaining > 0, "Failed to parse /proc/self/maps");
|
||||
|
||||
auto c = *Cursor++;
|
||||
Remaining--;
|
||||
|
||||
if (State == ScanEnd) {
|
||||
if (c == '\n') {
|
||||
State = ParseBegin;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Couldn't allocate at this size
|
||||
// Increase and continue
|
||||
++CurrentSizeIndex;
|
||||
if (State == ParseBegin) {
|
||||
if (c == '-') {
|
||||
STEAL_LOG("[%d] ParseBegin; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
|
||||
|
||||
auto MapBegin = std::max(RegionEnd, Begin);
|
||||
auto MapEnd = std::min(RegionBegin, End);
|
||||
|
||||
STEAL_LOG(" MapBegin: %016lX MapEnd: %016lX\n", MapBegin, MapEnd);
|
||||
|
||||
if (MapEnd > MapBegin) {
|
||||
STEAL_LOG(" Reserving\n");
|
||||
|
||||
auto MapSize = MapEnd - MapBegin;
|
||||
auto Alloc = mmap((void*)MapBegin, MapSize, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
|
||||
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({:x},{:x}) failed", MapBegin, MapSize);
|
||||
LogMan::Throw::AFmt(Alloc == (void*)MapBegin, "mmap({},{:x}) returned {} instead of {:x}", Alloc, MapBegin);
|
||||
|
||||
Regions.push_back({(void*)MapBegin, MapSize});
|
||||
}
|
||||
RegionBegin = 0;
|
||||
RegionEnd = 0;
|
||||
State = ParseEnd;
|
||||
continue;
|
||||
} else {
|
||||
LogMan::Throw::AFmt(std::isalpha(c) || std::isdigit(c), "Unexpected char '{}' in ParseBegin", c);
|
||||
RegionBegin = (RegionBegin << 4) | (c <= '9' ? (c - '0') : (c - 'a' + 10));
|
||||
}
|
||||
}
|
||||
|
||||
if (State == ParseEnd) {
|
||||
if (c == ' ') {
|
||||
STEAL_LOG("[%d] ParseEnd; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
|
||||
|
||||
State = ScanEnd;
|
||||
continue;
|
||||
} else {
|
||||
LogMan::Throw::AFmt(std::isalpha(c) || std::isdigit(c), "Unexpected char '{}' in ParseEnd", c);
|
||||
RegionEnd = (RegionEnd << 4) | (c <= '9' ? (c - '0') : (c - 'a' + 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cache[CurrentCacheOffset] = {
|
||||
.Ptr = static_cast<uint64_t>(reinterpret_cast<uint64_t>(Cache)),
|
||||
.Size = CacheSize,
|
||||
};
|
||||
return Cache;
|
||||
ERROR_AND_DIE_FMT("unreachable");
|
||||
}
|
||||
|
||||
PtrCache* Steal48BitVA() {
|
||||
std::vector<MemoryRegion> Steal48BitVA() {
|
||||
size_t Bits = FEXCore::Allocator::DetermineVASize();
|
||||
if (Bits < 48) {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
uintptr_t Begin48BitVA = 0x0'8000'0000'0000ULL;
|
||||
@@ -223,18 +255,9 @@ namespace FEXCore::Allocator {
|
||||
return StealMemoryRegion(Begin48BitVA, End48BitVA);
|
||||
}
|
||||
|
||||
void ReclaimMemoryRegion(PtrCache* Regions) {
|
||||
if (Regions == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0;; ++i) {
|
||||
void *Ptr = reinterpret_cast<void*>(Regions[i].Ptr);
|
||||
size_t Size = Regions[i].Size;
|
||||
::munmap(Ptr, Size);
|
||||
if (Ptr == Regions) {
|
||||
break;
|
||||
}
|
||||
void ReclaimMemoryRegion(const std::vector<MemoryRegion> &Regions) {
|
||||
for (const auto &Region: Regions) {
|
||||
::munmap(Region.Ptr, Region.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
-97
@@ -70,7 +70,10 @@ namespace Alloc::OSAllocator {
|
||||
ReservedVMARegion *SlabInfo;
|
||||
uint64_t FreeSpace{};
|
||||
uint32_t LastPageAllocation{};
|
||||
FEXCore::FlexBitSet<uint64_t> UsedPages;
|
||||
|
||||
// Align UsedPages so it pads to the next page.
|
||||
// Necessary to take advantage of madvise zero page pooling.
|
||||
alignas(4096) FEXCore::FlexBitSet<uint64_t> UsedPages;
|
||||
|
||||
// This returns the size of the LiveVMARegion in addition to the flex set that tracks the used data
|
||||
// The LiveVMARegion lives at the start of the VMA region which means on initialization we need to set that
|
||||
@@ -93,16 +96,23 @@ namespace Alloc::OSAllocator {
|
||||
Region->FreeSpace = Region->SlabInfo->RegionSize - SizePlusManagedData;
|
||||
|
||||
size_t NumPages = SizePlusManagedData >> FHU::FEX_PAGE_SHIFT;
|
||||
// Memset the full tracking to zero to state nothing used
|
||||
Region->UsedPages.MemSet(Region->SlabInfo->RegionSize >> FHU::FEX_PAGE_SHIFT);
|
||||
|
||||
// Use madvise to set the full tracking region to zero.
|
||||
// This ensures unused pages are zero, while not having the backing pages consuming memory.
|
||||
::madvise(Region->UsedPages.Memory + (NumPages * 4096), (Region->SlabInfo->RegionSize >> FHU::FEX_PAGE_SHIFT) - (NumPages * 4096), MADV_DONTNEED);
|
||||
|
||||
// Use madvise to claim WILLNEED on the beginning pages for initial state tracking.
|
||||
// Improves performance of the following MemClear by not doing a page level fault dance for data necessary to track >170TB of used pages.
|
||||
::madvise(Region->UsedPages.Memory, NumPages * 4096, MADV_WILLNEED);
|
||||
|
||||
// Set our reserved pages
|
||||
for (size_t i = 0; i < NumPages; ++i) {
|
||||
// Set our used pages
|
||||
Region->UsedPages.Set(i);
|
||||
}
|
||||
Region->UsedPages.MemSet(NumPages);
|
||||
Region->LastPageAllocation = NumPages;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(LiveVMARegion) == 4096, "Needs to be the size of a page");
|
||||
|
||||
static_assert(std::is_trivially_copyable<LiveVMARegion>::value, "Needs to be trivially copyable");
|
||||
static_assert(offsetof(LiveVMARegion, UsedPages) == sizeof(LiveVMARegion), "FlexBitSet needs to be at the end");
|
||||
|
||||
@@ -131,23 +141,29 @@ namespace Alloc::OSAllocator {
|
||||
|
||||
// Copy over the reserved data
|
||||
LiveRange->SlabInfo = ReservedRegion;
|
||||
|
||||
// Initialize VMA
|
||||
LiveVMARegion::InitializeVMARegionUsed(LiveRange, UsedSize);
|
||||
|
||||
// Add to our active tracked ranges
|
||||
auto LiveIter = LiveRegions->emplace_back(LiveRange);
|
||||
|
||||
return LiveIter;
|
||||
}
|
||||
|
||||
// 32-bit old kernel workarounds
|
||||
FEXCore::Allocator::PtrCache *Steal32BitIfOldKernel();
|
||||
std::vector<FEXCore::Allocator::MemoryRegion> Steal32BitIfOldKernel();
|
||||
};
|
||||
|
||||
void OSAllocator_64Bit::DetermineVASize() {
|
||||
size_t Bits = FEXCore::Allocator::DetermineVASize();
|
||||
uintptr_t Size = 1ULL << Bits;
|
||||
|
||||
UPPER_BOUND = Size;
|
||||
|
||||
#if _M_X86_64 // Last page cannot be allocated on x86
|
||||
UPPER_BOUND -= FHU::FEX_PAGE_SIZE;
|
||||
#endif
|
||||
|
||||
UPPER_BOUND_PAGE = UPPER_BOUND / FHU::FEX_PAGE_SIZE;
|
||||
}
|
||||
|
||||
@@ -490,11 +506,11 @@ int OSAllocator_64Bit::Munmap(void *addr, size_t length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
|
||||
std::vector<FEXCore::Allocator::MemoryRegion> OSAllocator_64Bit::Steal32BitIfOldKernel() {
|
||||
// First calculate kernel version
|
||||
struct utsname buf{};
|
||||
if (uname(&buf) == -1) {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t Major{};
|
||||
@@ -512,7 +528,7 @@ FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
|
||||
|
||||
if (Version >= ((4 << 24) | (17 << 16) | 0)) {
|
||||
// If the kernel is >= 4.17 then it supports MAP_FIXED_NOREPLACE
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr size_t LOWER_BOUND_32 = 0x1'0000;
|
||||
@@ -523,101 +539,39 @@ FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
|
||||
|
||||
OSAllocator_64Bit::OSAllocator_64Bit() {
|
||||
DetermineVASize();
|
||||
auto ArrayPtr = Steal32BitIfOldKernel();
|
||||
auto LowMem = Steal32BitIfOldKernel();
|
||||
|
||||
// On allocation try and steal the entire upper 64bits of address space for mapping
|
||||
constexpr std::array<size_t, 8> ReservedVMARegionSizes = {{
|
||||
// Anything larger than 64GB fails out
|
||||
64ULL * 1024 * 1024 * 1024, // 64GB
|
||||
32ULL * 1024 * 1024 * 1024, // 32GB
|
||||
16ULL * 1024 * 1024 * 1024, // 16GB
|
||||
4ULL * 1024 * 1024 * 1024, // 4GB
|
||||
1ULL * 1024 * 1024 * 1024, // 1GB
|
||||
512ULL * 1024 * 1024, // 512MB
|
||||
128ULL * 1024 * 1024, // 128MB
|
||||
4096ULL // One page
|
||||
}};
|
||||
auto Ranges = FEXCore::Allocator::StealMemoryRegion(LOWER_BOUND, UPPER_BOUND);
|
||||
|
||||
constexpr size_t AllocationSizeMaxIndex = ReservedVMARegionSizes.size() - 1;
|
||||
for (auto [Ptr, AllocationSize]: Ranges) {
|
||||
if (!ObjectAlloc) {
|
||||
auto MaxSize = std::min(size_t(64) * 1024 * 1024, AllocationSize);
|
||||
|
||||
// Have the first region only be 4GB VMA
|
||||
// Avoids conflicts with some tests
|
||||
uint64_t CurrentSizeIndex = 3;
|
||||
ReservedVMARegion *PrevReserved{};
|
||||
for (size_t MemoryOffset = LOWER_BOUND; MemoryOffset < UPPER_BOUND;) {
|
||||
size_t AllocationSize = ReservedVMARegionSizes[CurrentSizeIndex];
|
||||
size_t MemoryOffsetUpper = MemoryOffset + AllocationSize;
|
||||
// Allocate up to 64 MiB the first allocation for an intrusive allocator
|
||||
mprotect(Ptr, MaxSize, PROT_READ | PROT_WRITE);
|
||||
|
||||
// If we would go above the upper bound on size then try the next size
|
||||
if (MemoryOffsetUpper > UPPER_BOUND) {
|
||||
++CurrentSizeIndex;
|
||||
continue;
|
||||
}
|
||||
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
|
||||
::madvise(Ptr, MaxSize, MADV_HUGEPAGE);
|
||||
|
||||
void *Ptr = ::mmap(reinterpret_cast<void*>(MemoryOffset), AllocationSize, PROT_NONE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
ObjectAlloc = new (Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(Ptr, MaxSize);
|
||||
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
|
||||
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
|
||||
|
||||
// If we managed to allocate and not get the address we want then unmap it
|
||||
// This happens with kernels older than 4.17
|
||||
if (reinterpret_cast<uintptr_t>(Ptr) != MemoryOffset &&
|
||||
reinterpret_cast<uintptr_t>(Ptr) < LOWER_BOUND) {
|
||||
::munmap(Ptr, AllocationSize);
|
||||
Ptr = reinterpret_cast<void*>(~0ULL);
|
||||
}
|
||||
|
||||
// If we failed to allocate and we are on the smallest allocation size then just continue onward
|
||||
// This page was unmappable
|
||||
if (reinterpret_cast<uintptr_t>(Ptr) == ~0ULL && CurrentSizeIndex == AllocationSizeMaxIndex) {
|
||||
CurrentSizeIndex = 0;
|
||||
MemoryOffset += AllocationSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Congratulations we were able to map this bit
|
||||
// Reset and claim it was available
|
||||
if (reinterpret_cast<uintptr_t>(Ptr) != ~0ULL) {
|
||||
if (!ObjectAlloc) {
|
||||
// Steal the first allocation for an intrusive allocator
|
||||
// Will be mprotected correctly already
|
||||
mprotect(Ptr, AllocationSize, PROT_READ | PROT_WRITE);
|
||||
ObjectAlloc = new (Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(Ptr, AllocationSize);
|
||||
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
|
||||
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
|
||||
if (AllocationSize > MaxSize) {
|
||||
AllocationSize -= MaxSize;
|
||||
(uint8_t*&)Ptr += MaxSize;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
|
||||
// If the allocation size is large than a page, then try allowing it to be a huge page
|
||||
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
|
||||
// Considering we are allocating the entire VA space, this is a good thing
|
||||
// If MADV_HUGEPAGE isn't support then this will fail harmlessly
|
||||
if (AllocationSize > 4096) {
|
||||
::madvise(Ptr, AllocationSize, MADV_HUGEPAGE);
|
||||
}
|
||||
|
||||
bool Merged = false;
|
||||
if (PrevReserved) {
|
||||
Merged = MergeReservedRegionIfPossible(PrevReserved, reinterpret_cast<uint64_t>(Ptr), AllocationSize);
|
||||
}
|
||||
|
||||
if (!Merged) {
|
||||
ReservedVMARegion *Region = ObjectAlloc->new_construct<ReservedVMARegion>();
|
||||
Region->Base = reinterpret_cast<uint64_t>(Ptr);
|
||||
Region->RegionSize = AllocationSize;
|
||||
ReservedRegions->emplace_back(Region);
|
||||
PrevReserved = Region;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentSizeIndex = 0;
|
||||
MemoryOffset += AllocationSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Couldn't allocate at this size
|
||||
// Increase and continue
|
||||
++CurrentSizeIndex;
|
||||
|
||||
ReservedVMARegion *Region = ObjectAlloc->new_construct<ReservedVMARegion>();
|
||||
Region->Base = reinterpret_cast<uint64_t>(Ptr);
|
||||
Region->RegionSize = AllocationSize;
|
||||
ReservedRegions->emplace_back(Region);
|
||||
}
|
||||
|
||||
FEXCore::Allocator::ReclaimMemoryRegion(ArrayPtr);
|
||||
FEXCore::Allocator::ReclaimMemoryRegion(LowMem);
|
||||
}
|
||||
|
||||
OSAllocator_64Bit::~OSAllocator_64Bit() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <istream>
|
||||
@@ -280,4 +281,12 @@ namespace FEXCore::Context {
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
|
||||
FEX_DEFAULT_VISIBILITY CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
|
||||
*
|
||||
* @param CTX A valid non-null context instance.
|
||||
* @param Definitions A vector of thunk definitions that the frontend controls
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
|
||||
}
|
||||
@@ -21,7 +21,12 @@ class HostFeatures final {
|
||||
bool SupportsRCPC{};
|
||||
bool SupportsTSOImm9{};
|
||||
bool SupportsRAND{};
|
||||
bool Supports3DNow{};
|
||||
bool SupportsSSE4A{};
|
||||
bool SupportsAVX{};
|
||||
bool SupportsSHA{};
|
||||
bool SupportsBMI1{};
|
||||
bool SupportsBMI2{};
|
||||
|
||||
// Float exception behaviour
|
||||
bool SupportsFlushInputsToZero{};
|
||||
|
||||
@@ -58,6 +58,14 @@ namespace FEXCore {
|
||||
FEATURE_XTILE_DATA = 1U << 18,
|
||||
};
|
||||
|
||||
bool HasExtendedContext() const {
|
||||
return magic1 == FP_XSTATE_MAGIC;
|
||||
}
|
||||
|
||||
bool HasYMMH() const {
|
||||
return (xfeatures & FEATURE_YMM) != 0;
|
||||
}
|
||||
|
||||
// If magic1 is set to FP_XSTATE_MAGIC, then the encompassing
|
||||
// frame is an xstate frame. If 0, then it's a legacy frame.
|
||||
uint32_t magic1;
|
||||
|
||||
+7
@@ -413,6 +413,13 @@ struct SHA256Sum final {
|
||||
}
|
||||
};
|
||||
|
||||
typedef void ThunkedFunction(void* ArgsRv);
|
||||
|
||||
struct ThunkDefinition final {
|
||||
SHA256Sum Sum;
|
||||
ThunkedFunction *ThunkFunction;
|
||||
};
|
||||
|
||||
class NodeIterator;
|
||||
|
||||
/* This iterator can be used to step though nodes.
|
||||
|
||||
+8
-7
@@ -5,6 +5,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::Allocator {
|
||||
using MMAP_Hook = void*(*)(void*, size_t, int, int, int, off_t);
|
||||
@@ -24,19 +25,19 @@ namespace FEXCore::Allocator {
|
||||
FEX_DEFAULT_VISIBILITY void ClearHooks();
|
||||
|
||||
FEX_DEFAULT_VISIBILITY size_t DetermineVASize();
|
||||
// 48-bit VA handling
|
||||
struct PtrCache {
|
||||
uint64_t Ptr;
|
||||
uint64_t Size;
|
||||
|
||||
struct MemoryRegion {
|
||||
void *Ptr;
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
FEX_DEFAULT_VISIBILITY PtrCache* StealMemoryRegion(uintptr_t Begin, uintptr_t End);
|
||||
FEX_DEFAULT_VISIBILITY void ReclaimMemoryRegion(PtrCache* Regions);
|
||||
FEX_DEFAULT_VISIBILITY std::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End);
|
||||
FEX_DEFAULT_VISIBILITY void ReclaimMemoryRegion(const std::vector<MemoryRegion> & Regions);
|
||||
// When running a 64-bit executable on ARM then userspace guest only gets 47 bits of VA
|
||||
// This is a feature of x86-64 where the kernel gets a full 128TB of VA space
|
||||
// x86-64 canonical addresses with bit 48 set will sign extend the address (Ignoring LA57)
|
||||
// AArch64 canonical addresses are only up to bits 48/52 with the remainder being other things
|
||||
// Use this to reserve the top 128TB of VA so the guest never see it
|
||||
// Returns nullptr on host VA < 48bits
|
||||
FEX_DEFAULT_VISIBILITY PtrCache* Steal48BitVA();
|
||||
FEX_DEFAULT_VISIBILITY std::vector<MemoryRegion> Steal48BitVA();
|
||||
}
|
||||
@@ -30,11 +30,11 @@ On AArch64 hosts the user **MUST** have an x86-64 RootFS [Creating a RootFS](#Ro
|
||||
See the [Source Outline](docs/SourceOutline.md) for more information.
|
||||
|
||||
### Building FEX
|
||||
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.org/index.php/Development:Setting_up_FEX).
|
||||
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
|
||||
|
||||
### RootFS generation
|
||||
AArch64 hosts require a rootfs for running applications.
|
||||
Follow the guide on the wiki page for seeing how to set up the rootfs from scratch
|
||||
https://wiki.fex-emu.org/index.php/Development:Setting_up_RootFS
|
||||
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
|
||||
|
||||

|
||||
@@ -59,7 +59,13 @@ SyscallArm64File = "/include/uapi/asm-generic/unistd.h"
|
||||
DefinitionRenameDict = {
|
||||
"pread64": "pread_64",
|
||||
"pwrite64": "pwrite_64",
|
||||
"prlimit64": "prlimit_64"
|
||||
"prlimit64": "prlimit_64",
|
||||
# Shm symbols conflict with termux defines and FEX's syscall token pasting.
|
||||
# Underscore at the start to avoid name collision
|
||||
"shmget": "_shmget",
|
||||
"shmctl": "_shmctl",
|
||||
"shmat": "_shmat",
|
||||
"shmdt": "_shmdt",
|
||||
}
|
||||
|
||||
Definitions_x64 = []
|
||||
|
||||
@@ -306,12 +306,14 @@ def GetRootFSPath():
|
||||
return _RootFSPath
|
||||
|
||||
def CheckRootFSInstallStatus():
|
||||
# Matches what is available on https://rootfs.fex-emu.org/file/fex-rootfs/RootFS_links_XXH3.txt
|
||||
# Matches what is available on https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json
|
||||
UbuntuVersionToRootFS = {
|
||||
"20.04": "Ubuntu_21_04.sqsh",
|
||||
"21.04": "Ubuntu_21_04.sqsh",
|
||||
"21.10": "Ubuntu_21_10.sqsh",
|
||||
"21.10": "Ubuntu_21_10.ero",
|
||||
"22.04": "Ubuntu_22_04.sqsh",
|
||||
"22.04": "Ubuntu_22_04.ero",
|
||||
}
|
||||
|
||||
return os.path.exists(GetRootFSPath() + UbuntuVersionToRootFS[GetDistro()[1]])
|
||||
|
||||
@@ -63,8 +63,15 @@ class Mode(Flag) :
|
||||
MODE_64 = 1
|
||||
|
||||
class HostFeatures(Flag) :
|
||||
ANY = 0
|
||||
AVX_ONLY = 1
|
||||
FEATURE_ANY = 0
|
||||
FEATURE_3DNOW = (1 << 0)
|
||||
FEATURE_SSE4A = (1 << 1)
|
||||
FEATURE_AVX = (1 << 2)
|
||||
FEATURE_RAND = (1 << 3)
|
||||
FEATURE_SHA = (1 << 4)
|
||||
FEATURE_CLZERO = (1 << 5)
|
||||
FEATURE_BMI1 = (1 << 6)
|
||||
FEATURE_BMI2 = (1 << 7)
|
||||
|
||||
RegStringLookup = {
|
||||
"NONE": Regs.REG_NONE,
|
||||
@@ -128,8 +135,14 @@ ModeStringLookup = {
|
||||
}
|
||||
|
||||
HostFeaturesLookup = {
|
||||
"ANY" : HostFeatures.ANY,
|
||||
"AVX" : HostFeatures.AVX_ONLY,
|
||||
"3DNOW" : HostFeatures.FEATURE_3DNOW,
|
||||
"SSE4A" : HostFeatures.FEATURE_SSE4A,
|
||||
"AVX" : HostFeatures.FEATURE_AVX,
|
||||
"RAND" : HostFeatures.FEATURE_RAND,
|
||||
"SHA" : HostFeatures.FEATURE_SHA,
|
||||
"CLZERO" : HostFeatures.FEATURE_CLZERO,
|
||||
"BMI1" : HostFeatures.FEATURE_BMI1,
|
||||
"BMI2" : HostFeatures.FEATURE_BMI2,
|
||||
}
|
||||
|
||||
def parse_hexstring(s):
|
||||
@@ -152,7 +165,7 @@ def parse_json(json_text, output_file):
|
||||
OptionIgnore = Regs.REG_NONE
|
||||
OptionABI = ABI.ABI_SYSTEMV
|
||||
OptionMode = Mode.MODE_64
|
||||
OptionHostFeatures = HostFeatures.ANY
|
||||
OptionHostFeatures = HostFeatures.FEATURE_ANY
|
||||
OptionStackSize = 4096
|
||||
OptionEntryPoint = 1
|
||||
OptionRegData = {}
|
||||
@@ -208,10 +221,15 @@ def parse_json(json_text, output_file):
|
||||
|
||||
if ("HOSTFEATURES" in json_object):
|
||||
data = json_object["HOSTFEATURES"]
|
||||
data = data.upper()
|
||||
if not (data in HostFeaturesLookup):
|
||||
sys.exit("Invalid host feature")
|
||||
OptionHostFeatures = HostFeaturesLookup[data]
|
||||
if not (type(data) is list):
|
||||
sys.exit("HostFeatures value must be list of features")
|
||||
|
||||
for data_key in data:
|
||||
data_key = data_key.upper()
|
||||
if not (data_key in HostFeaturesLookup):
|
||||
sys.exit("Invalid host feature")
|
||||
|
||||
OptionHostFeatures |= HostFeaturesLookup[data_key]
|
||||
|
||||
if ("STACKSIZE" in json_object):
|
||||
data = json_object["STACKSIZE"]
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <linux/limits.h>
|
||||
#include <optional>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace FEX {
|
||||
[[maybe_unused]]
|
||||
static
|
||||
std::string get_fdpath(int fd) {
|
||||
std::optional<std::string> get_fdpath(int fd) {
|
||||
char SymlinkPath[PATH_MAX];
|
||||
std::filesystem::path Path = std::filesystem::path("/proc/self/fd") / std::to_string(fd);
|
||||
int Result = readlinkat(AT_FDCWD, Path.c_str(), SymlinkPath, sizeof(SymlinkPath));
|
||||
@@ -18,8 +19,8 @@ std::string get_fdpath(int fd) {
|
||||
return std::string(SymlinkPath, Result);
|
||||
}
|
||||
|
||||
LOGMAN_MSG_A_FMT("Couldn't get symlink from /proc/self/fd/{}", fd);
|
||||
return {};
|
||||
// Not fatal if an FD doesn't point to a file
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,7 +101,12 @@ namespace FEXServerClient {
|
||||
}
|
||||
|
||||
std::string GetServerSocketFile() {
|
||||
return fmt::format("{}/{}.FEXServer.socket", std::filesystem::temp_directory_path().string(), ::geteuid());
|
||||
FEX_CONFIG_OPT(ServerSocketPath, SERVERSOCKETPATH);
|
||||
if (ServerSocketPath().empty()) {
|
||||
return fmt::format("{}/{}.FEXServer.socket", std::filesystem::temp_directory_path().string(), ::geteuid());
|
||||
}
|
||||
|
||||
return ServerSocketPath;
|
||||
}
|
||||
|
||||
int GetServerFD() {
|
||||
|
||||
+36
-28
@@ -2,8 +2,14 @@ add_subdirectory(LinuxSyscalls)
|
||||
|
||||
list(APPEND LIBS FEXCore Common)
|
||||
|
||||
if (TERMUX_BUILD)
|
||||
# Termux needs android-shmem to get the shm emulation library.
|
||||
list(APPEND LIBS android-shmem)
|
||||
endif()
|
||||
|
||||
add_executable(FEXLoader
|
||||
FEXLoader.cpp
|
||||
VDSO_Emulation.cpp
|
||||
AOT/AOTGenerator.cpp)
|
||||
|
||||
# Enable FEX APIs to be used by targets that use target_link_libraries on FEXLoader
|
||||
@@ -159,18 +165,20 @@ install(TARGETS FEXBash
|
||||
COMPONENT runtime
|
||||
)
|
||||
|
||||
add_executable(TestHarnessRunner TestHarnessRunner/HostRunner.cpp TestHarnessRunner.cpp)
|
||||
target_include_directories(TestHarnessRunner
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_link_libraries(TestHarnessRunner
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_executable(TestHarnessRunner TestHarnessRunner/HostRunner.cpp TestHarnessRunner.cpp)
|
||||
target_include_directories(TestHarnessRunner
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_link_libraries(TestHarnessRunner
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
)
|
||||
|
||||
# add_executable(UnitTestGenerator UnitTestGenerator.cpp)
|
||||
# target_include_directories(UnitTestGenerator
|
||||
@@ -184,19 +192,19 @@ target_link_libraries(TestHarnessRunner
|
||||
# )
|
||||
#
|
||||
|
||||
add_executable(IRLoader
|
||||
IRLoader.cpp
|
||||
)
|
||||
target_include_directories(IRLoader
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_link_libraries(IRLoader
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
fmt::fmt
|
||||
)
|
||||
|
||||
add_executable(IRLoader
|
||||
IRLoader.cpp
|
||||
)
|
||||
target_include_directories(IRLoader
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_link_libraries(IRLoader
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
fmt::fmt
|
||||
)
|
||||
endif()
|
||||
@@ -79,7 +79,9 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
return false;
|
||||
} else {
|
||||
auto Filename = FEX::get_fdpath(file.fd);
|
||||
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
|
||||
if (Filename.has_value()) {
|
||||
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename.value(), (prot & PROT_EXEC) != 0});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -207,6 +209,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
};
|
||||
|
||||
std::vector<LoadedSection> Sections;
|
||||
|
||||
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
|
||||
Args {args} {
|
||||
|
||||
@@ -424,9 +427,11 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
// On x86 only allows userspace to check for monitor and fs/gs base writing in CPL3
|
||||
//AuxVariables.emplace_back(auxv_t{26, 0}); // AT_HWCAP2
|
||||
|
||||
// we don't support vsyscall or vDSO so we don't set those
|
||||
// we don't support vsyscall so we don't set those
|
||||
//AuxVariables.emplace_back(auxv_t{32, 0}); // AT_SYSINFO - Entry point to syscall
|
||||
//AuxVariables.emplace_back(auxv_t{33, 0}); // AT_SYSINFO_EHDR - Address of the start of VDSO
|
||||
if (VDSOBase) {
|
||||
AuxVariables.emplace_back(auxv_t{33, reinterpret_cast<uint64_t>(VDSOBase)}); // AT_SYSINFO_EHDR - Address of the start of VDSO
|
||||
}
|
||||
}
|
||||
else {
|
||||
AuxVariables.emplace_back(auxv_t{4, 0x20}); // AT_PHENT
|
||||
@@ -638,6 +643,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
return ElfValid;
|
||||
}
|
||||
|
||||
void SetVDSOBase(void* Base) {
|
||||
VDSOBase = Base;
|
||||
}
|
||||
|
||||
constexpr static uint64_t BRK_SIZE = 8 * 1024 * 1024;
|
||||
constexpr static uint64_t STACK_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
@@ -650,6 +659,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
uint64_t ArgumentBackingSize{};
|
||||
uint64_t EnvironmentBackingSize{};
|
||||
uint64_t BaseOffset{};
|
||||
FEX_CONFIG_OPT(AdditionalArguments, ADDITIONALARGUMENTS);
|
||||
void* VDSOBase{};
|
||||
|
||||
FEX_CONFIG_OPT(AdditionalArguments, ADDITIONALARGUMENTS);
|
||||
};
|
||||
@@ -9,6 +9,7 @@ $end_info$
|
||||
#include "Common/ArgumentLoader.h"
|
||||
#include "Common/FEXServerClient.h"
|
||||
#include "ELFCodeLoader2.h"
|
||||
#include "VDSO_Emulation.h"
|
||||
#include "Tests/LinuxSyscalls/LinuxAllocator.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
|
||||
@@ -328,10 +329,11 @@ int main(int argc, char **argv, char **const envp) {
|
||||
}
|
||||
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.second);
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
|
||||
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
|
||||
FEXCore::Allocator::PtrCache *Base48Bit{};
|
||||
std::vector<FEXCore::Allocator::MemoryRegion> Base48Bit;
|
||||
|
||||
if (Loader.Is64BitMode()) {
|
||||
// Destroy the 48th bit if it exists
|
||||
@@ -388,6 +390,12 @@ int main(int argc, char **argv, char **const envp) {
|
||||
auto Mapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMmap, SyscallHandler.get());
|
||||
auto Unmapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMunmap, SyscallHandler.get());
|
||||
|
||||
if (Loader.Is64BitMode()) {
|
||||
// Load VDSO in to memory prior to mapping our ELFs.
|
||||
void* VDSOBase = FEX::VDSO::LoadVDSOThunks(Mapper);
|
||||
Loader.SetVDSOBase(VDSOBase);
|
||||
}
|
||||
|
||||
if (!Loader.MapMemory(Mapper, Unmapper)) {
|
||||
// failed to map
|
||||
LogMan::Msg::EFmt("Failed to map %d-bit elf file.", Loader.Is64BitMode() ? 64 : 32);
|
||||
@@ -404,6 +412,9 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
|
||||
FEXCore::Context::InitCore(CTX, Loader.DefaultRIP(), Loader.GetStackPointer());
|
||||
|
||||
// Pass in our VDSO thunks
|
||||
FEXCore::Context::AppendThunkDefinitions(CTX, FEX::VDSO::GetVDSOThunkDefinitions());
|
||||
|
||||
FEXCore::Context::ExitReason ShutdownReason = FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
|
||||
|
||||
// There might already be an exit handler, leave it installed
|
||||
|
||||
@@ -374,7 +374,27 @@ namespace FEX::HarnessHelper {
|
||||
}
|
||||
|
||||
bool Is64BitMode() const { return BaseConfig.OptionMode == 1; }
|
||||
bool RequiresAVX() const { return BaseConfig.OptionHostFeatures == 1; }
|
||||
|
||||
enum HostFeatures {
|
||||
FEATURE_ANY = 0,
|
||||
FEATURE_3DNOW = (1 << 0),
|
||||
FEATURE_SSE4A = (1 << 1),
|
||||
FEATURE_AVX = (1 << 2),
|
||||
FEATURE_RAND = (1 << 3),
|
||||
FEATURE_SHA = (1 << 4),
|
||||
FEATURE_CLZERO = (1 << 5),
|
||||
FEATURE_BMI1 = (1 << 6),
|
||||
FEATURE_BMI2 = (1 << 7),
|
||||
};
|
||||
|
||||
bool Requires3DNow() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_3DNOW; }
|
||||
bool RequiresSSE4A() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_SSE4A; }
|
||||
bool RequiresAVX() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_AVX; }
|
||||
bool RequiresRAND() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_RAND; }
|
||||
bool RequiresSHA() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_SHA; }
|
||||
bool RequiresCLZERO() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_CLZERO; }
|
||||
bool RequiresBMI1() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI1; }
|
||||
bool RequiresBMI2() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI2; }
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ConfigDumpGPRs, DUMPGPRS);
|
||||
@@ -506,7 +526,14 @@ namespace FEX::HarnessHelper {
|
||||
}
|
||||
|
||||
bool Is64BitMode() const { return Config.Is64BitMode(); }
|
||||
bool RequiresAVX() const { return Config.RequiresAVX(); }
|
||||
bool Requires3DNow() const { return Config.Requires3DNow(); }
|
||||
bool RequiresSSE4A() const { return Config.RequiresSSE4A(); }
|
||||
bool RequiresAVX() const { return Config.RequiresAVX(); }
|
||||
bool RequiresRAND() const { return Config.RequiresRAND(); }
|
||||
bool RequiresSHA() const { return Config.RequiresSHA(); }
|
||||
bool RequiresCLZERO() const { return Config.RequiresCLZERO(); }
|
||||
bool RequiresBMI1() const { return Config.RequiresBMI1(); }
|
||||
bool RequiresBMI2() const { return Config.RequiresBMI2(); }
|
||||
|
||||
private:
|
||||
constexpr static uint64_t STACK_SIZE = FHU::FEX_PAGE_SIZE;
|
||||
|
||||
@@ -204,10 +204,10 @@ enum Syscalls_Arm64 {
|
||||
SYSCALL_Arm64_semctl = 191,
|
||||
SYSCALL_Arm64_semtimedop = 192,
|
||||
SYSCALL_Arm64_semop = 193,
|
||||
SYSCALL_Arm64_shmget = 194,
|
||||
SYSCALL_Arm64_shmctl = 195,
|
||||
SYSCALL_Arm64_shmat = 196,
|
||||
SYSCALL_Arm64_shmdt = 197,
|
||||
SYSCALL_Arm64__shmget = 194,
|
||||
SYSCALL_Arm64__shmctl = 195,
|
||||
SYSCALL_Arm64__shmat = 196,
|
||||
SYSCALL_Arm64__shmdt = 197,
|
||||
SYSCALL_Arm64_socket = 198,
|
||||
SYSCALL_Arm64_socketpair = 199,
|
||||
SYSCALL_Arm64_bind = 200,
|
||||
|
||||
@@ -714,7 +714,7 @@ namespace FEX::EmulatedFile {
|
||||
dirfs != AT_FDCWD) {
|
||||
// Passed in a dirfd that isn't magic FDCWD
|
||||
// We need to get the path from the fd now
|
||||
Path = FEX::get_fdpath(dirfs);
|
||||
Path = FEX::get_fdpath(dirfs).value_or("");
|
||||
|
||||
if (pathname) {
|
||||
if (!Path.empty()) {
|
||||
|
||||
@@ -7,6 +7,7 @@ $end_info$
|
||||
|
||||
#include "Common/FDUtils.h"
|
||||
|
||||
#include "FEXCore/Config/Config.h"
|
||||
#include "Tests/LinuxSyscalls/FileManagement.h"
|
||||
#include "Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
@@ -222,16 +223,81 @@ void FileManager::LoadThunkDatabase(bool Global) {
|
||||
FileManager::FileManager(FEXCore::Context::Context *ctx)
|
||||
: EmuFD {ctx} {
|
||||
|
||||
bool LoadedThunkDatabase{};
|
||||
auto ThunkConfigFile = ThunkConfig();
|
||||
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs());
|
||||
|
||||
if (ThunkConfigFile.size()) {
|
||||
auto LoadThunksDB = [this, ThunkGuestPath](bool *LoadedThunkDatabase, json_t const* ThunksDB) {
|
||||
// If a thunks DB property exists then we pull in data from the thunks database
|
||||
// Load the initial thunks database
|
||||
if (LoadedThunkDatabase) {
|
||||
LoadThunkDatabase(true);
|
||||
LoadThunkDatabase(false);
|
||||
*LoadedThunkDatabase = true;
|
||||
}
|
||||
|
||||
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs());
|
||||
// Now load this property
|
||||
for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) {
|
||||
const char *LibraryName = json_getName(Item);
|
||||
int64_t LibraryEnabled = json_getInteger(Item);
|
||||
if (LibraryEnabled != 0) {
|
||||
// If the library is enabled then find it in the DB
|
||||
// Enable the overlay and all the dependencies in one go
|
||||
auto DBObject = ThunkDB.find(LibraryName);
|
||||
if (DBObject != ThunkDB.end() &&
|
||||
DBObject->second.Enabled == false) {
|
||||
|
||||
auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName;
|
||||
if (std::filesystem::exists(ThunkPath)) {
|
||||
for (auto Overlay : DBObject->second.Overlays) {
|
||||
// Direct full path in guest RootFS to our overlay file
|
||||
ThunkOverlays.emplace(Overlay, ThunkPath);
|
||||
}
|
||||
}
|
||||
DBObject->second.Enabled = true;
|
||||
// Now walk the dependencies and set them up as well
|
||||
// Make sure to enable each one as we go to remove circular dependencies
|
||||
std::function<void(std::unordered_set<std::string> &Depends)> InsertDependencies
|
||||
= [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set<std::string> &Depends) -> void {
|
||||
for (auto &Depend : Depends) {
|
||||
auto DBDepend = ThunkDB.find(Depend);
|
||||
if (DBDepend != ThunkDB.end() &&
|
||||
DBDepend->second.Enabled == false) {
|
||||
|
||||
auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName;
|
||||
if (std::filesystem::exists(ThunkPath)) {
|
||||
for (auto Overlay : DBDepend->second.Overlays) {
|
||||
// Direct full path in guest RootFS to our overlay file
|
||||
ThunkOverlays.emplace(Overlay, ThunkPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled, now walk this dependencies
|
||||
DBDepend->second.Enabled = true;
|
||||
InsertDependencies(DBDepend->second.Depends);
|
||||
}
|
||||
}
|
||||
};
|
||||
InsertDependencies(DBObject->second.Depends);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We try to load ThunksDB from {FEX global config, FEX user config, AppConfig Global, AppConfig Local, Defined ThunksConfig option}
|
||||
// This doesn't support the classic thunks interface.
|
||||
|
||||
std::vector<std::string> ConfigPaths {
|
||||
FEXCore::Config::GetConfigFileLocation(true),
|
||||
FEXCore::Config::GetConfigFileLocation(false),
|
||||
FEXCore::Config::GetApplicationConfig(AppConfigName(), true),
|
||||
FEXCore::Config::GetApplicationConfig(AppConfigName(), false),
|
||||
ThunkConfigFile,
|
||||
};
|
||||
|
||||
for (const auto &Path : ConfigPaths) {
|
||||
std::vector<char> FileData;
|
||||
if (LoadFile(FileData, ThunkConfigFile)) {
|
||||
FileData.push_back(0);
|
||||
|
||||
if (LoadFile(FileData, Path)) {
|
||||
JSON::JsonAllocator Pool {
|
||||
.PoolObject = {
|
||||
.init = JSON::PoolInit,
|
||||
@@ -240,101 +306,22 @@ FileManager::FileManager(FEXCore::Context::Context *ctx)
|
||||
};
|
||||
|
||||
json_t const *json = json_createWithPool(&FileData.at(0), &Pool.PoolObject);
|
||||
|
||||
json_t const* thunks = json_getProperty( json, "thunks" );
|
||||
if (thunks && json_getType(thunks) == JSON_OBJ) {
|
||||
json_t const* thunk;
|
||||
for( thunk = json_getChild( thunks ); thunk != 0; thunk = json_getSibling( thunk )) {
|
||||
char const* GuestThunk = json_getName( thunk );
|
||||
jsonType_t propertyType = json_getType( thunk );
|
||||
|
||||
if (propertyType == JSON_TEXT) {
|
||||
char const* RootFSLib = json_getValue( thunk );
|
||||
auto ThunkPath = ThunkGuestPath / GuestThunk;
|
||||
if (std::filesystem::exists(ThunkPath)) {
|
||||
ThunkOverlays.emplace(RootFSLib, ThunkPath);
|
||||
}
|
||||
} else if (propertyType == JSON_ARRAY) {
|
||||
json_t const* child;
|
||||
for( child = json_getChild( thunk ); child != 0; child = json_getSibling( child ) ) {
|
||||
if (json_getType( child ) == JSON_TEXT) {
|
||||
char const* RootFSLib = json_getValue( child );
|
||||
auto ThunkPath = ThunkGuestPath / GuestThunk;
|
||||
if (std::filesystem::exists(ThunkPath)) {
|
||||
ThunkOverlays.emplace(RootFSLib, ThunkPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_t const* ThunksDB = json_getProperty( json, "ThunksDB" );
|
||||
if (ThunksDB) {
|
||||
// If a thunks DB property exists then we pull in data from the thunks database
|
||||
// Load the initial thunks database
|
||||
LoadThunkDatabase(true);
|
||||
LoadThunkDatabase(false);
|
||||
|
||||
// Now load this property
|
||||
for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) {
|
||||
const char *LibraryName = json_getName(Item);
|
||||
int64_t LibraryEnabled = json_getInteger(Item);
|
||||
if (LibraryEnabled != 0) {
|
||||
// If the library is enabled then find it in the DB
|
||||
// Enable the overlay and all the dependencies in one go
|
||||
auto DBObject = ThunkDB.find(LibraryName);
|
||||
if (DBObject != ThunkDB.end() &&
|
||||
DBObject->second.Enabled == false) {
|
||||
|
||||
auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName;
|
||||
if (std::filesystem::exists(ThunkPath)) {
|
||||
for (auto Overlay : DBObject->second.Overlays) {
|
||||
// Direct full path in guest RootFS to our overlay file
|
||||
ThunkOverlays.emplace(Overlay, ThunkPath);
|
||||
}
|
||||
}
|
||||
DBObject->second.Enabled = true;
|
||||
// Now walk the dependencies and set them up as well
|
||||
// Make sure to enable each one as we go to remove circular dependencies
|
||||
std::function<void(std::unordered_set<std::string> &Depends)> InsertDependencies
|
||||
= [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set<std::string> &Depends) -> void {
|
||||
for (auto &Depend : Depends) {
|
||||
auto DBDepend = ThunkDB.find(Depend);
|
||||
if (DBDepend != ThunkDB.end() &&
|
||||
DBDepend->second.Enabled == false) {
|
||||
|
||||
auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName;
|
||||
if (std::filesystem::exists(ThunkPath)) {
|
||||
for (auto Overlay : DBDepend->second.Overlays) {
|
||||
// Direct full path in guest RootFS to our overlay file
|
||||
ThunkOverlays.emplace(Overlay, ThunkPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled, now walk this dependencies
|
||||
DBDepend->second.Enabled = true;
|
||||
InsertDependencies(DBDepend->second.Depends);
|
||||
}
|
||||
}
|
||||
};
|
||||
InsertDependencies(DBObject->second.Depends);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now clear the thunk database since we're loaded
|
||||
ThunkDB.clear();
|
||||
LoadThunksDB(&LoadedThunkDatabase, ThunksDB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false) {
|
||||
// Useful for debugging
|
||||
if (ThunkOverlays.size()) {
|
||||
LogMan::Msg::IFmt("Thunk Overlays:");
|
||||
for (const auto& [Overlay, ThunkPath] : ThunkOverlays) {
|
||||
LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath);
|
||||
}
|
||||
// Now clear the thunk database since we're loaded
|
||||
ThunkDB.clear();
|
||||
|
||||
if (false) {
|
||||
// Useful for debugging
|
||||
if (ThunkOverlays.size()) {
|
||||
LogMan::Msg::IFmt("Thunk Overlays:");
|
||||
for (const auto& [Overlay, ThunkPath] : ThunkOverlays) {
|
||||
LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -575,7 +562,7 @@ uint64_t FileManager::Readlinkat(int dirfd, const char *pathname, char *buf, siz
|
||||
dirfd != AT_FDCWD) {
|
||||
// Passed in a dirfd that isn't magic FDCWD
|
||||
// We need to get the path from the fd now
|
||||
Path = FEX::get_fdpath(dirfd);
|
||||
Path = FEX::get_fdpath(dirfd).value_or("");
|
||||
|
||||
if (pathname) {
|
||||
if (!Path.empty()) {
|
||||
|
||||
@@ -80,6 +80,7 @@ private:
|
||||
FEX_CONFIG_OPT(ThunkHostLibs, THUNKHOSTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG);
|
||||
FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME);
|
||||
uint32_t CurrentPID{};
|
||||
|
||||
void LoadThunkDatabase(bool Global);
|
||||
|
||||
@@ -44,11 +44,11 @@ public:
|
||||
FindPageRangePtr = &MemAllocator32Bit::FindPageRange;
|
||||
}
|
||||
}
|
||||
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override;
|
||||
int munmap(void *addr, size_t length) override;
|
||||
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override;
|
||||
uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override;
|
||||
uint64_t shmdt(const void* shmaddr) override;
|
||||
void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override;
|
||||
int Munmap(void *addr, size_t length) override;
|
||||
void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override;
|
||||
uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override;
|
||||
uint64_t Shmdt(const void* shmaddr) override;
|
||||
static constexpr bool SearchDown = true;
|
||||
|
||||
// PageAddr is a page already shifted to page index
|
||||
@@ -131,7 +131,7 @@ uint64_t MemAllocator32Bit::FindPageRange_TopDown(uint64_t Start, size_t Pages)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *MemAllocator32Bit::mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
||||
void *MemAllocator32Bit::Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
||||
std::scoped_lock<std::mutex> lk{AllocMutex};
|
||||
size_t PagesLength = FEXCore::AlignUp(length, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
|
||||
|
||||
@@ -282,7 +282,7 @@ restart:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MemAllocator32Bit::munmap(void *addr, size_t length) {
|
||||
int MemAllocator32Bit::Munmap(void *addr, size_t length) {
|
||||
std::scoped_lock<std::mutex> lk{AllocMutex};
|
||||
size_t PagesLength = FEXCore::AlignUp(length, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
|
||||
|
||||
@@ -327,7 +327,7 @@ int MemAllocator32Bit::munmap(void *addr, size_t length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
|
||||
void *MemAllocator32Bit::Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
|
||||
size_t OldPagesLength = FEXCore::AlignUp(old_size, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
|
||||
size_t NewPagesLength = FEXCore::AlignUp(new_size, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
|
||||
|
||||
@@ -404,7 +404,7 @@ void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_s
|
||||
// New Size is >= old size
|
||||
|
||||
// First, try and allocate a region the size of the new size
|
||||
void *MappedPtr = this->mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
void *MappedPtr = this->Mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
std::scoped_lock<std::mutex> lk{AllocMutex};
|
||||
if (FEX::HLE::HasSyscallError(MappedPtr)) {
|
||||
// Couldn't find a region that fit our space
|
||||
@@ -435,7 +435,7 @@ void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_s
|
||||
return reinterpret_cast<void*>(-errno);
|
||||
}
|
||||
|
||||
uint64_t MemAllocator32Bit::shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) {
|
||||
uint64_t MemAllocator32Bit::Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) {
|
||||
std::scoped_lock<std::mutex> lk{AllocMutex};
|
||||
|
||||
if (shmaddr != nullptr) {
|
||||
@@ -548,9 +548,9 @@ restart:
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) {
|
||||
uint64_t MemAllocator32Bit::Shmdt(const void* shmaddr) {
|
||||
std::scoped_lock<std::mutex> lk{AllocMutex};
|
||||
|
||||
|
||||
uint32_t AddrPage = reinterpret_cast<uint64_t>(shmaddr) >> FHU::FEX_PAGE_SHIFT;
|
||||
auto it = PageToShm.find(AddrPage);
|
||||
|
||||
@@ -567,7 +567,7 @@ uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) {
|
||||
|
||||
class MemAllocatorPassThrough final : public FEX::HLE::MemAllocator {
|
||||
public:
|
||||
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override {
|
||||
void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override {
|
||||
uint64_t Result = (uint64_t)::mmap(addr, length, prot, flags, fd, offset);
|
||||
if (Result == ~0ULL) {
|
||||
return reinterpret_cast<void*>(-errno);
|
||||
@@ -575,12 +575,12 @@ public:
|
||||
return reinterpret_cast<void*>(Result);
|
||||
}
|
||||
|
||||
int munmap(void *addr, size_t length) override {
|
||||
int Munmap(void *addr, size_t length) override {
|
||||
uint64_t Result = (uint64_t)::munmap(addr, length);
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override {
|
||||
void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override {
|
||||
uint64_t Result = (uint64_t)::mremap(old_address, old_size, new_size, flags, new_address);
|
||||
if (Result == ~0ULL) {
|
||||
return reinterpret_cast<void*>(-errno);
|
||||
@@ -588,7 +588,7 @@ public:
|
||||
return reinterpret_cast<void*>(Result);
|
||||
}
|
||||
|
||||
uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override {
|
||||
uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override {
|
||||
uint64_t Result = (uint64_t)::shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg);
|
||||
if (Result != ~0ULL) {
|
||||
*ResultAddress = Result;
|
||||
@@ -597,7 +597,7 @@ public:
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
uint64_t shmdt(const void* shmaddr) override {
|
||||
uint64_t Shmdt(const void* shmaddr) override {
|
||||
uint64_t Result = ::shmdt(shmaddr);
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ namespace FEX::HLE {
|
||||
class MemAllocator {
|
||||
public:
|
||||
virtual ~MemAllocator() = default;
|
||||
virtual void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0;
|
||||
virtual int munmap(void *addr, size_t length) = 0;
|
||||
virtual void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0;
|
||||
virtual uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0;
|
||||
virtual uint64_t shmdt(const void* shmaddr) = 0;
|
||||
virtual void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0;
|
||||
virtual int Munmap(void *addr, size_t length) = 0;
|
||||
virtual void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0;
|
||||
virtual uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0;
|
||||
virtual uint64_t Shmdt(const void* shmaddr) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<FEX::HLE::MemAllocator> Create32BitAllocator();
|
||||
|
||||
@@ -14,6 +14,8 @@ $end_info$
|
||||
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Types.h"
|
||||
#include "Tests/LinuxSyscalls/x64/Types.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/Context.h>
|
||||
@@ -58,6 +60,112 @@ namespace FEX::HLE {
|
||||
class SignalDelegator;
|
||||
SyscallHandler *_SyscallHandler{};
|
||||
|
||||
|
||||
template<bool IncrementOffset, typename T>
|
||||
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count) {
|
||||
std::vector<uint8_t> TmpVector(count);
|
||||
void *TmpPtr = reinterpret_cast<void*>(&TmpVector.at(0));
|
||||
|
||||
uint64_t Offset = 0;
|
||||
uint64_t TmpOffset = 0;
|
||||
// Copy the incoming structures to our temporary array
|
||||
while (Offset < count) {
|
||||
T *Incoming = (T*)(reinterpret_cast<uint64_t>(dirp) + Offset);
|
||||
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
|
||||
|
||||
if (!Incoming->d_reclen ||
|
||||
(Offset + Incoming->d_reclen) > count) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t NewRecLen = FEXCore::AlignUp(Incoming->d_reclen + (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Incoming)),
|
||||
alignof(decltype(Tmp->d_ino)));
|
||||
Tmp->d_ino = Incoming->d_ino;
|
||||
Tmp->d_off = Incoming->d_off;
|
||||
Tmp->d_reclen = NewRecLen;
|
||||
|
||||
// d_type is hidden at the very end of reclen
|
||||
Tmp->d_type = Incoming->d_name[Incoming->d_reclen - offsetof(T, d_name) - 1];
|
||||
|
||||
// This actually copies one more byte than the string of d_name
|
||||
// Copies a null byte for the string
|
||||
size_t CopySize = std::clamp<uint32_t>(Incoming->d_reclen - offsetof(T, d_name) - 1, 0U, count - Offset);
|
||||
memcpy(Tmp->d_name, Incoming->d_name, CopySize);
|
||||
|
||||
// We take up 8 more bytes of space
|
||||
TmpOffset += NewRecLen;
|
||||
Offset += Incoming->d_reclen;
|
||||
}
|
||||
|
||||
uint64_t Result = syscall(SYSCALL_DEF(getdents64),
|
||||
static_cast<uint64_t>(fd),
|
||||
TmpPtr,
|
||||
static_cast<uint64_t>(count));
|
||||
|
||||
// Now copy back in to the array we were given
|
||||
if (Result != -1) {
|
||||
// If the outgoing d_ino is smaller than the incoming d_ino from the kernel
|
||||
// Then we need to check for overflow before writing any of the data back
|
||||
if (sizeof(decltype(FEX::HLE::x64::linux_dirent_64::d_ino)) > sizeof(decltype(T::d_ino))) {
|
||||
uint64_t TmpOffset = 0;
|
||||
while (TmpOffset < Result) {
|
||||
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
|
||||
decltype(T::d_ino) Result_d_ino = Tmp->d_ino;
|
||||
|
||||
if (Result_d_ino != Tmp->d_ino) {
|
||||
// The resulting d_ino truncated, return error
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
TmpOffset += Tmp->d_reclen;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Offset = 0;
|
||||
uint64_t TmpOffset = 0;
|
||||
size_t OffsetIndex = 1;
|
||||
// With how the emulation occurs we will always return a smaller buffer than what was given to us
|
||||
while (TmpOffset < Result) {
|
||||
T *Outgoing = (T*)(reinterpret_cast<uint64_t>(dirp) + Offset);
|
||||
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
|
||||
|
||||
if (!Tmp->d_reclen) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t NewRecLen = FEXCore::AlignUp(Tmp->d_reclen - (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Outgoing)),
|
||||
alignof(decltype(Tmp->d_ino)));
|
||||
Outgoing->d_ino = Tmp->d_ino;
|
||||
|
||||
// 32-bit getdents can't safely handle d_off
|
||||
// A safe way of emulating this is to just use an incrementing offset from 1
|
||||
Outgoing->d_off = IncrementOffset ? OffsetIndex : Tmp->d_off;
|
||||
size_t OffsetOfName = offsetof(std::remove_reference<decltype(*Tmp)>::type, d_name);
|
||||
Outgoing->d_reclen = NewRecLen;
|
||||
|
||||
// Copies null character as well
|
||||
size_t NameLength = Tmp->d_reclen - OffsetOfName - 1;
|
||||
memcpy(Outgoing->d_name, Tmp->d_name, NameLength);
|
||||
|
||||
// Copy the hidden d_type flag
|
||||
Outgoing->d_name[Outgoing->d_reclen - offsetof(T, d_name) - 1] = Tmp->d_type;
|
||||
|
||||
TmpOffset += Tmp->d_reclen;
|
||||
// Outgoing is 5 bytes smaller
|
||||
Offset += NewRecLen;
|
||||
|
||||
++OffsetIndex;
|
||||
}
|
||||
Result = Offset;
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
template
|
||||
uint64_t GetDentsEmulation<false>(int, FEX::HLE::x64::linux_dirent*, uint32_t);
|
||||
|
||||
template
|
||||
uint64_t GetDentsEmulation<true>(int, FEX::HLE::x32::linux_dirent_32*, uint32_t);
|
||||
|
||||
static bool IsSupportedByInterpreter(std::string const &Filename) {
|
||||
// If it is a supported ELF then we can
|
||||
if (ELFLoader::ELFContainer::IsSupportedELF(Filename.c_str())) {
|
||||
|
||||
@@ -509,6 +509,8 @@ static bool HasSyscallError(const void* Result) {
|
||||
return HasSyscallError(reinterpret_cast<uintptr_t>(Result));
|
||||
}
|
||||
|
||||
template<bool IncrementOffset, typename T>
|
||||
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count);
|
||||
}
|
||||
|
||||
// Registers syscall for both 32bit and 64bit
|
||||
|
||||
@@ -18,14 +18,14 @@ namespace FEX::HLE {
|
||||
void RegisterSHM(FEX::HLE::SyscallHandler *Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame *Frame, key_t key, size_t size, int shmflg) -> uint64_t {
|
||||
uint64_t Result = shmget(key, size, shmflg);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
// XXX: shmid_ds is definitely not correct for 32-bit
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame *Frame, int shmid, int cmd, struct shmid_ds *buf) -> uint64_t {
|
||||
uint64_t Result = ::shmctl(shmid, cmd, buf);
|
||||
SYSCALL_ERRNO();
|
||||
|
||||
@@ -199,16 +199,17 @@ void SyscallHandler::TrackMmap(uintptr_t Base, uintptr_t Size, int Prot, int Fla
|
||||
fstat64(fd, &buf);
|
||||
MRID mrid {buf.st_dev, buf.st_ino};
|
||||
|
||||
auto [Iter, Inserted] = VMATracking.MappedResources.emplace(mrid, MappedResource {nullptr, nullptr, 0});
|
||||
Resource = &Iter->second;
|
||||
auto filename = FEX::get_fdpath(fd);
|
||||
|
||||
if (Inserted) {
|
||||
auto filename = FEX::get_fdpath(fd);
|
||||
if (filename.has_value()) {
|
||||
auto [Iter, Inserted] = VMATracking.MappedResources.emplace(mrid, MappedResource {nullptr, nullptr, 0});
|
||||
Resource = &Iter->second;
|
||||
|
||||
Resource->AOTIRCacheEntry = FEXCore::Context::LoadAOTIRCacheEntry(CTX, filename);
|
||||
Resource->Iterator = Iter;
|
||||
if (Inserted) {
|
||||
Resource->AOTIRCacheEntry = FEXCore::Context::LoadAOTIRCacheEntry(CTX, filename.value());
|
||||
Resource->Iterator = Iter;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (Flags & MAP_SHARED) {
|
||||
MRID mrid{SpecialDev::Anon, AnonSharedId++};
|
||||
|
||||
|
||||
@@ -583,75 +583,7 @@ namespace FEX::HLE::x32 {
|
||||
REGISTER_SYSCALL_IMPL_X32(ioctl, ioctl32);
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
|
||||
#ifdef SYS_getdents
|
||||
std::vector<uint8_t> TmpVector(count);
|
||||
void *TmpPtr = reinterpret_cast<void*>(&TmpVector.at(0));
|
||||
|
||||
// Copy the incoming structures to our temporary array
|
||||
for (uint64_t Offset = 0, TmpOffset = 0;
|
||||
Offset < count;) {
|
||||
linux_dirent_32 *Incoming = (linux_dirent_32*)(reinterpret_cast<uint64_t>(dirp) + Offset);
|
||||
linux_dirent *Tmp = (linux_dirent*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
|
||||
|
||||
if (!Incoming->d_reclen ||
|
||||
(Offset + Incoming->d_reclen) > count) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t NewRecLen = Incoming->d_reclen + (sizeof(linux_dirent) - sizeof(linux_dirent_32));
|
||||
Tmp->d_ino = Incoming->d_ino;
|
||||
Tmp->d_off = Incoming->d_off;
|
||||
Tmp->d_reclen = NewRecLen;
|
||||
|
||||
// This actually copies two more bytes than the string of d_name
|
||||
// Copies a null byte for the string
|
||||
// Copies a d_type flag that lives after the name
|
||||
size_t CopySize = std::clamp<uint32_t>(Incoming->d_reclen - offsetof(linux_dirent_32, d_name), 0U, count - Offset);
|
||||
memcpy(Tmp->d_name, Incoming->d_name, CopySize);
|
||||
|
||||
// We take up 8 more bytes of space
|
||||
TmpOffset += NewRecLen;
|
||||
Offset += Incoming->d_reclen;
|
||||
}
|
||||
|
||||
uint64_t Result = syscall(SYSCALL_DEF(getdents),
|
||||
static_cast<uint64_t>(fd),
|
||||
TmpPtr,
|
||||
static_cast<uint64_t>(count));
|
||||
|
||||
// Now copy back in to the array we were given
|
||||
if (Result != -1) {
|
||||
uint64_t Offset = 0;
|
||||
// With how the emulation occurs we will always return a smaller buffer than what was given to us
|
||||
for (uint64_t TmpOffset = 0, num = 0; TmpOffset < Result; ++num) {
|
||||
linux_dirent_32 *Outgoing = (linux_dirent_32*)(reinterpret_cast<uint64_t>(dirp) + Offset);
|
||||
linux_dirent *Tmp = (linux_dirent*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
|
||||
|
||||
if (!Tmp->d_reclen) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t NewRecLen = Tmp->d_reclen - (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Outgoing));
|
||||
Outgoing->d_ino = Tmp->d_ino;
|
||||
// If we pass d_off directly then we seem to encounter issues?
|
||||
Outgoing->d_off = num; //Tmp->d_off;
|
||||
size_t OffsetOfName = offsetof(std::remove_reference<decltype(*Tmp)>::type, d_name);
|
||||
Outgoing->d_reclen = NewRecLen;
|
||||
|
||||
// Copies null character and d_type flag as well
|
||||
memcpy(Outgoing->d_name, Tmp->d_name, Tmp->d_reclen - OffsetOfName);
|
||||
|
||||
TmpOffset += Tmp->d_reclen;
|
||||
// Outgoing is 8 bytes smaller
|
||||
Offset += NewRecLen;
|
||||
}
|
||||
Result = Offset;
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
#else
|
||||
// XXX: Emulate
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
return GetDentsEmulation<true>(fd, reinterpret_cast<FEX::HLE::x32::linux_dirent_32*>(dirp), count);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace FEX::HLE::x32 {
|
||||
void *x32SyscallHandler::GuestMmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
||||
LOGMAN_THROW_AA_FMT((length >> 32) == 0, "values must fit to 32 bits");
|
||||
|
||||
auto Result = (uint64_t)GetAllocator()->mmap((void*)addr, length, prot, flags, fd, offset);
|
||||
auto Result = (uint64_t)GetAllocator()->Mmap((void*)addr, length, prot, flags, fd, offset);
|
||||
|
||||
LOGMAN_THROW_AA_FMT((Result >> 32) == 0|| (Result >> 32) == 0xFFFFFFFF, "values must fit to 32 bits");
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace FEX::HLE::x32 {
|
||||
LOGMAN_THROW_AA_FMT((uintptr_t(addr) >> 32) == 0, "values must fit to 32 bits");
|
||||
LOGMAN_THROW_AA_FMT((length >> 32) == 0, "values must fit to 32 bits");
|
||||
|
||||
auto Result = GetAllocator()->munmap(addr, length);
|
||||
auto Result = GetAllocator()->Munmap(addr, length);
|
||||
|
||||
if (Result == 0) {
|
||||
FEX::HLE::_SyscallHandler->TrackMunmap((uintptr_t)addr, length);
|
||||
@@ -92,7 +92,7 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(mremap, [](FEXCore::Core::CpuStateFrame *Frame, void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) -> uint64_t {
|
||||
uint64_t Result = reinterpret_cast<uint64_t>(static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
|
||||
mremap(old_address, old_size, new_size, flags, new_address));
|
||||
Mremap(old_address, old_size, new_size, flags, new_address));
|
||||
|
||||
if (!FEX::HLE::HasSyscallError(Result)) {
|
||||
FEX::HLE::_SyscallHandler->TrackMremap((uintptr_t)old_address, old_size, new_size, flags, Result);
|
||||
@@ -111,11 +111,11 @@ namespace FEX::HLE::x32 {
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(shmat, [](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL_X32(_shmat, [](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
|
||||
// also implemented in ipc:OP_SHMAT
|
||||
uint32_t ResultAddr{};
|
||||
uint64_t Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
|
||||
shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg, &ResultAddr);
|
||||
Shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg, &ResultAddr);
|
||||
|
||||
if (!FEX::HLE::HasSyscallError(Result)) {
|
||||
FEX::HLE::_SyscallHandler->TrackShmat(shmid, ResultAddr, shmflg);
|
||||
@@ -126,11 +126,11 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(shmdt, [](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL_X32(_shmdt, [](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
|
||||
// also implemented in ipc:OP_SHMDT
|
||||
uint64_t Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
|
||||
shmdt(shmaddr);
|
||||
|
||||
Shmdt(shmaddr);
|
||||
|
||||
if (!FEX::HLE::HasSyscallError(Result)) {
|
||||
FEX::HLE::_SyscallHandler->TrackShmdt((uintptr_t)shmaddr);
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace FEX::HLE::x32 {
|
||||
case OP_SHMAT: {
|
||||
// also implemented in memory:shmat
|
||||
Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
|
||||
shmat(first, reinterpret_cast<const void*>(ptr), second, reinterpret_cast<uint32_t*>(third));
|
||||
Shmat(first, reinterpret_cast<const void*>(ptr), second, reinterpret_cast<uint32_t*>(third));
|
||||
if (!FEX::HLE::HasSyscallError(Result)) {
|
||||
FEX::HLE::_SyscallHandler->TrackShmat(first, *reinterpret_cast<uint32_t*>(third), second);
|
||||
}
|
||||
@@ -290,7 +290,7 @@ namespace FEX::HLE::x32 {
|
||||
case OP_SHMDT: {
|
||||
// also implemented in memory:shmdt
|
||||
Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
|
||||
shmdt(reinterpret_cast<void*>(ptr));
|
||||
Shmdt(reinterpret_cast<void*>(ptr));
|
||||
if (!FEX::HLE::HasSyscallError(Result)) {
|
||||
FEX::HLE::_SyscallHandler->TrackShmdt(ptr);
|
||||
}
|
||||
@@ -317,7 +317,7 @@ namespace FEX::HLE::x32 {
|
||||
else {
|
||||
buf = *shmun.buf32;
|
||||
}
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, &buf);
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, &buf);
|
||||
// IPC_SET sets the internal data structure that the kernel uses
|
||||
// No need to writeback
|
||||
break;
|
||||
@@ -326,7 +326,7 @@ namespace FEX::HLE::x32 {
|
||||
case SHM_STAT_ANY:
|
||||
case IPC_STAT: {
|
||||
struct shmid64_ds buf{};
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, &buf);
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, &buf);
|
||||
if (Result != -1) {
|
||||
if (IPC64) {
|
||||
*shmun.buf64 = buf;
|
||||
@@ -339,7 +339,7 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
case IPC_INFO: {
|
||||
struct shminfo si{};
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
|
||||
if (Result != -1) {
|
||||
if (IPC64) {
|
||||
*shmun.__buf64 = si;
|
||||
@@ -352,7 +352,7 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
case SHM_INFO: {
|
||||
struct shm_info si{};
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
|
||||
if (Result != -1) {
|
||||
// SHM_INFO doesn't follow IPC64 behaviour
|
||||
*shmun.__buf_info_32 = si;
|
||||
@@ -360,13 +360,13 @@ namespace FEX::HLE::x32 {
|
||||
break;
|
||||
}
|
||||
case SHM_LOCK:
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
|
||||
break;
|
||||
case SHM_UNLOCK:
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
|
||||
break;
|
||||
case IPC_RMID:
|
||||
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
|
||||
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -410,10 +410,10 @@ enum Syscalls_x86 {
|
||||
SYSCALL_x86_rseq = 386,
|
||||
SYSCALL_x86_semget = 393,
|
||||
SYSCALL_x86_semctl = 394,
|
||||
SYSCALL_x86_shmget = 395,
|
||||
SYSCALL_x86_shmctl = 396,
|
||||
SYSCALL_x86_shmat = 397,
|
||||
SYSCALL_x86_shmdt = 398,
|
||||
SYSCALL_x86__shmget = 395,
|
||||
SYSCALL_x86__shmctl = 396,
|
||||
SYSCALL_x86__shmat = 397,
|
||||
SYSCALL_x86__shmdt = 398,
|
||||
SYSCALL_x86_msgget = 399,
|
||||
SYSCALL_x86_msgsnd = 400,
|
||||
SYSCALL_x86_msgrcv = 401,
|
||||
|
||||
@@ -10,6 +10,7 @@ $end_info$
|
||||
#include "Tests/LinuxSyscalls/x64/Types.h"
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
@@ -196,16 +197,7 @@ namespace FEX::HLE::x64 {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
|
||||
#ifdef SYS_getdents
|
||||
uint64_t Result = syscall(SYSCALL_DEF(getdents),
|
||||
static_cast<uint64_t>(fd),
|
||||
reinterpret_cast<uint64_t>(dirp),
|
||||
static_cast<uint64_t>(count));
|
||||
SYSCALL_ERRNO();
|
||||
#else
|
||||
// XXX: Emulate
|
||||
return -EFAULT;
|
||||
#endif
|
||||
return GetDentsEmulation<false>(fd, reinterpret_cast<FEX::HLE::x64::linux_dirent*>(dirp), count);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace FEX::HLE::x64 {
|
||||
|
||||
bool Map32Bit = flags & FEX::HLE::X86_64_MAP_32BIT;
|
||||
if (Map32Bit) {
|
||||
Result = (uint64_t)Get32BitAllocator()->mmap(addr, length, prot,flags, fd, offset);
|
||||
Result = (uint64_t)Get32BitAllocator()->Mmap(addr, length, prot,flags, fd, offset);
|
||||
if (FEX::HLE::HasSyscallError(Result)) {
|
||||
errno = -Result;
|
||||
Result = -1;
|
||||
@@ -49,7 +49,7 @@ namespace FEX::HLE::x64 {
|
||||
int x64SyscallHandler::GuestMunmap(void *addr, uint64_t length) {
|
||||
uint64_t Result{};
|
||||
if (reinterpret_cast<uintptr_t>(addr) < 0x1'0000'0000ULL) {
|
||||
Result = Get32BitAllocator()->munmap(addr, length);
|
||||
Result = Get32BitAllocator()->Munmap(addr, length);
|
||||
|
||||
if (FEX::HLE::HasSyscallError(Result)) {
|
||||
errno = -Result;
|
||||
@@ -117,7 +117,7 @@ namespace FEX::HLE::x64 {
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(_shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
|
||||
uint64_t Result = reinterpret_cast<uint64_t>(shmat(shmid, shmaddr, shmflg));
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace FEX::HLE::x64 {
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(_shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
|
||||
uint64_t Result = ::shmdt(shmaddr);
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ enum Syscalls_x64 {
|
||||
SYSCALL_x64_msync = 26,
|
||||
SYSCALL_x64_mincore = 27,
|
||||
SYSCALL_x64_madvise = 28,
|
||||
SYSCALL_x64_shmget = 29,
|
||||
SYSCALL_x64_shmat = 30,
|
||||
SYSCALL_x64_shmctl = 31,
|
||||
SYSCALL_x64__shmget = 29,
|
||||
SYSCALL_x64__shmat = 30,
|
||||
SYSCALL_x64__shmctl = 31,
|
||||
SYSCALL_x64_dup = 32,
|
||||
SYSCALL_x64_dup2 = 33,
|
||||
SYSCALL_x64_pause = 34,
|
||||
@@ -74,7 +74,7 @@ enum Syscalls_x64 {
|
||||
SYSCALL_x64_semget = 64,
|
||||
SYSCALL_x64_semop = 65,
|
||||
SYSCALL_x64_semctl = 66,
|
||||
SYSCALL_x64_shmdt = 67,
|
||||
SYSCALL_x64__shmdt = 67,
|
||||
SYSCALL_x64_msgget = 68,
|
||||
SYSCALL_x64_msgsnd = 69,
|
||||
SYSCALL_x64_msgrcv = 70,
|
||||
|
||||
@@ -194,4 +194,37 @@ using __time_t = time_t;
|
||||
// Original definition in `arch/x86/include/uapi/asm/stat.h` for future excavation
|
||||
static_assert(std::is_trivial<FEX::HLE::x64::guest_stat>::value, "Needs to be trivial");
|
||||
static_assert(sizeof(FEX::HLE::x64::guest_stat) == 144, "Incorrect size");
|
||||
|
||||
// There is no public definition of this struct
|
||||
// Matches the definition of `struct linux_dirent` in fs/readdir.c
|
||||
struct
|
||||
FEX_ANNOTATE("fex-match")
|
||||
linux_dirent {
|
||||
uint64_t d_ino;
|
||||
uint64_t d_off;
|
||||
uint16_t d_reclen;
|
||||
char d_name[1];
|
||||
/* Has hidden null character and d_type */
|
||||
};
|
||||
static_assert(std::is_trivial<linux_dirent>::value, "Needs to be trivial");
|
||||
static_assert(offsetof(linux_dirent, d_ino) == 0, "Incorrect offset");
|
||||
static_assert(offsetof(linux_dirent, d_off) == 8, "Incorrect offset");
|
||||
static_assert(offsetof(linux_dirent, d_reclen) == 16, "Incorrect offset");
|
||||
static_assert(offsetof(linux_dirent, d_name) == 18, "Incorrect offset");
|
||||
static_assert(sizeof(linux_dirent) == 24, "Incorrect size");
|
||||
|
||||
// There is no public definition of this struct
|
||||
// Matches the definition of `struct linux_dirent64` in include/linux/dirent.h
|
||||
struct
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
linux_dirent_64 {
|
||||
uint64_t d_ino;
|
||||
uint64_t d_off;
|
||||
uint16_t d_reclen;
|
||||
uint8_t d_type;
|
||||
char d_name[];
|
||||
};
|
||||
static_assert(std::is_trivial<linux_dirent_64>::value, "Needs to be trivial");
|
||||
static_assert(sizeof(linux_dirent_64) == 19, "Incorrect size");
|
||||
}
|
||||
@@ -139,7 +139,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEX_CONFIG_OPT(Core, CORE);
|
||||
|
||||
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
|
||||
|
||||
|
||||
if (!Loader.Is64BitMode()) {
|
||||
// Setup our userspace allocator
|
||||
uint32_t KernelVersion = FEX::HLE::SyscallHandler::CalculateHostKernelVersion();
|
||||
@@ -159,6 +159,32 @@ int main(int argc, char **argv, char **const envp) {
|
||||
bool DidFault = false;
|
||||
bool SupportsAVX = false;
|
||||
FEXCore::Core::CPUState State;
|
||||
|
||||
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
|
||||
|
||||
auto CTX = FEXCore::Context::CreateNewContext();
|
||||
|
||||
FEXCore::Context::InitializeContext(CTX);
|
||||
|
||||
// Skip any tests that the host doesn't support features for
|
||||
auto HostFeatures = FEXCore::Context::GetHostFeatures(CTX);
|
||||
SupportsAVX = HostFeatures.SupportsAVX;
|
||||
|
||||
bool TestUnsupported =
|
||||
(!HostFeatures.Supports3DNow && Loader.Requires3DNow()) ||
|
||||
(!HostFeatures.SupportsSSE4A && Loader.RequiresSSE4A()) ||
|
||||
(!SupportsAVX && Loader.RequiresAVX()) ||
|
||||
(!HostFeatures.SupportsRAND && Loader.RequiresRAND()) ||
|
||||
(!HostFeatures.SupportsSHA && Loader.RequiresSHA()) ||
|
||||
(!HostFeatures.SupportsCLZERO && Loader.RequiresCLZERO()) ||
|
||||
(!HostFeatures.SupportsBMI1 && Loader.RequiresBMI1()) ||
|
||||
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2());
|
||||
|
||||
if (TestUnsupported) {
|
||||
FEXCore::Context::DestroyContext(CTX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Core != FEXCore::Config::CONFIG_CUSTOM) {
|
||||
jmp_buf LongJump{};
|
||||
int LongJumpVal{};
|
||||
@@ -175,12 +201,6 @@ int main(int argc, char **argv, char **const envp) {
|
||||
}, true);
|
||||
|
||||
// Run through FEX
|
||||
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
|
||||
|
||||
auto CTX = FEXCore::Context::CreateNewContext();
|
||||
|
||||
FEXCore::Context::InitializeContext(CTX);
|
||||
|
||||
auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX, SignalDelegation.get())
|
||||
: FEX::HLE::x32::CreateHandler(CTX, SignalDelegation.get(), std::move(Allocator));
|
||||
|
||||
@@ -195,14 +215,13 @@ int main(int argc, char **argv, char **const envp) {
|
||||
|
||||
FEXCore::Context::SetSignalDelegator(CTX, SignalDelegation.get());
|
||||
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
|
||||
|
||||
bool Result1 = FEXCore::Context::InitCore(CTX, Loader.DefaultRIP(), Loader.GetStackPointer());
|
||||
|
||||
if (!Result1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
SupportsAVX = FEXCore::Context::GetHostFeatures(CTX).SupportsAVX;
|
||||
|
||||
LongJumpVal = setjmp(LongJump);
|
||||
if (!LongJumpVal) {
|
||||
FEXCore::Context::RunUntilExit(CTX);
|
||||
@@ -212,8 +231,6 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEXCore::Context::GetCPUState(CTX, &State);
|
||||
|
||||
SyscallHandler.reset();
|
||||
FEXCore::Context::DestroyContext(CTX);
|
||||
FEXCore::Context::ShutdownStaticTables();
|
||||
} else {
|
||||
// Run as host
|
||||
SupportsAVX = true;
|
||||
@@ -227,12 +244,15 @@ int main(int argc, char **argv, char **const envp) {
|
||||
RunAsHost(SignalDelegation, Loader.DefaultRIP(), Loader.GetStackPointer(), &State);
|
||||
}
|
||||
|
||||
FEXCore::Context::DestroyContext(CTX);
|
||||
FEXCore::Context::ShutdownStaticTables();
|
||||
|
||||
bool Passed = !DidFault && Loader.CompareStates(&State, nullptr, SupportsAVX);
|
||||
|
||||
LogMan::Msg::IFmt("Faulted? {}", DidFault ? "Yes" : "No");
|
||||
LogMan::Msg::IFmt("Passed? {}", Passed ? "Yes" : "No");
|
||||
|
||||
|
||||
|
||||
SignalDelegation.reset();
|
||||
|
||||
FEXCore::Config::Shutdown();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/UContext.h>
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
@@ -152,9 +153,16 @@ public:
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
|
||||
memcpy(&OutState->xmm.avx.data[i], &_mcontext->fpregs->_xmm[i], sizeof(_mcontext->fpregs->_xmm[0]));
|
||||
}
|
||||
const auto* xstate = reinterpret_cast<FEXCore::x86_64::xstate*>(_mcontext->fpregs);
|
||||
const auto* reserved = &xstate->fpstate.sw_reserved;
|
||||
if (reserved->HasExtendedContext() && reserved->HasYMMH()) {
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&OutState->xmm.avx.data[i][2], &xstate->ymmh.ymmh_space[i],
|
||||
sizeof(xstate->ymmh.ymmh_space[0]));
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
|
||||
|
||||
const uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
||||
memcpy(&OutState->mm[(i + CurrentOffset) % 8], &_mcontext->fpregs->_st[i], sizeof(_mcontext->fpregs->_st[0]));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "VDSO_Emulation.h"
|
||||
#include "FEXCore/IR/IR.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace FEX::VDSO {
|
||||
using TimeType = decltype(::time)*;
|
||||
using GetTimeOfDayType = decltype(::gettimeofday)*;
|
||||
using ClockGetTimeType = decltype(::clock_gettime)*;
|
||||
using ClockGetResType = decltype(::clock_getres)*;
|
||||
using GetCPUType = decltype(::getcpu)*;
|
||||
|
||||
TimeType TimePtr = ::time;
|
||||
GetTimeOfDayType GetTimeOfDayPtr = ::gettimeofday;
|
||||
ClockGetTimeType ClockGetTimePtr = ::clock_gettime;
|
||||
ClockGetResType ClockGetResPtr = ::clock_getres;
|
||||
GetCPUType GetCPUPtr = ::getcpu;
|
||||
|
||||
static void time(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
time_t *a_0;
|
||||
uint64_t rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = TimePtr(args->a_0);
|
||||
}
|
||||
|
||||
static void gettimeofday(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
struct timeval *tv;
|
||||
struct timezone *tz;
|
||||
uint64_t rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = GetTimeOfDayPtr(args->tv, args->tz);
|
||||
}
|
||||
|
||||
static void clock_gettime(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
clockid_t clk_id;
|
||||
struct timespec *tp;
|
||||
uint64_t rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = ClockGetTimePtr(args->clk_id, args->tp);
|
||||
}
|
||||
|
||||
static void clock_getres(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
clockid_t clk_id;
|
||||
struct timespec *tp;
|
||||
uint64_t rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = ClockGetResPtr(args->clk_id, args->tp);
|
||||
}
|
||||
|
||||
static void getcpu(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
uint32_t *cpu;
|
||||
uint32_t *node;
|
||||
uint64_t rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = GetCPUPtr(args->cpu, args->node);
|
||||
}
|
||||
|
||||
void LoadHostVDSO() {
|
||||
void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
if (!vdso) {
|
||||
vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
}
|
||||
|
||||
if (!vdso) {
|
||||
// We couldn't load VDSO, fallback to C implementations. Which will still be faster than emulated libc versions.
|
||||
LogMan::Msg::IFmt("linux-vdso implementation falling back to libc. Consider enabling VDSO in your kernel.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto SymbolPtr = dlsym(vdso, "__vdso_time");
|
||||
if (SymbolPtr) {
|
||||
TimePtr = reinterpret_cast<TimeType>(SymbolPtr);
|
||||
}
|
||||
|
||||
SymbolPtr = dlsym(vdso, "__vdso_gettimeofday");
|
||||
if (SymbolPtr) {
|
||||
GetTimeOfDayPtr = reinterpret_cast<GetTimeOfDayType>(SymbolPtr);
|
||||
}
|
||||
|
||||
SymbolPtr = dlsym(vdso, "__vdso_clock_gettime");
|
||||
if (SymbolPtr) {
|
||||
ClockGetTimePtr = reinterpret_cast<ClockGetTimeType>(SymbolPtr);
|
||||
}
|
||||
|
||||
SymbolPtr = dlsym(vdso, "__vdso_clock_getres");
|
||||
if (SymbolPtr) {
|
||||
ClockGetResPtr = reinterpret_cast<ClockGetResType>(SymbolPtr);
|
||||
}
|
||||
|
||||
SymbolPtr = dlsym(vdso, "__vdso_getcpu");
|
||||
if (SymbolPtr) {
|
||||
GetCPUPtr = reinterpret_cast<GetCPUType>(SymbolPtr);
|
||||
}
|
||||
dlclose(vdso);
|
||||
}
|
||||
|
||||
static std::vector<FEXCore::IR::ThunkDefinition> VDSODefinitions = {
|
||||
{
|
||||
// sha256(libVDSO:time)
|
||||
{ 0x37, 0x63, 0x46, 0xb0, 0x79, 0x06, 0x5f, 0x9d, 0x00, 0xb6, 0x8d, 0xfd, 0x9e, 0x4a, 0x62, 0xcd, 0x1e, 0x6c, 0xcc, 0x22, 0xcd, 0xb2, 0xc0, 0x17, 0x7d, 0x42, 0x6a, 0x40, 0xd1, 0xeb, 0xfa, 0xe0 },
|
||||
&FEX::VDSO::time
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:gettimeofday)
|
||||
{ 0x77, 0x2a, 0xde, 0x1c, 0x13, 0x2d, 0xe9, 0x48, 0xaf, 0xe0, 0xba, 0xcc, 0x6a, 0x89, 0xff, 0xca, 0x4a, 0xdc, 0xd5, 0x63, 0x2c, 0xc5, 0x62, 0x8b, 0x5d, 0xde, 0x0b, 0x15, 0x35, 0xc6, 0xc7, 0x14 },
|
||||
&FEX::VDSO::gettimeofday
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:clock_gettime)
|
||||
{ 0x3c, 0x96, 0x9b, 0x2d, 0xc3, 0xad, 0x2b, 0x3b, 0x9c, 0x4e, 0x4d, 0xca, 0x1c, 0xe8, 0x18, 0x4a, 0x12, 0x8a, 0xe4, 0xc1, 0x56, 0x92, 0x73, 0xce, 0x65, 0x85, 0x5f, 0x65, 0x7e, 0x94, 0x26, 0xbe },
|
||||
&FEX::VDSO::clock_gettime
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:clock_getres)
|
||||
{ 0xe4, 0xa1, 0xf6, 0x23, 0x35, 0xae, 0xb7, 0xb6, 0xb0, 0x37, 0xc5, 0xc3, 0xa3, 0xfd, 0xbf, 0xa2, 0xa1, 0xc8, 0x95, 0x78, 0xe5, 0x76, 0x86, 0xdb, 0x3e, 0x6c, 0x54, 0xd5, 0x02, 0x60, 0xd8, 0x6d },
|
||||
&FEX::VDSO::clock_getres
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:getcpu)
|
||||
{ 0x39, 0x83, 0x39, 0x36, 0x0f, 0x68, 0xd6, 0xfc, 0xc2, 0x3a, 0x97, 0x11, 0x85, 0x09, 0xc7, 0x25, 0xbb, 0x50, 0x49, 0x55, 0x6b, 0x0c, 0x9f, 0x50, 0x37, 0xf5, 0x9d, 0xb0, 0x38, 0x58, 0x57, 0x12 },
|
||||
&FEX::VDSO::getcpu
|
||||
},
|
||||
};
|
||||
|
||||
void* LoadVDSOThunks(MapperFn Mapper) {
|
||||
void* VDSOBase{};
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
|
||||
|
||||
// Load VDSO if we can
|
||||
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()) / "libVDSO-guest.so";
|
||||
int VDSOFD = ::open(ThunkGuestPath.string().c_str(), O_RDONLY);
|
||||
|
||||
if (VDSOFD != -1) {
|
||||
// Get file size
|
||||
size_t VDSOSize = lseek(VDSOFD, 0, SEEK_END);
|
||||
|
||||
if (VDSOSize >= 4) {
|
||||
// Reset to beginning
|
||||
lseek(VDSOFD, 0, SEEK_SET);
|
||||
VDSOSize = FEXCore::AlignUp(VDSOSize, 4096);
|
||||
|
||||
// Map the VDSO file to memory
|
||||
VDSOBase = Mapper(nullptr, VDSOSize, PROT_READ, MAP_PRIVATE, VDSOFD, 0);
|
||||
|
||||
// Since we found our VDSO thunk library, find our host VDSO function implementations.
|
||||
LoadHostVDSO();
|
||||
|
||||
}
|
||||
close(VDSOFD);
|
||||
}
|
||||
|
||||
return VDSOBase;
|
||||
}
|
||||
|
||||
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions() {
|
||||
return VDSODefinitions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
namespace FEX::VDSO {
|
||||
using MapperFn = std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)>;
|
||||
void* LoadVDSOThunks(MapperFn Mapper);
|
||||
|
||||
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions();
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
@@ -441,7 +442,7 @@ namespace WebFileFetcher {
|
||||
FileType Type;
|
||||
};
|
||||
|
||||
const static std::string DownloadURL = "https://rootfs.fex-emu.org/file/fex-rootfs/RootFS_links.json";
|
||||
const static std::string DownloadURL = "https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json";
|
||||
|
||||
std::string DownloadToString(const std::string &URL) {
|
||||
std::string BigArgs =
|
||||
@@ -514,10 +515,14 @@ namespace WebFileFetcher {
|
||||
return &*alloc->json_objects->emplace(alloc->json_objects->end());
|
||||
}
|
||||
|
||||
std::vector<FileTargets> GetRootFSLinks() {
|
||||
std::optional<std::vector<FileTargets>> GetRootFSLinks() {
|
||||
// Decode the filetargets
|
||||
std::string Data = DownloadToString(DownloadURL);
|
||||
|
||||
if (Data.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
JsonAllocator Pool {
|
||||
.PoolObject = {
|
||||
.init = PoolInit,
|
||||
@@ -1121,7 +1126,14 @@ int main(int argc, char **argv, char **const envp) {
|
||||
}
|
||||
|
||||
if (AskForConfirmation(Question)) {
|
||||
auto Targets = WebFileFetcher::GetRootFSLinks();
|
||||
auto TargetReturn = WebFileFetcher::GetRootFSLinks();
|
||||
if (!TargetReturn.has_value()) {
|
||||
ExecWithInfo("Couldn't download rootfs list from the server. Try again in a minute or report on the fex-emu issue tracker.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto Targets = TargetReturn.value();
|
||||
|
||||
if (Targets.empty()) {
|
||||
ExecWithInfo("Couldn't parse rootfs definition URL.");
|
||||
return -1;
|
||||
|
||||
+92
-100
@@ -471,9 +471,11 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
return std::string { function_name } + "CBFN" + std::to_string(param_index);
|
||||
};
|
||||
|
||||
if (!output_filenames.thunks.empty()) {
|
||||
std::ofstream file(output_filenames.thunks);
|
||||
// Files used guest-side
|
||||
if (!output_filenames.guest.empty()) {
|
||||
std::ofstream file(output_filenames.guest);
|
||||
|
||||
// Guest->Host transition points for API functions
|
||||
file << "extern \"C\" {\n";
|
||||
for (auto& thunk : thunks) {
|
||||
const auto& function_name = thunk.function_name;
|
||||
@@ -481,13 +483,14 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
file << "MAKE_THUNK(" << libname << ", " << function_name << ", \"";
|
||||
bool first = true;
|
||||
for (auto c : sha256) {
|
||||
file << (first ? "" : ", ") << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c;
|
||||
file << (first ? "" : ", ") << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c << std::dec;
|
||||
first = false;
|
||||
}
|
||||
file << "\")\n";
|
||||
}
|
||||
file << "}\n";
|
||||
|
||||
// Guest->Host transition points for invoking runtime host-function pointers based on their signature
|
||||
for (auto type_it = funcptr_types.begin(); type_it != funcptr_types.end(); ++type_it) {
|
||||
auto* type = *type_it;
|
||||
std::string funcptr_signature = clang::QualType { type, 0 }.getAsString();
|
||||
@@ -506,50 +509,8 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
auto funcptr_idx = std::distance(funcptr_types.begin(), type_it);
|
||||
file << " MAKE_CALLBACK_THUNK(callback_" << funcptr_idx << ", " << funcptr_signature << ", \"" << cb_sha256_str << "\");\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_filenames.function_packs_public.empty()) {
|
||||
std::ofstream file(output_filenames.function_packs_public);
|
||||
|
||||
file << "extern \"C\" {\n";
|
||||
for (auto& data : thunked_api) {
|
||||
if (data.custom_guest_impl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& function_name = data.function_name;
|
||||
|
||||
file << "__attribute__((alias(\"fexfn_pack_" << function_name << "\"))) auto " << function_name << "(";
|
||||
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
|
||||
auto& type = data.param_types[idx];
|
||||
file << (idx == 0 ? "" : ", ") << format_decl(type, "a_" + std::to_string(idx));
|
||||
}
|
||||
file << ") -> " << data.return_type.getAsString() << ";\n";
|
||||
}
|
||||
|
||||
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
|
||||
bool empty = true;
|
||||
for (auto& symbol : thunked_api) {
|
||||
if (symbol.symtable_namespace == namespace_idx) {
|
||||
if (empty) {
|
||||
file << "static struct { const char* name; void (*fn)(); } " << namespaces[namespace_idx].name << "_symtable[] = {\n";
|
||||
empty = false;
|
||||
}
|
||||
file << " { \"" << symbol.function_name << "\", (void(*)())&" << symbol.function_name << " },\n";
|
||||
}
|
||||
}
|
||||
if (!empty) {
|
||||
file << " { nullptr, nullptr }\n";
|
||||
file << "};\n";
|
||||
}
|
||||
}
|
||||
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
if (!output_filenames.function_packs.empty()) {
|
||||
std::ofstream file(output_filenames.function_packs);
|
||||
|
||||
// Thunks-internal packing functions
|
||||
file << "extern \"C\" {\n";
|
||||
for (auto& data : thunks) {
|
||||
const auto& function_name = data.function_name;
|
||||
@@ -592,15 +553,89 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
file << "}\n";
|
||||
}
|
||||
file << "}\n";
|
||||
|
||||
// Publicly exports equivalent to symbols exported from the native guest library
|
||||
file << "extern \"C\" {\n";
|
||||
for (auto& data : thunked_api) {
|
||||
if (data.custom_guest_impl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& function_name = data.function_name;
|
||||
|
||||
file << "__attribute__((alias(\"fexfn_pack_" << function_name << "\"))) auto " << function_name << "(";
|
||||
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
|
||||
auto& type = data.param_types[idx];
|
||||
file << (idx == 0 ? "" : ", ") << format_decl(type, "a_" + std::to_string(idx));
|
||||
}
|
||||
file << ") -> " << data.return_type.getAsString() << ";\n";
|
||||
}
|
||||
file << "}\n";
|
||||
|
||||
// Symbol enumerators
|
||||
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
|
||||
const auto& ns = namespaces[namespace_idx];
|
||||
file << "#define FOREACH_" << ns.name << (ns.name.empty() ? "" : "_") << "SYMBOL(EXPAND) \\\n";
|
||||
for (auto& symbol : thunked_api) {
|
||||
if (symbol.symtable_namespace.value_or(0) == namespace_idx) {
|
||||
file << " EXPAND(" << symbol.function_name << ", \"TODO\") \\\n";
|
||||
}
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_filenames.function_unpacks.empty()) {
|
||||
std::ofstream file(output_filenames.function_unpacks);
|
||||
// Files used host-side
|
||||
if (!output_filenames.host.empty()) {
|
||||
std::ofstream file(output_filenames.host);
|
||||
|
||||
// Forward declarations for symbols loaded from the native host library
|
||||
for (auto& import : thunked_api) {
|
||||
const auto& function_name = import.function_name;
|
||||
const char* variadic_ellipsis = import.is_variadic ? ", ..." : "";
|
||||
file << "using fexldr_type_" << libname << "_" << function_name << " = auto " << "(" << format_function_params(import) << variadic_ellipsis << ") -> " << import.return_type.getAsString() << ";\n";
|
||||
file << "static fexldr_type_" << libname << "_" << function_name << " *fexldr_ptr_" << libname << "_" << function_name << ";\n";
|
||||
}
|
||||
|
||||
file << "extern \"C\" {\n";
|
||||
for (auto& thunk : thunks) {
|
||||
const auto& function_name = thunk.function_name;
|
||||
|
||||
// Generate stub callbacks
|
||||
for (auto& [cb_idx, cb] : thunk.callbacks) {
|
||||
if (cb.is_stub) {
|
||||
const char* variadic_ellipsis = cb.is_variadic ? ", ..." : "";
|
||||
auto cb_function_name = "fexfn_unpack_" + get_callback_name(function_name, cb_idx) + "_stub";
|
||||
file << "[[noreturn]] static " << cb.return_type.getAsString() << " "
|
||||
<< cb_function_name << "("
|
||||
<< format_function_params(cb) << variadic_ellipsis << ") {\n";
|
||||
file << " fprintf(stderr, \"FATAL: Attempted to invoke callback stub for " << function_name << "\\n\");\n";
|
||||
file << " std::abort();\n";
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Forward declarations for user-provided implementations
|
||||
if (thunk.custom_host_impl) {
|
||||
file << "static auto fexfn_impl_" << libname << "_" << function_name << "(";
|
||||
for (std::size_t idx = 0; idx < thunk.param_types.size(); ++idx) {
|
||||
// TODO: fex_guest_function_ptr for guest callbacks?
|
||||
auto& type = thunk.param_types[idx];
|
||||
|
||||
file << (idx == 0 ? "" : ", ");
|
||||
|
||||
auto cb = thunk.callbacks.find(idx);
|
||||
if (cb != thunk.callbacks.end() && cb->second.is_guest) {
|
||||
file << "fex_guest_function_ptr a_" << idx;
|
||||
} else {
|
||||
file << format_decl(type, "a_" + std::to_string(idx));
|
||||
}
|
||||
}
|
||||
// Using trailing return type as it makes handling function pointer returns much easier
|
||||
file << ") -> " << thunk.return_type.getAsString() << ";\n";
|
||||
}
|
||||
|
||||
// Packed argument structs used in fexfn_unpack_*
|
||||
auto GeneratePackedArgs = [&](const auto &function_name, const auto &thunk) -> std::string {
|
||||
std::string struct_name = "fexfn_packed_args_" + libname + "_" + function_name;
|
||||
file << "struct " << struct_name << " {\n";
|
||||
@@ -615,24 +650,9 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
file << "};\n";
|
||||
return struct_name;
|
||||
};
|
||||
|
||||
/* Generate stub callbacks */
|
||||
for (auto& [cb_idx, cb] : thunk.callbacks) {
|
||||
if (cb.is_stub) {
|
||||
const char* variadic_ellipsis = cb.is_variadic ? ", ..." : "";
|
||||
auto cb_function_name = "fexfn_unpack_" + get_callback_name(function_name, cb_idx) + "_stub";
|
||||
file << "[[noreturn]] static " << cb.return_type.getAsString() << " "
|
||||
<< cb_function_name << "("
|
||||
<< format_function_params(cb) << variadic_ellipsis << ") {\n";
|
||||
file << " fprintf(stderr, \"FATAL: Attempted to invoke callback stub for " << function_name << "\\n\");\n";
|
||||
file << " std::abort();\n";
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto struct_name = GeneratePackedArgs(function_name, thunk);
|
||||
|
||||
FunctionParams args = thunk;
|
||||
// Unpacking functions
|
||||
auto function_to_call = "fexldr_ptr_" + libname + "_" + function_name;
|
||||
if (thunk.custom_host_impl) {
|
||||
function_to_call = "fexfn_impl_" + libname + "_" + function_name;
|
||||
@@ -657,29 +677,27 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
}
|
||||
};
|
||||
|
||||
file << format_function_args(args, format_param);
|
||||
file << format_function_args(thunk, format_param);
|
||||
}
|
||||
file << ");\n";
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
if (!output_filenames.tab_function_unpacks.empty()) {
|
||||
std::ofstream file(output_filenames.tab_function_unpacks);
|
||||
|
||||
// Endpoints for Guest->Host invocation of API functions
|
||||
file << "static ExportEntry exports[] = {\n";
|
||||
for (auto& thunk : thunks) {
|
||||
const auto& function_name = thunk.function_name;
|
||||
auto sha256 = get_sha256(function_name);
|
||||
|
||||
file << "{(uint8_t*)\"";
|
||||
for (auto c : sha256) {
|
||||
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c;
|
||||
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c << std::dec;
|
||||
}
|
||||
file << "\", (void(*)(void *))&fexfn_unpack_" << libname << "_" << function_name << "}, // " << libname << ":" << function_name << "\n";
|
||||
}
|
||||
|
||||
// Endpoints for Guest->Host invocation of runtime host-function pointers
|
||||
for (auto& type : funcptr_types) {
|
||||
std::string mangled_name = clang::QualType { type, 0 }.getAsString();
|
||||
{
|
||||
@@ -693,11 +711,10 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
file << " {(uint8_t*)\"" << cb_sha256_str << "\", (void(*)(void *))&CallbackUnpack<" << mangled_name << ">::ForIndirectCall},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_filenames.ldr.empty()) {
|
||||
std::ofstream file(output_filenames.ldr);
|
||||
file << " { nullptr, nullptr }\n";
|
||||
file << "};\n";
|
||||
|
||||
// Symbol lookup from native host library
|
||||
file << "static void* fexldr_ptr_" << libname << "_so;\n";
|
||||
file << "extern \"C\" bool fexldr_init_" << libname << "() {\n";
|
||||
|
||||
@@ -714,32 +731,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
}
|
||||
file << " return true;\n";
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
if (!output_filenames.ldr_ptrs.empty()) {
|
||||
std::ofstream file(output_filenames.ldr_ptrs);
|
||||
|
||||
for (auto& import : thunked_api) {
|
||||
const auto& function_name = import.function_name;
|
||||
const char* variadic_ellipsis = import.is_variadic ? ", ..." : "";
|
||||
file << "using fexldr_type_" << libname << "_" << function_name << " = auto " << "(" << format_function_params(import) << variadic_ellipsis << ") -> " << import.return_type.getAsString() << ";\n";
|
||||
file << "static fexldr_type_" << libname << "_" << function_name << " *fexldr_ptr_" << libname << "_" << function_name << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_filenames.symbol_list.empty()) {
|
||||
std::ofstream file(output_filenames.symbol_list);
|
||||
|
||||
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
|
||||
const auto& ns = namespaces[namespace_idx];
|
||||
file << "#define FOREACH_" << ns.name << (ns.name.empty() ? "" : "_") << "SYMBOL(EXPAND) \\\n";
|
||||
for (auto& symbol : thunked_api) {
|
||||
if (symbol.symtable_namespace.value_or(0) == namespace_idx) {
|
||||
file << " EXPAND(" << symbol.function_name << ", \"TODO\") \\\n";
|
||||
}
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,19 +5,8 @@
|
||||
#include <string>
|
||||
|
||||
struct OutputFilenames {
|
||||
// Host
|
||||
std::string function_unpacks;
|
||||
std::string tab_function_unpacks;
|
||||
std::string ldr;
|
||||
std::string ldr_ptrs;
|
||||
|
||||
// Guest
|
||||
std::string thunks;
|
||||
std::string function_packs;
|
||||
std::string function_packs_public;
|
||||
|
||||
// Guest + Host
|
||||
std::string symbol_list;
|
||||
std::string host;
|
||||
std::string guest;
|
||||
};
|
||||
|
||||
class GenerateThunkLibsAction : public clang::ASTFrontendAction {
|
||||
|
||||
@@ -26,37 +26,27 @@ int main(int argc, char* argv[]) {
|
||||
std::cerr << "\nError: " << error << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char** const last_internal_arg = argv + argc;
|
||||
|
||||
// Process arguments before the "--" separator
|
||||
if (argc != 5) {
|
||||
print_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char** arg = argv + 1;
|
||||
const auto filename = *arg++;
|
||||
const std::string libname = *arg++;
|
||||
const std::string target_abi = *arg++;
|
||||
const std::string output_filename = *arg++;
|
||||
|
||||
// Iterate over generator targets (remaining arguments up to "--" separator)
|
||||
OutputFilenames output_filenames;
|
||||
while (arg < last_internal_arg) {
|
||||
auto target = std::string { *arg++ };
|
||||
auto out_filename = *arg++;
|
||||
if (target == "-function_unpacks") {
|
||||
output_filenames.function_unpacks = out_filename;
|
||||
} else if (target == "-tab_function_unpacks") {
|
||||
output_filenames.tab_function_unpacks = out_filename;
|
||||
} else if (target == "-ldr") {
|
||||
output_filenames.ldr = out_filename;
|
||||
} else if (target == "-ldr_ptrs") {
|
||||
output_filenames.ldr_ptrs = out_filename;
|
||||
} else if (target == "-thunks") {
|
||||
output_filenames.thunks = out_filename;
|
||||
} else if (target == "-function_packs") {
|
||||
output_filenames.function_packs = out_filename;
|
||||
} else if (target == "-function_packs_public") {
|
||||
output_filenames.function_packs_public = out_filename;
|
||||
} else if (target == "-symbol_list") {
|
||||
output_filenames.symbol_list = out_filename;
|
||||
} else {
|
||||
std::cerr << "Unrecognized generator target \"" << target << "\"\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (target_abi == "-host") {
|
||||
output_filenames.host = output_filename;
|
||||
} else if (target_abi == "-guest") {
|
||||
output_filenames.guest = output_filename;
|
||||
} else {
|
||||
std::cerr << "Unrecognized generator target ABI \"" << target_abi << "\"\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ClangTool Tool(*compile_db, { filename });
|
||||
|
||||
@@ -17,7 +17,7 @@ else()
|
||||
set(GENERATE_GUEST_INSTALL_TARGETS FALSE)
|
||||
endif()
|
||||
|
||||
# Syntax: generate(libxyz libxyz-interface.cpp generator-targets...)
|
||||
# Syntax: generate(libxyz libxyz-interface.cpp)
|
||||
# This defines a target and a custom command:
|
||||
# - custom command: Main build step that runs the thunk generator on the given interface definition
|
||||
# - libxyz-guest-deps: Interface target to read include directories from which are passed to libclang when parsing the interface definition
|
||||
@@ -25,36 +25,42 @@ function(generate NAME SOURCE_FILE)
|
||||
# Interface target for the user to add include directories
|
||||
add_library(${NAME}-guest-deps INTERFACE)
|
||||
target_include_directories(${NAME}-guest-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include")
|
||||
target_compile_definitions(${NAME}-guest-deps INTERFACE GUEST_THUNK_LIBRARY)
|
||||
# Shorthand for the include directories added after calling this function.
|
||||
# This is not evaluated directly, hence directories added after return are still picked up
|
||||
set(prop "$<TARGET_PROPERTY:${NAME}-guest-deps,INTERFACE_INCLUDE_DIRECTORIES>")
|
||||
set(compile_prop "$<TARGET_PROPERTY:${NAME}-guest-deps,INTERFACE_COMPILE_DEFINITIONS>")
|
||||
|
||||
# Run thunk generator for each of the given output files
|
||||
foreach(WHAT IN LISTS ARGN)
|
||||
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${NAME}")
|
||||
set(OUTFILE "${OUTFOLDER}/${WHAT}.inl")
|
||||
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen")
|
||||
set(OUTFILE "${OUTFOLDER}/thunkgen_guest_${NAME}.inl")
|
||||
|
||||
file(MAKE_DIRECTORY "${OUTFOLDER}")
|
||||
file(MAKE_DIRECTORY "${OUTFOLDER}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTFILE}"
|
||||
DEPENDS "${GENERATOR_EXE}"
|
||||
DEPENDS "${SOURCE_FILE}"
|
||||
COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 -DGUEST_THUNK_LIBRARY
|
||||
# Expand include directories to space-separated list of -isystem parameters
|
||||
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTFILE}"
|
||||
DEPENDS "${GENERATOR_EXE}"
|
||||
DEPENDS "${SOURCE_FILE}"
|
||||
COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-guest" "${OUTFILE}" -- -std=c++17
|
||||
# Expand compile definitions to space-separated list of -D parameters
|
||||
"$<$<BOOL:${compile_prop}>:;-D$<JOIN:${compile_prop},;-D>>"
|
||||
# Expand include directories to space-separated list of -isystem parameters
|
||||
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
|
||||
list(APPEND OUTPUTS "${OUTFILE}")
|
||||
endforeach()
|
||||
list(APPEND OUTPUTS "${OUTFILE}")
|
||||
set(GEN_${NAME} ${OUTPUTS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(add_guest_lib NAME)
|
||||
function(add_guest_lib NAME SONAME)
|
||||
set (SOURCE_FILE ../lib${NAME}/lib${NAME}_Guest.cpp)
|
||||
get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE)
|
||||
|
||||
set (SOURCE_LDS_FILE ../lib${NAME}/lib${NAME}_Guest.lds)
|
||||
get_filename_component(SOURCE_LDS_FILE_ABS "${SOURCE_LDS_FILE}" ABSOLUTE)
|
||||
|
||||
if (NOT EXISTS "${SOURCE_FILE_ABS}")
|
||||
set (SOURCE_FILE ../lib${NAME}/Guest.cpp)
|
||||
get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE)
|
||||
@@ -64,13 +70,35 @@ function(add_guest_lib NAME)
|
||||
endif()
|
||||
|
||||
add_library(${NAME}-guest ${TARGET_TYPE} ${SOURCE_FILE} ${GEN_lib${NAME}})
|
||||
target_include_directories(${NAME}-guest PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${NAME}")
|
||||
target_include_directories(${NAME}-guest PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/")
|
||||
target_compile_definitions(${NAME}-guest PRIVATE GUEST_THUNK_LIBRARY)
|
||||
target_link_libraries(${NAME}-guest PRIVATE lib${NAME}-guest-deps)
|
||||
|
||||
## Make signed overflow well defined 2's complement overflow
|
||||
target_compile_options(${NAME}-guest PRIVATE -fwrapv)
|
||||
|
||||
# Add linker script if set
|
||||
if (EXISTS "${SOURCE_LDS_FILE_ABS}")
|
||||
target_link_options(${NAME}-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../lib${NAME}/lib${NAME}_Guest.lds")
|
||||
endif()
|
||||
|
||||
# We need to override the soname for the linker.
|
||||
# Our guest thunk libraries are named `lib<Thunk>-guest`.
|
||||
# Once we override the loaded name, the guest is free to dlopen again by SONAME rather than filepath.
|
||||
# eg:
|
||||
# dlopen("libGL.so.1", RTLD_GLOBAL | RTLD_NOW); -> We override this `libGL.so.1` to `libGL-guest.so`
|
||||
# Later on in the program, it can do:
|
||||
# dlopen("libGL.so.1", RTLD_GLOBAL | RTLD_NOLOAD);
|
||||
# This second dlopen will only check to see if the previous load has made the library resident
|
||||
# Searching for SONAME in the process.
|
||||
#
|
||||
# Additionally, VDSO can only be opened by SONAME.
|
||||
# This means it will only ever open the handle with `dlopen("linux-vdso.so.1", RTLD_GLOBAL | RTLD_NOLOAD);
|
||||
# Note that this doesn't have a lib prefix, and also since it doesn't exist on the filesystem, it can never
|
||||
# Actually load from a path.
|
||||
target_link_options(${NAME}-guest PRIVATE "LINKER:-soname,${SONAME}")
|
||||
set_target_properties(${NAME}-guest PROPERTIES NO_SONAME ON)
|
||||
|
||||
if (GENERATE_GUEST_INSTALL_TARGETS)
|
||||
install(TARGETS ${NAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks/)
|
||||
endif()
|
||||
@@ -79,17 +107,17 @@ endfunction()
|
||||
#add_guest_lib(fex_malloc_loader)
|
||||
#target_link_libraries(fex_malloc_loader-guest PRIVATE dl)
|
||||
|
||||
#generate(libfex_malloc thunks function_packs function_packs_public)
|
||||
#generate(libfex_malloc)
|
||||
#add_guest_lib(fex_malloc)
|
||||
|
||||
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(asound)
|
||||
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp)
|
||||
add_guest_lib(asound "libasound.so.2")
|
||||
|
||||
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(EGL)
|
||||
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp)
|
||||
add_guest_lib(EGL "libEGL.so.1")
|
||||
|
||||
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp thunks function_packs function_packs_public symbol_list)
|
||||
add_guest_lib(GL)
|
||||
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp)
|
||||
add_guest_lib(GL "libGL.so.1")
|
||||
|
||||
# libGL must pull in libX11.so, so generate a placeholder libX11.so to link against
|
||||
add_library(X11 SHARED ../libX11/libX11_NativeGuest.cpp)
|
||||
@@ -97,59 +125,83 @@ target_link_libraries(GL-guest PRIVATE X11)
|
||||
|
||||
# disabled for now, headers are platform specific
|
||||
# find_package(SDL2 REQUIRED)
|
||||
# generate(libSDL2 thunks function_packs function_packs_public)
|
||||
# generate(libSDL2)
|
||||
# add_guest_lib(SDL2)
|
||||
# target_include_directories(SDL2-guest PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
# target_link_libraries(SDL2-guest PRIVATE GL)
|
||||
# target_link_libraries(SDL2-guest PRIVATE dl)
|
||||
|
||||
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(X11)
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(X11 REQUIRED x11)
|
||||
|
||||
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(Xext)
|
||||
string(REGEX MATCH "([0-9]*)\.([0-9]*)\.([0-9]*)" _ "${X11_VERSION}")
|
||||
set(X11_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(X11_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(X11_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
|
||||
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(Xrender)
|
||||
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp)
|
||||
add_guest_lib(X11 "libX11.so.6")
|
||||
|
||||
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(Xfixes)
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
|
||||
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp thunks function_packs function_packs_public symbol_list)
|
||||
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp)
|
||||
add_guest_lib(Xext "libXext.so.6")
|
||||
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
|
||||
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp)
|
||||
add_guest_lib(Xrender "libXrender.so.1")
|
||||
|
||||
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp)
|
||||
add_guest_lib(Xfixes "libXfixes.so.3")
|
||||
|
||||
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp)
|
||||
target_include_directories(libvulkan-guest-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/)
|
||||
add_guest_lib(vulkan)
|
||||
add_guest_lib(vulkan "libvulkan.so.1")
|
||||
|
||||
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb)
|
||||
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp)
|
||||
add_guest_lib(xcb "libxcb.so.1")
|
||||
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-dri2)
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
|
||||
add_guest_lib(xcb-dri2 "libxcb-dri2.so.0")
|
||||
|
||||
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-dri3)
|
||||
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp)
|
||||
add_guest_lib(xcb-dri3 "libxcb-dri3.so.0")
|
||||
|
||||
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-xfixes)
|
||||
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp)
|
||||
add_guest_lib(xcb-xfixes "libxcb-xfixes.so.0")
|
||||
|
||||
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-shm)
|
||||
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp)
|
||||
add_guest_lib(xcb-shm "libxcb-shm.so.0")
|
||||
|
||||
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-sync)
|
||||
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp)
|
||||
add_guest_lib(xcb-sync "libxcb-sync.so.1")
|
||||
|
||||
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-present)
|
||||
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp)
|
||||
add_guest_lib(xcb-present "libxcb-present.so.0")
|
||||
|
||||
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-randr)
|
||||
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp)
|
||||
add_guest_lib(xcb-randr "libxcb-randr.so.0")
|
||||
|
||||
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xcb-glx)
|
||||
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp)
|
||||
add_guest_lib(xcb-glx "libxcb-glx.so.0")
|
||||
|
||||
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp thunks function_packs function_packs_public)
|
||||
add_guest_lib(xshmfence)
|
||||
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp)
|
||||
add_guest_lib(xshmfence "libxshmfence.so.1")
|
||||
|
||||
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp thunks function_packs function_packs_public)
|
||||
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp)
|
||||
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/drm/)
|
||||
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/libdrm/)
|
||||
add_guest_lib(drm)
|
||||
add_guest_lib(drm "libdrm.so.2")
|
||||
|
||||
generate(libVDSO ${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_interface.cpp)
|
||||
add_guest_lib(VDSO "linux-vdso.so.1")
|
||||
# Can't use a stack protector because otherwise cross-compiling fails
|
||||
# Not necessary anyway because it only trampolines
|
||||
target_compile_options(VDSO-guest PRIVATE "-fno-stack-protector")
|
||||
target_link_options(VDSO-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_Guest.lds" "-nostdlib"
|
||||
"LINKER:--no-undefined" "LINKER:-z,max-page-size=4096" "LINKER:--hash-style=both")
|
||||
@@ -4,7 +4,7 @@ project(host-thunks)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set (HOSTLIBS_DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/fex-emu" CACHE PATH "global data directory")
|
||||
|
||||
# Syntax: generate(libxyz libxyz-interface.cpp generator-targets...)
|
||||
# Syntax: generate(libxyz libxyz-interface.cpp)
|
||||
# This defines two targets and a custom command:
|
||||
# - custom command: Main build step that runs the thunk generator on the given interface definition
|
||||
# - libxyz-interface: Target for IDE integration (making sure libxyz-interface.cpp shows up as a source file in the project tree)
|
||||
@@ -17,31 +17,37 @@ function(generate NAME SOURCE_FILE)
|
||||
# Shorthand for the include directories added after calling this function.
|
||||
# This is not evaluated directly, hence directories added after return are still picked up
|
||||
set(prop "$<TARGET_PROPERTY:${NAME}-deps,INTERFACE_INCLUDE_DIRECTORIES>")
|
||||
set(compile_prop "$<TARGET_PROPERTY:${NAME}-deps,INTERFACE_COMPILE_DEFINITIONS>")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
list(APPEND compile_prop _M_X86_64=1)
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
list(APPEND compile_prop _M_ARM_64=1)
|
||||
endif()
|
||||
|
||||
# Target for IDE integration
|
||||
add_library(${NAME}-interface EXCLUDE_FROM_ALL ${SOURCE_FILE})
|
||||
target_link_libraries(${NAME}-interface PRIVATE ${NAME}-deps)
|
||||
|
||||
# Run thunk generator for each of the given output files
|
||||
foreach(WHAT IN LISTS ARGN)
|
||||
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${NAME}")
|
||||
set(OUTFILE "${OUTFOLDER}/${WHAT}.inl")
|
||||
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen")
|
||||
set(OUTFILE "${OUTFOLDER}/thunkgen_host_${NAME}.inl")
|
||||
|
||||
file(MAKE_DIRECTORY "${OUTFOLDER}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTFILE}"
|
||||
DEPENDS "${SOURCE_FILE}"
|
||||
DEPENDS thunkgen
|
||||
COMMAND thunkgen "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17
|
||||
# Expand include directories to space-separated list of -isystem parameters
|
||||
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
file(MAKE_DIRECTORY "${OUTFOLDER}")
|
||||
|
||||
list(APPEND OUTPUTS "${OUTFILE}")
|
||||
endforeach()
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTFILE}"
|
||||
DEPENDS "${SOURCE_FILE}"
|
||||
DEPENDS thunkgen
|
||||
COMMAND thunkgen "${SOURCE_FILE}" "${NAME}" "-host" "${OUTFILE}" -- -std=c++17
|
||||
# Expand compile definitions to space-separated list of -D parameters
|
||||
"$<$<BOOL:${compile_prop}>:;-D$<JOIN:${compile_prop},;-D>>"
|
||||
# Expand include directories to space-separated list of -isystem parameters
|
||||
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
|
||||
list(APPEND OUTPUTS "${OUTFILE}")
|
||||
set(GEN_${NAME} ${OUTPUTS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -57,7 +63,7 @@ function(add_host_lib NAME)
|
||||
endif()
|
||||
|
||||
add_library(${NAME}-host SHARED ${SOURCE_FILE} ${GEN_lib${NAME}})
|
||||
target_include_directories(${NAME}-host PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${NAME}")
|
||||
target_include_directories(${NAME}-host PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/")
|
||||
target_link_libraries(${NAME}-host PRIVATE dl)
|
||||
target_link_libraries(${NAME}-host PRIVATE lib${NAME}-deps)
|
||||
## Make signed overflow well defined 2's complement overflow
|
||||
@@ -71,16 +77,16 @@ endfunction()
|
||||
|
||||
#add_host_lib(fex_malloc_symbols)
|
||||
|
||||
#generate(libfex_malloc function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
#generate(libfex_malloc)
|
||||
#add_host_lib(fex_malloc)
|
||||
|
||||
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp)
|
||||
add_host_lib(asound)
|
||||
|
||||
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp)
|
||||
add_host_lib(EGL)
|
||||
|
||||
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp)
|
||||
add_host_lib(GL)
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
@@ -88,57 +94,73 @@ target_link_libraries(GL-host PRIVATE OpenGL::GL)
|
||||
|
||||
# disabled for now, headers are platform specific
|
||||
# find_package(SDL2 REQUIRED)
|
||||
# generate(libSDL2 function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
# generate(libSDL2)
|
||||
# add_host_lib(SDL2)
|
||||
# target_include_directories(SDL2-host PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(X11 REQUIRED x11)
|
||||
|
||||
string(REGEX MATCH "([0-9]*)\.([0-9]*)\.([0-9]*)" _ "${X11_VERSION}")
|
||||
set(X11_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(X11_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(X11_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
|
||||
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp)
|
||||
add_host_lib(X11)
|
||||
|
||||
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
target_compile_definitions(libX11-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libX11-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libX11-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
|
||||
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp)
|
||||
add_host_lib(Xext)
|
||||
|
||||
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
target_compile_definitions(libXext-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libXext-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libXext-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
|
||||
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp)
|
||||
add_host_lib(Xrender)
|
||||
|
||||
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp)
|
||||
add_host_lib(Xfixes)
|
||||
|
||||
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs symbol_list)
|
||||
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp)
|
||||
target_include_directories(libvulkan-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/)
|
||||
add_host_lib(vulkan)
|
||||
|
||||
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp)
|
||||
add_host_lib(xcb)
|
||||
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
|
||||
add_host_lib(xcb-dri2)
|
||||
|
||||
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp)
|
||||
add_host_lib(xcb-dri3)
|
||||
|
||||
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp)
|
||||
add_host_lib(xcb-xfixes)
|
||||
|
||||
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp)
|
||||
add_host_lib(xcb-shm)
|
||||
|
||||
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp)
|
||||
add_host_lib(xcb-sync)
|
||||
|
||||
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp)
|
||||
add_host_lib(xcb-present)
|
||||
|
||||
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp)
|
||||
add_host_lib(xcb-randr)
|
||||
|
||||
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp)
|
||||
add_host_lib(xcb-glx)
|
||||
|
||||
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp)
|
||||
add_host_lib(xshmfence)
|
||||
|
||||
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
|
||||
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp)
|
||||
target_include_directories(libdrm-deps INTERFACE /usr/include/drm/)
|
||||
target_include_directories(libdrm-deps INTERFACE /usr/include/libdrm/)
|
||||
add_host_lib(drm)
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
|
||||
#define EXPORTS(name) \
|
||||
extern "C" { \
|
||||
ExportEntry* fexthunks_exports_##name(uintptr_t allocate, uintptr_t finalize) { \
|
||||
ExportEntry* fexthunks_exports_##name() { \
|
||||
if (!fexldr_init_##name()) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
@@ -102,9 +102,6 @@ struct GuestcallInfo {
|
||||
#elif defined(_M_ARM_64)
|
||||
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
|
||||
asm volatile("mov %0, x11" : "=r" (target_variable))
|
||||
#else
|
||||
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
|
||||
abort()
|
||||
#endif
|
||||
|
||||
template<typename>
|
||||
|
||||
@@ -13,9 +13,7 @@ $end_info$
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libEGL.inl"
|
||||
|
||||
typedef void voidFunc();
|
||||
|
||||
@@ -27,4 +25,4 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
|
||||
LOAD_LIB(libEGL)
|
||||
LOAD_LIB(libEGL)
|
||||
@@ -12,14 +12,6 @@ $end_info$
|
||||
#include "common/Host.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
#include "thunkgen_host_libEGL.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
EXPORTS(libEGL)
|
||||
EXPORTS(libEGL)
|
||||
@@ -24,10 +24,7 @@ $end_info$
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "symbol_list.inl"
|
||||
#include "thunkgen_guest_libGL.inl"
|
||||
|
||||
typedef void voidFunc();
|
||||
|
||||
|
||||
@@ -22,18 +22,10 @@ $end_info$
|
||||
|
||||
#include "common/Host.h"
|
||||
|
||||
#include "thunkgen_host_libGL.inl"
|
||||
|
||||
void* symbolFromGlXGetProcAddr(void*, const char* name) {
|
||||
return (void*)glXGetProcAddress((const GLubyte*)name);
|
||||
}
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
EXPORTS(libGL)
|
||||
@@ -19,9 +19,7 @@ $end_info$
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libSDL2.inl"
|
||||
|
||||
LOAD_LIB(libSDL2)
|
||||
|
||||
@@ -70,4 +68,4 @@ extern "C" {
|
||||
dlclose(lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,6 @@ $end_info$
|
||||
#include "common/Host.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
#include "thunkgen_host_libSDL2.inl"
|
||||
|
||||
EXPORTS(libSDL2)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
/*
|
||||
$info$
|
||||
tags: thunklibs|VDSO
|
||||
desc: Linux VDSO thunking
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
|
||||
#include <sched.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunkgen_guest_libVDSO.inl"
|
||||
|
||||
extern "C" {
|
||||
time_t __vdso_time(time_t *tloc) __attribute__((alias("fexfn_pack_time")));
|
||||
int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) __attribute__((alias("fexfn_pack_gettimeofday")));
|
||||
int __vdso_clock_gettime(clockid_t, struct timespec *) __attribute__((alias("fexfn_pack_clock_gettime")));
|
||||
int __vdso_clock_getres(clockid_t, struct timespec *) __attribute__((alias("fexfn_pack_clock_getres")));
|
||||
int __vdso_getcpu(uint32_t *, uint32_t *) __attribute__((alias("fexfn_pack_getcpu")));
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
SECTIONS {
|
||||
. = SIZEOF_HEADERS;
|
||||
.hash : { *(.hash) } :text
|
||||
.gnu.hash : { *(.gnu.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.dynamic : { *(.dynamic) } :text :dynamic
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
*(.data*)
|
||||
*(.sdata*)
|
||||
*(.got.plt) *(.got)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.bss*)
|
||||
*(.dynbss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
} :text
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.note)
|
||||
*(.note.gnu.property)
|
||||
*(.eh_frame_hdr)
|
||||
*(.eh_frame)
|
||||
*(.symtab)
|
||||
}
|
||||
}
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD FLAGS(PF_R | PF_X) FILEHDR PHDRS;
|
||||
dynamic PT_DYNAMIC FLAGS(PF_R);
|
||||
note PT_NOTE FLAGS(PF_R);
|
||||
}
|
||||
|
||||
VERSION {
|
||||
LINUX_2.6 {
|
||||
global:
|
||||
__vdso_time;
|
||||
time;
|
||||
__vdso_gettimeofday;
|
||||
gettimeofday;
|
||||
__vdso_clock_gettime;
|
||||
clock_gettime;
|
||||
__vdso_clock_getres;
|
||||
clock_getres;
|
||||
__vdso_getcpu;
|
||||
getcpu;
|
||||
local: *;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <common/GeneratorInterface.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
template<auto>
|
||||
struct fex_gen_config {
|
||||
};
|
||||
|
||||
template<> struct fex_gen_config<time> {};
|
||||
template<> struct fex_gen_config<gettimeofday> {};
|
||||
template<> struct fex_gen_config<clock_gettime> {};
|
||||
template<> struct fex_gen_config<clock_getres> {};
|
||||
template<> struct fex_gen_config<getcpu> {};
|
||||
@@ -26,10 +26,7 @@ $end_info$
|
||||
#include "common/Guest.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "thunks.inl"
|
||||
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libX11.inl"
|
||||
|
||||
|
||||
// Custom implementations //
|
||||
@@ -206,7 +203,10 @@ extern "C" {
|
||||
|
||||
MakeHostFunctionGuestCallable(ret->resource_alloc);
|
||||
MakeHostFunctionGuestCallable(ret->idlist_alloc);
|
||||
#if (X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
|
||||
// Doesn't exist on older X11
|
||||
MakeHostFunctionGuestCallable(ret->exit_handler);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -24,117 +24,307 @@ $end_info$
|
||||
#include <dlfcn.h>
|
||||
#include <utility>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "thunkgen_host_libX11.inl"
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
// This Variadic asm only works for one signature
|
||||
// ({uint32_t,uint64_t} a_0, size_t count, uint64_t *list)
|
||||
//
|
||||
// Variadic ABI for AArch64 (flat uint64_t):
|
||||
// Arguments 0-7 is in registers
|
||||
// 8+ stored on to stack
|
||||
//
|
||||
// The X11 functions we are calling need an additional nullptr passed in.
|
||||
// nullptr will be at the end of the list of generated stack items when called through this.
|
||||
// We will always generate a variadic frame of `count` objects + 1 for nullptr.
|
||||
//
|
||||
// Incoming:
|
||||
// x0 = XIM
|
||||
// x1 = count
|
||||
// x2 = array of 64-bit values
|
||||
// x3 = Function to call
|
||||
//
|
||||
// Outgoing:
|
||||
// x0: Ptr
|
||||
|
||||
__attribute__((naked))
|
||||
void *libX11_Variadic_u64(uint64_t a_0, size_t count, unsigned long *list, void *Func) {
|
||||
asm volatile(R"(
|
||||
# Move our function to x8, which will be unused
|
||||
mov x8, x3
|
||||
|
||||
# Move our list to x9, which will be unused
|
||||
mov x9, x2
|
||||
|
||||
# >6 means use stack callback
|
||||
cmp x1, 6
|
||||
b.gt .stack%=
|
||||
|
||||
# Setup a jump table
|
||||
adr x10, .zero%=
|
||||
adr x11, .jump_table%=
|
||||
ldrb w11, [x11, x1]
|
||||
add x10, x10, x11, lsl 2
|
||||
br x10
|
||||
|
||||
.zero%=:
|
||||
mov x1, 0
|
||||
br x8
|
||||
|
||||
.one%=:
|
||||
ldr x1, [x9, 0]
|
||||
mov x2, #0
|
||||
br x8
|
||||
|
||||
.two%=:
|
||||
ldp x1, x2, [x9, 0]
|
||||
mov x3, 0
|
||||
br x8
|
||||
|
||||
.three%=:
|
||||
ldp x1, x2, [x9, 0]
|
||||
ldr x3, [x9, 16]
|
||||
mov x4, 0
|
||||
br x8
|
||||
|
||||
.four%=:
|
||||
ldp x1, x2, [x9, 0]
|
||||
ldp x3, x4, [x9, 16]
|
||||
mov x5, 0
|
||||
br x8
|
||||
|
||||
.five%=:
|
||||
ldp x1, x2, [x9, 0]
|
||||
ldp x3, x4, [x9, 16]
|
||||
ldr x5, [x9, 32]
|
||||
mov x6, 0
|
||||
br x8
|
||||
|
||||
.six%=:
|
||||
ldp x1, x2, [x9, 0]
|
||||
ldp x3, x4, [x9, 16]
|
||||
ldp x5, x6, [x9, 32]
|
||||
mov x7, 0
|
||||
br x8
|
||||
|
||||
.stack%=:
|
||||
# Store LR and x28
|
||||
stp x28, x30, [sp, -16]!
|
||||
|
||||
# x8 = <arg ptr>
|
||||
# x0 = <arg im>
|
||||
# x1 = <count>
|
||||
# x9 = <list ptr>
|
||||
|
||||
# Stack objects
|
||||
# Count >= 7
|
||||
# Subtract 6 count objects
|
||||
# Leaves us at least 1 (nullptr)
|
||||
sub x1, x1, 6
|
||||
|
||||
# Round up to the nearest pair
|
||||
and x10, x1, 1
|
||||
add x10, x10, x1
|
||||
|
||||
# Multiply by eight to get the size of stack we need to create
|
||||
lsl x10, x10, 3
|
||||
|
||||
# Allocate stack space
|
||||
sub sp, sp, x10
|
||||
|
||||
# Store how much data we added to the stack in our callee saved register we stole
|
||||
mov x28, x10
|
||||
|
||||
# Subtract one member due to nullptr ender
|
||||
sub x10, x1, 1
|
||||
|
||||
# x11 - stack offset
|
||||
mov x11, sp
|
||||
|
||||
# x12 - load offset
|
||||
add x12, x9, (7 * 8)
|
||||
|
||||
cmp x10, 1
|
||||
b.eq .single%=
|
||||
b.lt .no_single%=
|
||||
|
||||
.load_pair%=:
|
||||
ldp x1, x2, [x12], 16
|
||||
stp x1, x2, [x11], 16
|
||||
sub x10, x10, 8
|
||||
cmp x10, 1
|
||||
b.gt .load_pair%=
|
||||
b.lt .no_single%=
|
||||
|
||||
# One variable at most
|
||||
.single%=:
|
||||
ldr x1, [x12]
|
||||
stp x1, xzr, [x11]
|
||||
b .top_reg_args%=
|
||||
|
||||
.no_single%=:
|
||||
# Need to store nullptr
|
||||
str xzr, [x11]
|
||||
|
||||
.top_reg_args%=:
|
||||
ldp x1, x2, [x9, 0]
|
||||
ldp x3, x4, [x9, 16]
|
||||
ldp x5, x6, [x9, 32]
|
||||
ldr x7, [x9, 48]
|
||||
|
||||
# Stack is setup going in to this
|
||||
blr x8
|
||||
|
||||
# Move stack back
|
||||
add sp, sp, x28
|
||||
ldp x28, x30, [sp], 16
|
||||
ret
|
||||
|
||||
.jump_table%=:
|
||||
.byte (.zero%= - .zero%=) >> 2
|
||||
.byte (.one%= - .zero%=) >> 2
|
||||
.byte (.two%= - .zero%=) >> 2
|
||||
.byte (.three%= - .zero%=) >> 2
|
||||
.byte (.four%= - .zero%=) >> 2
|
||||
.byte (.five%= - .zero%=) >> 2
|
||||
.byte (.six%= - .zero%=) >> 2
|
||||
)"
|
||||
::: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
_XIC *fexfn_impl_libX11_XCreateIC_internal(XIM a_0, size_t count, unsigned long *list) {
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XCreateIC(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
default:
|
||||
fprintf(stderr, "XCreateIC_internal FAILURE\n");
|
||||
return nullptr;
|
||||
}
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XCreateIC(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
case 8: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
|
||||
default:
|
||||
#ifdef _M_ARM_64
|
||||
return reinterpret_cast<_XIC*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, list, reinterpret_cast<void*>(fexldr_ptr_libX11_XCreateIC)));
|
||||
#else
|
||||
fprintf(stderr, "XCreateIC_internal FAILURE\n");
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
char* fexfn_impl_libX11_XGetICValues_internal(XIC a_0, size_t count, unsigned long *list) {
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XGetICValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
default:
|
||||
fprintf(stderr, "XGetICValues_internal FAILURE\n");
|
||||
abort();
|
||||
}
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XGetICValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
case 8: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
|
||||
default:
|
||||
#ifdef _M_ARM_64
|
||||
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, list, reinterpret_cast<void*>(fexldr_ptr_libX11_XGetICValues)));
|
||||
#else
|
||||
fprintf(stderr, "XGetICValues_internal FAILURE\n");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
char* fexfn_impl_libX11_XSetICValues_internal(XIC a_0, size_t count, unsigned long *list) {
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XSetICValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
default:
|
||||
fprintf(stderr, "XSetICValues_internal FAILURE\n");
|
||||
abort();
|
||||
}
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XSetICValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
case 8: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
|
||||
default:
|
||||
#ifdef _M_ARM_64
|
||||
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, list, reinterpret_cast<void*>(fexldr_ptr_libX11_XSetICValues)));
|
||||
#else
|
||||
fprintf(stderr, "XSetICValues_internal FAILURE\n");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
char* fexfn_impl_libX11_XGetIMValues_internal(XIM a_0, size_t count, void **list) {
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XGetIMValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
default:
|
||||
fprintf(stderr, "XGetIMValues_internal FAILURE\n");
|
||||
abort();
|
||||
}
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XGetIMValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
case 8: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
|
||||
default:
|
||||
#ifdef _M_ARM_64
|
||||
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, reinterpret_cast<unsigned long *>(list), reinterpret_cast<void*>(fexldr_ptr_libX11_XGetIMValues)));
|
||||
#else
|
||||
fprintf(stderr, "XGetIMValues_internal FAILURE\n");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
char* fexfn_impl_libX11_XSetIMValues_internal(XIM a_0, size_t count, void **list) {
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XSetIMValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
default:
|
||||
fprintf(stderr, "XSetIMValues_internal FAILURE\n");
|
||||
abort();
|
||||
}
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XSetIMValues(a_0, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
case 8: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
|
||||
default:
|
||||
#ifdef _M_ARM_64
|
||||
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, reinterpret_cast<unsigned long *>(list), reinterpret_cast<void*>(fexldr_ptr_libX11_XSetIMValues)));
|
||||
#else
|
||||
fprintf(stderr, "XSetIMValues_internal FAILURE\n");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
XVaNestedList fexfn_impl_libX11_XVaCreateNestedList_internal(int unused_arg, size_t count, void** list) {
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
default:
|
||||
fprintf(stderr, "XVaCreateNestedList_internal FAILURE\n");
|
||||
abort();
|
||||
}
|
||||
switch(count) {
|
||||
case 0: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, nullptr); break;
|
||||
case 1: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], nullptr); break;
|
||||
case 2: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], nullptr); break;
|
||||
case 3: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], nullptr); break;
|
||||
case 4: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], nullptr); break;
|
||||
case 5: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], nullptr); break;
|
||||
case 6: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
|
||||
case 7: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
|
||||
case 8: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
|
||||
default:
|
||||
#ifdef _M_ARM_64
|
||||
return reinterpret_cast<XVaNestedList>(libX11_Variadic_u64(unused_arg, count, reinterpret_cast<unsigned long *>(list), reinterpret_cast<void*>(fexldr_ptr_libX11_XVaCreateNestedList)));
|
||||
#else
|
||||
fprintf(stderr, "XVaCreateNestedList_internal FAILURE\n");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Status fexfn_impl_libX11_XInitThreadsInternal(uintptr_t, uintptr_t);
|
||||
|
||||
Status fexfn_impl_libX11__XReply(Display*, xReply*, int, Bool);
|
||||
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
static int (*ACTUAL_XInitDisplayLock_fn)(Display*) = nullptr;
|
||||
static int (*INTERNAL_XInitDisplayLock_fn)(Display*) = nullptr;
|
||||
|
||||
|
||||
@@ -472,6 +472,12 @@ template<> struct fex_gen_type<XID(Display*)> {}; // XDisplay::resource_alloc
|
||||
template<> struct fex_gen_type<void(Display*/*, char*, int*/)> {}; // XDisplay::lock_fns->lock_display
|
||||
|
||||
template<> struct fex_gen_type<void(_XDisplay*, XID*, int)> {}; // XDisplay::idlist_alloc
|
||||
|
||||
#if !(X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
|
||||
// Doesn't exist on older X11
|
||||
typedef void (*XIOErrorExitHandler)(Display*, void*);
|
||||
#endif
|
||||
|
||||
template<> struct fex_gen_type<std::remove_pointer_t<XIOErrorExitHandler>> {}; // XDisplay::exit_handler
|
||||
template<> struct fex_gen_config<XOpenDisplay> : fexgen::custom_guest_entrypoint {};
|
||||
|
||||
@@ -630,3 +636,53 @@ template<> struct fex_gen_config<_XInitImageFuncPtrs> : fexgen::custom_guest_ent
|
||||
template<> struct fex_gen_config<XVaCreateNestedList> {
|
||||
using uniform_va_type = void*;
|
||||
};
|
||||
|
||||
// Xlibint
|
||||
template<> struct fex_gen_config<_XError> {};
|
||||
template<> struct fex_gen_config<_XIOError> {};
|
||||
template<> struct fex_gen_config<_XGetHostname> {};
|
||||
template<> struct fex_gen_config<_XScreenOfWindow> {};
|
||||
template<> struct fex_gen_config<_XAsyncErrorHandler> {};
|
||||
template<> struct fex_gen_config<_XGetAsyncData> {};
|
||||
template<> struct fex_gen_config<_XEventsQueued> {};
|
||||
template<> struct fex_gen_config<_XEnq> {};
|
||||
template<> struct fex_gen_config<_XDeq> {};
|
||||
template<> struct fex_gen_config<_XUnknownWireEvent> {};
|
||||
template<> struct fex_gen_config<_XUnknownWireEventCookie> {};
|
||||
template<> struct fex_gen_config<_XUnknownCopyEventCookie> {};
|
||||
template<> struct fex_gen_config<_XWireToEvent> {};
|
||||
template<> struct fex_gen_config<_XDefaultWireError> {};
|
||||
template<> struct fex_gen_config<_XPollfdCacheInit> {};
|
||||
template<> struct fex_gen_config<_XPollfdCacheAdd> {};
|
||||
template<> struct fex_gen_config<_XPollfdCacheDel> {};
|
||||
template<> struct fex_gen_config<_XAllocID> {};
|
||||
template<> struct fex_gen_config<_XAllocIDs> {};
|
||||
template<> struct fex_gen_config<_XFreeExtData> {};
|
||||
template<> struct fex_gen_config<XESetPrintErrorValues> : fexgen::returns_guest_pointer {};
|
||||
template<> struct fex_gen_config<XESetWireToError> : fexgen::returns_guest_pointer {};
|
||||
template<> struct fex_gen_config<XESetBeforeFlush> : fexgen::returns_guest_pointer {};
|
||||
template<> struct fex_gen_config<_XRegisterInternalConnection> {};
|
||||
template<> struct fex_gen_config<_XUnregisterInternalConnection> {};
|
||||
template<> struct fex_gen_config<_XProcessInternalConnection> {};
|
||||
template<> struct fex_gen_config<_XTextHeight> {};
|
||||
template<> struct fex_gen_config<_XTextHeight16> {};
|
||||
template<> struct fex_gen_config<_XEventToWire> {};
|
||||
template<> struct fex_gen_config<_XF86LoadQueryLocaleFont> {};
|
||||
template<> struct fex_gen_config<_XProcessWindowAttributes> {};
|
||||
template<> struct fex_gen_config<_XDefaultError> {};
|
||||
template<> struct fex_gen_config<_XDefaultIOError> {};
|
||||
|
||||
#if !(X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
|
||||
// Doesn't exist on older X11
|
||||
extern void _XDefaultIOErrorExit(Display *dpy, void *user_data);
|
||||
#endif
|
||||
|
||||
template<> struct fex_gen_config<_XDefaultIOErrorExit> {};
|
||||
template<> struct fex_gen_config<_XSetClipRectangles> {};
|
||||
template<> struct fex_gen_config<_XGetWindowAttributes> {};
|
||||
template<> struct fex_gen_config<_XPutBackEvent> {};
|
||||
template<> struct fex_gen_config<_XFreeEventCookies> {};
|
||||
template<> struct fex_gen_config<_XStoreEventCookie> {};
|
||||
template<> struct fex_gen_config<_XFetchEventCookie> {};
|
||||
template<> struct fex_gen_config<_XCopyEventCookie> {};
|
||||
template<> struct fex_gen_config<xlocaledir> {};
|
||||
@@ -39,9 +39,6 @@ extern "C" {
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libXext.inl"
|
||||
|
||||
LOAD_LIB(libXext)
|
||||
@@ -41,15 +41,6 @@ extern "C" {
|
||||
#include "common/Host.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
#include "thunkgen_host_libXext.inl"
|
||||
|
||||
EXPORTS(libXext)
|
||||
|
||||
@@ -172,7 +172,14 @@ template<> struct fex_gen_config<_XF86LoadQueryLocaleFont> {};
|
||||
template<> struct fex_gen_config<_XProcessWindowAttributes> {};
|
||||
template<> struct fex_gen_config<_XDefaultError> {};
|
||||
template<> struct fex_gen_config<_XDefaultIOError> {};
|
||||
|
||||
#if !(X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
|
||||
// Doesn't exist on older X11
|
||||
extern void _XDefaultIOErrorExit(Display *dpy, void *user_data);
|
||||
#endif
|
||||
|
||||
template<> struct fex_gen_config<_XDefaultIOErrorExit> {};
|
||||
|
||||
template<> struct fex_gen_config<_XSetClipRectangles> {};
|
||||
template<> struct fex_gen_config<_XGetWindowAttributes> {};
|
||||
template<> struct fex_gen_config<_XPutBackEvent> {};
|
||||
|
||||
@@ -11,8 +11,6 @@ $end_info$
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libXfixes.inl"
|
||||
|
||||
LOAD_LIB(libXfixes)
|
||||
@@ -13,14 +13,6 @@ $end_info$
|
||||
#include "common/Host.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
EXPORTS(libXfixes)
|
||||
#include "thunkgen_host_libXfixes.inl"
|
||||
|
||||
EXPORTS(libXfixes)
|
||||
@@ -12,8 +12,6 @@ $end_info$
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libXrender.inl"
|
||||
|
||||
LOAD_LIB(libXrender)
|
||||
@@ -13,15 +13,6 @@ $end_info$
|
||||
#include "common/Host.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
EXPORTS(libXrender)
|
||||
#include "thunkgen_host_libXrender.inl"
|
||||
|
||||
EXPORTS(libXrender)
|
||||
@@ -16,8 +16,6 @@ extern "C" {
|
||||
#include "common/Guest.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libasound.inl"
|
||||
|
||||
LOAD_LIB(libasound)
|
||||
@@ -11,15 +11,6 @@ $end_info$
|
||||
#include "common/Host.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
EXPORTS(libasound)
|
||||
#include "thunkgen_host_libasound.inl"
|
||||
|
||||
EXPORTS(libasound)
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <common/GeneratorInterface.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alsa/version.h>
|
||||
|
||||
template<auto>
|
||||
struct fex_gen_config {
|
||||
@@ -8,6 +9,10 @@ struct fex_gen_config {
|
||||
};
|
||||
|
||||
template<> struct fex_gen_config<snd_asoundlib_version> {};
|
||||
#if SND_LIB_VERSION < ((1 << 16) | (2 << 8) | (6))
|
||||
// Exists on 1.2.6
|
||||
int snd_dlpath(char *path, size_t path_len, const char *name);
|
||||
#endif
|
||||
template<> struct fex_gen_config<snd_dlpath> {};
|
||||
template<> struct fex_gen_config<snd_dlopen> {};
|
||||
template<> struct fex_gen_config<snd_dlsym> {};
|
||||
|
||||
@@ -14,9 +14,7 @@ $end_info$
|
||||
#include "common/Guest.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libdrm.inl"
|
||||
|
||||
extern "C" {
|
||||
void FEX_malloc_free_on_host(void *Ptr) {
|
||||
|
||||
@@ -12,7 +12,7 @@ $end_info$
|
||||
#include <dlfcn.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "thunkgen_host_libdrm.inl"
|
||||
|
||||
static size_t fexfn_impl_libdrm_FEX_usable_size(void *a_0){
|
||||
return malloc_usable_size(a_0);
|
||||
@@ -22,14 +22,4 @@ static void fexfn_impl_libdrm_FEX_free_on_host(void *a_0){
|
||||
free(a_0);
|
||||
}
|
||||
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
EXPORTS(libdrm)
|
||||
|
||||
@@ -14,9 +14,7 @@ $end_info$
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libfex_malloc.inl"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@ $end_info$
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
|
||||
#include "function_unpacks.inl"
|
||||
#include "thunkgen_host_libfex_malloc.inl"
|
||||
|
||||
void fexfn_impl_libfex_malloc_fex_get_allocation_ptrs(AllocationPtrs *Ptrs);
|
||||
|
||||
@@ -183,13 +181,6 @@ void *(*__memalign_hook)(size_t alignment, size_t size) =
|
||||
fex_memalign;
|
||||
}
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
void fexfn_impl_libfex_malloc_fex_get_allocation_ptrs(AllocationPtrs *Ptrs) {
|
||||
*Ptrs = AllocationPointers;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,7 @@ $end_info$
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "symbol_list.inl"
|
||||
#include "thunkgen_guest_libvulkan.inl"
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ $end_info$
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "thunkgen_host_libvulkan.inl"
|
||||
|
||||
static bool SetupInstance{};
|
||||
static std::mutex SetupMutex{};
|
||||
@@ -135,14 +135,4 @@ static PFN_vkVoidFunction FEXFN_IMPL(vkGetInstanceProcAddr)(VkInstance a_0, cons
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
EXPORTS(libvulkan)
|
||||
@@ -15,9 +15,7 @@ $end_info$
|
||||
#include "common/Guest.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libxcb-dri2.inl"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -14,7 +14,7 @@ $end_info$
|
||||
#include <dlfcn.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "thunkgen_host_libxcb-dri2.inl"
|
||||
|
||||
static void fexfn_impl_libxcb_dri2_FEX_xcb_dri2_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1);
|
||||
|
||||
@@ -26,15 +26,6 @@ static void fexfn_impl_libxcb_dri2_FEX_free_on_host(void *a_0){
|
||||
free(a_0);
|
||||
}
|
||||
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
static void fexfn_impl_libxcb_dri2_FEX_xcb_dri2_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1){
|
||||
xcb_extension_t *ext{};
|
||||
if (strcmp(a_1->name, "DRI2") == 0) {
|
||||
|
||||
@@ -15,9 +15,7 @@ $end_info$
|
||||
#include "common/Guest.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libxcb-dri3.inl"
|
||||
|
||||
extern "C" {
|
||||
xcb_extension_t xcb_dri3_id = {
|
||||
|
||||
@@ -14,7 +14,7 @@ $end_info$
|
||||
#include <dlfcn.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "ldr_ptrs.inl"
|
||||
#include "thunkgen_host_libxcb-dri3.inl"
|
||||
|
||||
static void fexfn_impl_libxcb_dri3_FEX_xcb_dri3_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1);
|
||||
|
||||
@@ -26,16 +26,6 @@ static void fexfn_impl_libxcb_dri3_FEX_free_on_host(void *a_0){
|
||||
free(a_0);
|
||||
}
|
||||
|
||||
#include "function_unpacks.inl"
|
||||
|
||||
static ExportEntry exports[] = {
|
||||
#include "tab_function_unpacks.inl"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
||||
#include "ldr.inl"
|
||||
|
||||
static void fexfn_impl_libxcb_dri3_FEX_xcb_dri3_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1){
|
||||
xcb_extension_t *ext{};
|
||||
if (strcmp(a_1->name, "DRI3") == 0) {
|
||||
|
||||
@@ -15,9 +15,7 @@ $end_info$
|
||||
#include "common/Guest.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "thunks.inl"
|
||||
#include "function_packs.inl"
|
||||
#include "function_packs_public.inl"
|
||||
#include "thunkgen_guest_libxcb-glx.inl"
|
||||
|
||||
extern "C" {
|
||||
xcb_extension_t xcb_glx_id = {
|
||||
|
||||
Loaded 100 of 181 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user