LibraryForwarding: Add custom repacking tests for Vulkan-like scenarios

This commit is contained in:
Tony Wasserka committed 2025-03-12 17:35:07 +01:00
1 parent bdd351a42c
commit 585320093a
5 files changed
+174 -1

No files matched your search

+84 -1
View File
@@ -4,9 +4,10 @@ tags: thunklibs|fex_thunk_test
$end_info$
*/
#include <cstddef>
#include <dlfcn.h>
#include <unordered_map>
#include "common/Host.h"
#include "api.h"
@@ -29,4 +30,86 @@ bool fex_custom_repack_exit(guest_layout<CustomRepackedType>& to, const host_lay
return false;
}
template<StructType TypeIndex, typename Type>
static const TestBaseStruct* convert(const TestBaseStruct* source) {
// Using malloc here since no easily available type information is available at the time of destruction.
auto guest_next = reinterpret_cast<guest_layout<Type>*>((void*)source);
auto child_mem = (char*)aligned_alloc(alignof(host_layout<Type>), sizeof(host_layout<Type>));
auto child = new (child_mem) host_layout<Type> {*guest_next};
fex_custom_repack_entry(*child, *reinterpret_cast<guest_layout<Type>*>((void*)(source)));
return (const TestBaseStruct*)child;
}
template<StructType TypeIndex, typename Type>
static void convert_to_guest(void* into, const TestBaseStruct* from) {
auto typed_into = (guest_layout<Type>*)into;
auto oldNext = typed_into->data.Next;
*typed_into = to_guest(to_host_layout(*(Type*)from));
typed_into->data.Next = oldNext;
fex_custom_repack_exit(*typed_into, to_host_layout(*(Type*)from));
}
template<StructType TypeIndex, typename Type>
inline constexpr std::pair<StructType, std::pair<const TestBaseStruct* (*)(const TestBaseStruct*), void (*)(void*, const TestBaseStruct*)>> converters = {
TypeIndex,
{convert<TypeIndex, Type>, convert_to_guest<TypeIndex, Type>}};
static std::unordered_map<StructType, std::pair<const TestBaseStruct* (*)(const TestBaseStruct*), void (*)(void*, const TestBaseStruct*)>> next_handlers {
converters<StructType::Struct1, TestStruct1>,
converters<StructType::Struct2, TestStruct2>,
};
static void default_fex_custom_repack_entry(TestBaseStruct& into, const guest_layout<TestBaseStruct>* from) {
if (!from->data.Next.get_pointer()) {
into.Next = nullptr;
return;
}
auto typed_source = reinterpret_cast<const guest_layout<TestBaseStruct>*>(from->data.Next.get_pointer());
auto next_handler = next_handlers.at(StructType {typed_source->data.Type.data});
into.Next = (TestBaseStruct*)next_handler.first((const TestBaseStruct*)typed_source);
}
static void default_fex_custom_repack_reverse(guest_layout<TestBaseStruct>& into, const TestBaseStruct* from) {
auto NextHost = from->Next;
if (!NextHost) {
return;
}
auto next_handler = next_handlers.at(static_cast<StructType>(into.data.Next.get_pointer()->data.Type.data));
next_handler.second((void*)into.data.Next.get_pointer(), from->Next);
free((void*)NextHost);
}
#define CREATE_INFO_DEFAULT_CUSTOM_REPACK(name) \
void fex_custom_repack_entry(host_layout<name>& into, const guest_layout<name>& from) { \
default_fex_custom_repack_entry(*(TestBaseStruct*)&into.data, reinterpret_cast<const guest_layout<TestBaseStruct>*>(&from)); \
} \
\
bool fex_custom_repack_exit(guest_layout<name>& into, const host_layout<name>& from) { \
auto prev_next = into.data.Next; \
default_fex_custom_repack_reverse(*reinterpret_cast<guest_layout<TestBaseStruct>*>(&into), \
&reinterpret_cast<const TestBaseStruct&>(from.data)); \
into = to_guest(from); \
into.data.Next = prev_next; \
return true; \
}
CREATE_INFO_DEFAULT_CUSTOM_REPACK(TestStruct1)
CREATE_INFO_DEFAULT_CUSTOM_REPACK(TestStruct2)
void fex_custom_repack_entry(host_layout<TestBaseStruct>&, const guest_layout<TestBaseStruct>&) {
std::abort();
}
bool fex_custom_repack_exit(guest_layout<TestBaseStruct>&, const host_layout<TestBaseStruct>&) {
std::abort();
return false;
}
EXPORTS(libfex_thunk_test)
+33
View File
@@ -5,6 +5,7 @@
#pragma once
#include <cstdint>
#include <limits>
extern "C" {
@@ -73,4 +74,36 @@ enum DivType : uint8_t {};
enum DivType : uint32_t {};
#endif
int FunctionWithDivergentSignature(DivType, DivType, DivType, DivType);
/// Interfaces used to test Vulkan-like APIs
// Equivalent of VkStructureType
enum class StructType {
Struct1,
Struct2,
};
// Equivalent of VkBaseInStructure
struct TestBaseStruct {
TestBaseStruct* Next;
StructType Type;
};
// Equivalent of e.g. VkImageCreateInfo
struct TestStruct1 {
const void* Next;
StructType Type; // StructType::Struct1
uint8_t Data2;
uint8_t pad0[3];
int Data1;
};
struct TestStruct2 {
const void* Next;
StructType Type; // StructType::Struct2
int Data1;
};
int ReadData1(TestStruct1*, int depth);
}
+24
View File
@@ -1,5 +1,8 @@
#include "api.h"
#include <cstdio>
#include <cstddef>
extern "C" {
uint32_t GetDoubledValue(uint32_t input) {
@@ -59,4 +62,25 @@ int FunctionWithDivergentSignature(DivType a, DivType b, DivType c, DivType d) {
return ((uint8_t)a << 24) | ((uint8_t)b << 16) | ((uint8_t)c << 8) | (uint8_t)d;
}
int ReadData1(TestStruct1* data, int depth) {
auto* base = (TestBaseStruct*)data;
for (int i = 0; i != depth; ++i) {
if (!base) {
return -1;
}
base = base->Next;
}
if (!base) {
return -1;
}
switch (base->Type) {
case StructType::Struct1: return ((TestStruct1*)base)->Data1;
case StructType::Struct2: return ((TestStruct2*)base)->Data1;
default: return -2;
}
}
} // extern "C"
@@ -53,3 +53,13 @@ struct fex_gen_config<RanCustomRepack> {};
template<>
struct fex_gen_config<FunctionWithDivergentSignature> {};
template<>
struct fex_gen_config<&TestBaseStruct::Next> : fexgen::custom_repack {};
template<>
struct fex_gen_config<&TestStruct1::Next> : fexgen::custom_repack {};
template<>
struct fex_gen_config<&TestStruct2::Next> : fexgen::custom_repack {};
template<>
struct fex_gen_config<ReadData1> {};
@@ -36,6 +36,8 @@ struct Fixture {
GET_SYMBOL(RanCustomRepack);
GET_SYMBOL(FunctionWithDivergentSignature);
GET_SYMBOL(ReadData1);
};
TEST_CASE_METHOD(Fixture, "Trivial") {
@@ -89,3 +91,24 @@ TEST_CASE_METHOD(Fixture, "Assisted struct repacking") {
TEST_CASE_METHOD(Fixture, "Function signature with differing parameter sizes") {
CHECK(FunctionWithDivergentSignature(DivType {1}, DivType {2}, DivType {3}, DivType {4}) == 0x01020304);
}
// Test Vulkan-like linked lists
TEST_CASE_METHOD(Fixture, "Assisted repacking of linked lists") {
const int s2_data = 0xcddeeff;
TestStruct2 s2 {
.Next = nullptr,
.Type = StructType::Struct2,
.Data1 = s2_data,
};
const int s1_data = 0x1234567;
TestStruct1 s1 {
.Next = &s2,
.Type = StructType::Struct1,
.Data2 = 0xab,
.Data1 = s1_data,
};
CHECK(ReadData1(&s1, 0) == s1_data);
CHECK(ReadData1(&s1, 1) == s2_data);
}