mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 14:00:16 +02:00
Merge pull request #3422 from neobrain/feature_libfwd_wayland32
Library Forwarding: Add support for 32-bit Wayland
This commit is contained in:
9 files changed
+499
-103
No files matched your search
@@ -366,6 +366,13 @@ void AnalysisAction::ParseInterface(clang::ASTContext& context) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip pointers-to-structs passed through to the host in guest_layout.
|
||||
// This avoids pulling in member types that can't be processed.
|
||||
if (data.param_annotations[param_idx].is_passthrough &&
|
||||
param_type->isPointerType() && param_type->getPointeeType()->isStructureType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto check_struct_type = [&](const clang::Type* type) {
|
||||
if (type->isIncompleteType()) {
|
||||
throw report_error(type->getAsTagDecl()->getBeginLoc(), "Unannotated pointer with incomplete struct type; consider using an opaque_type annotation")
|
||||
|
||||
@@ -588,11 +588,11 @@ void GenerateThunkLibsAction::OnAnalysisComplete(clang::ASTContext& context) {
|
||||
if (!param_type->isPointerType() || !param_type->getPointeeType()->isStructureType()) {
|
||||
continue;
|
||||
}
|
||||
auto type = param_type->getPointeeType();
|
||||
if (!types.at(context.getCanonicalType(type.getTypePtr())).assumed_compatible && type_compat.at(context.getCanonicalType(type.getTypePtr())) == TypeCompatibility::None) {
|
||||
// TODO: Factor in "assume_compatible_layout" annotations here
|
||||
// That annotation should cause the type to be treated as TypeCompatibility::Full
|
||||
if (!thunk.param_annotations[param_idx].is_passthrough) {
|
||||
if (!thunk.param_annotations[param_idx].is_passthrough) {
|
||||
auto type = param_type->getPointeeType();
|
||||
if (!types.at(context.getCanonicalType(type.getTypePtr())).assumed_compatible && type_compat.at(context.getCanonicalType(type.getTypePtr())) == TypeCompatibility::None) {
|
||||
// TODO: Factor in "assume_compatible_layout" annotations here
|
||||
// That annotation should cause the type to be treated as TypeCompatibility::Full
|
||||
throw report_error(thunk.decl->getLocation(), "Unsupported parameter type %0").AddTaggedVal(param_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,9 +232,6 @@ if (BITNESS EQUAL 64)
|
||||
target_compile_definitions(libxcb-guest-deps INTERFACE -DXCB_VERSION_MINOR=${XCB_VERSION_MINOR})
|
||||
target_compile_definitions(libxcb-guest-deps INTERFACE -DXCB_VERSION_PATCH=${XCB_VERSION_PATCH})
|
||||
|
||||
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp)
|
||||
add_guest_lib(wayland-client "libwayland-client.so.0.20.0")
|
||||
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
|
||||
add_guest_lib(xcb-dri2 "libxcb-dri2.so.0")
|
||||
|
||||
@@ -268,6 +265,9 @@ if (BITNESS EQUAL 64)
|
||||
add_guest_lib(drm "libdrm.so.2")
|
||||
endif()
|
||||
|
||||
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp)
|
||||
add_guest_lib(wayland-client "libwayland-client.so.0.20.0")
|
||||
|
||||
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
|
||||
|
||||
@@ -163,9 +163,6 @@ foreach(GUEST_BITNESS IN LISTS BITNESS_LIST)
|
||||
target_compile_definitions(libxcb-${GUEST_BITNESS}-deps INTERFACE -DXCB_VERSION_MINOR=${XCB_VERSION_MINOR})
|
||||
target_compile_definitions(libxcb-${GUEST_BITNESS}-deps INTERFACE -DXCB_VERSION_PATCH=${XCB_VERSION_PATCH})
|
||||
|
||||
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp ${GUEST_BITNESS})
|
||||
add_host_lib(wayland-client ${GUEST_BITNESS})
|
||||
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp ${GUEST_BITNESS})
|
||||
add_host_lib(xcb-dri2 ${GUEST_BITNESS})
|
||||
|
||||
@@ -203,6 +200,9 @@ set (BITNESS_LIST "32;64")
|
||||
foreach(GUEST_BITNESS IN LISTS BITNESS_LIST)
|
||||
generate(libfex_thunk_test ${CMAKE_CURRENT_SOURCE_DIR}/../libfex_thunk_test/libfex_thunk_test_interface.cpp ${GUEST_BITNESS})
|
||||
add_host_lib(fex_thunk_test ${GUEST_BITNESS})
|
||||
|
||||
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp ${GUEST_BITNESS})
|
||||
add_host_lib(wayland-client ${GUEST_BITNESS})
|
||||
endforeach()
|
||||
|
||||
add_library(fex_thunk_test SHARED ../libfex_thunk_test/lib.cpp)
|
||||
|
||||
@@ -167,6 +167,14 @@ struct guest_layout<T*> {
|
||||
const guest_layout<T>* get_pointer() const {
|
||||
return reinterpret_cast<const guest_layout<T>*>(uintptr_t { data });
|
||||
}
|
||||
|
||||
T* force_get_host_pointer() {
|
||||
return reinterpret_cast<T*>(uintptr_t { data });
|
||||
}
|
||||
|
||||
const T* force_get_host_pointer() const {
|
||||
return reinterpret_cast<const T*>(uintptr_t { data });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -33,17 +33,11 @@ extern "C" const wl_interface wl_callback_interface {};
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunkgen_guest_libwayland-client.inl"
|
||||
|
||||
struct wl_proxy_private {
|
||||
wl_interface* interface;
|
||||
// Other data members omitted
|
||||
};
|
||||
|
||||
// See wayland-util.h for documentation on protocol message signatures
|
||||
template<char> struct ArgType;
|
||||
template<> struct ArgType<'s'> { using type = const char*; };
|
||||
@@ -56,37 +50,31 @@ template<> struct ArgType<'f'> { using type = wl_fixed_t; };
|
||||
template<> struct ArgType<'h'> { using type = int32_t; }; // fd?
|
||||
|
||||
template<char... Signature>
|
||||
static void* WaylandAllocateHostTrampolineForGuestListener(void (*callback)()) {
|
||||
static uint64_t WaylandAllocateHostTrampolineForGuestListener(void (*callback)()) {
|
||||
using cb = void(void*, wl_proxy*, typename ArgType<Signature>::type...);
|
||||
return (void*)AllocateHostTrampolineForGuestFunction((cb*)callback);
|
||||
return (uint64_t)(uintptr_t)(void*)AllocateHostTrampolineForGuestFunction((cb*)callback);
|
||||
}
|
||||
|
||||
#define WL_CLOSURE_MAX_ARGS 20
|
||||
|
||||
// Per-proxy list of callbacks set up via wl_proxy_add_listener.
|
||||
// These tables store the host-callable trampolines to the actual listener
|
||||
// callbacks provided by the guest application.
|
||||
// NOTE: There can only be one listener per proxy. Wayland will return an error
|
||||
// if wl_proxy_add_listener is called twice.
|
||||
// NOTE: Entries should be removed in wl_destroy_proxy. Since proxy wrappers do
|
||||
// not use their own listeners, wl_proxy_wrapper_destroy does not need to
|
||||
// be customized.
|
||||
static std::unordered_map<wl_proxy*, std::array<void*, WL_CLOSURE_MAX_ARGS>> proxy_listeners;
|
||||
|
||||
extern "C" int wl_proxy_add_listener(wl_proxy *proxy,
|
||||
void (**callback)(void), void *data) {
|
||||
auto interface = ((wl_proxy_private*)proxy)->interface;
|
||||
// Replace guest-provided callback table with host-callable function pointers
|
||||
// NOTE: A reference to this table is stored in the wl_proxy, so the data
|
||||
// must remain valid until the proxy is destroyed (or another listener
|
||||
// is added)
|
||||
delete[] (uint64_t*)wl_proxy_get_listener(proxy); // Delete previous substitute, if any
|
||||
auto host_callbacks = new uint64_t[WL_CLOSURE_MAX_ARGS];
|
||||
|
||||
// NOTE: This table must remain valid past the return of this function.
|
||||
auto& host_callbacks = proxy_listeners[proxy];
|
||||
|
||||
for (int i = 0; i < ((wl_proxy_private*)proxy)->interface->event_count; ++i) {
|
||||
auto signature_view = std::string_view { interface->events[i].signature };
|
||||
for (int i = 0; i < fex_wl_get_interface_event_count(proxy); ++i) {
|
||||
char event_signature[16];
|
||||
fex_wl_get_interface_event_signature(proxy, i, event_signature);
|
||||
auto signature2 = std::string_view { event_signature };
|
||||
|
||||
// A leading number indicates the minimum protocol version
|
||||
uint32_t since_version = 0;
|
||||
auto [ptr, res] = std::from_chars(signature_view.begin(), signature_view.end(), since_version, 10);
|
||||
std::string signature { ptr, &*signature_view.end() };
|
||||
auto [ptr, res] = std::from_chars(signature2.begin(), signature2.end(), since_version, 10);
|
||||
auto signature = std::string { signature2.substr(ptr - signature2.begin()) };
|
||||
|
||||
// ? just indicates that the argument may be null, so it doesn't change the signature
|
||||
signature.erase(std::remove(signature.begin(), signature.end(), '?'), signature.end());
|
||||
@@ -185,17 +173,18 @@ extern "C" int wl_proxy_add_listener(wl_proxy *proxy,
|
||||
// E.g. zwp_text_input_v3::preedit_string
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 'i', 'i'>(callback[i]);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown wayland signature descriptor \"%s\" for event \"%s\" in interface \"%s\"\n", signature.data(), interface->events[i].name, interface->name);
|
||||
fprintf(stderr, "TODO: Unknown wayland event signature descriptor %s\n", signature.data());
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
return fexfn_pack_wl_proxy_add_listener(proxy, (void(**)())host_callbacks.data(), data);
|
||||
return fexfn_pack_wl_proxy_add_listener(proxy, (void(**)())host_callbacks, data);
|
||||
}
|
||||
|
||||
extern "C" void wl_proxy_destroy(struct wl_proxy *proxy) {
|
||||
proxy_listeners.erase(proxy);
|
||||
return fexfn_pack_wl_proxy_destroy(proxy);
|
||||
extern "C" void wl_proxy_destroy(wl_proxy *proxy) {
|
||||
// Delete substitute callback table (if any), then the proxy itself
|
||||
delete[] (uint64_t*)wl_proxy_get_listener(proxy);
|
||||
fexfn_pack_wl_proxy_destroy(proxy);
|
||||
}
|
||||
|
||||
// Adapted from the Wayland sources
|
||||
@@ -266,18 +255,48 @@ extern "C" void wl_proxy_marshal(wl_proxy *proxy, uint32_t opcode, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, opcode);
|
||||
#ifdef IS_32BIT_THUNK
|
||||
// Must extract signature from host due to different data layout on 32-bit
|
||||
#error Not implemented
|
||||
#else
|
||||
wl_argument_from_va_list(((wl_proxy_private*)proxy)->interface->methods[opcode].signature,
|
||||
args, WL_CLOSURE_MAX_ARGS, ap);
|
||||
#endif
|
||||
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
|
||||
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
|
||||
char signature[64];
|
||||
fex_wl_get_method_signature(proxy, opcode, signature);
|
||||
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
|
||||
va_end(ap);
|
||||
|
||||
wl_proxy_marshal_array(proxy, opcode, args);
|
||||
}
|
||||
|
||||
extern "C" wl_proxy *wl_proxy_marshal_constructor(wl_proxy *proxy, uint32_t opcode,
|
||||
const wl_interface *interface, ...) {
|
||||
wl_argument args[WL_CLOSURE_MAX_ARGS];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, interface);
|
||||
// This is equivalent to reading ((wl_proxy_private*)proxy)->interface->methods[opcode].signature on 64-bit.
|
||||
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
|
||||
char signature[64];
|
||||
fex_wl_get_method_signature(proxy, opcode, signature);
|
||||
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
|
||||
va_end(ap);
|
||||
|
||||
return wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);
|
||||
}
|
||||
|
||||
extern "C" wl_proxy *wl_proxy_marshal_constructor_versioned(wl_proxy *proxy, uint32_t opcode,
|
||||
const wl_interface *interface, uint32_t version, ...) {
|
||||
wl_argument args[WL_CLOSURE_MAX_ARGS];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, version);
|
||||
// This is equivalent to reading ((wl_proxy_private*)proxy)->interface->methods[opcode].signature on 64-bit.
|
||||
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
|
||||
char signature[64];
|
||||
fex_wl_get_method_signature(proxy, opcode, signature);
|
||||
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
|
||||
va_end(ap);
|
||||
|
||||
return wl_proxy_marshal_array_constructor_versioned(proxy, opcode, args, interface, version);
|
||||
}
|
||||
|
||||
extern "C" wl_proxy *wl_proxy_marshal_flags(wl_proxy *proxy, uint32_t opcode,
|
||||
const wl_interface *interface,
|
||||
uint32_t version,
|
||||
@@ -286,13 +305,11 @@ extern "C" wl_proxy *wl_proxy_marshal_flags(wl_proxy *proxy, uint32_t opcode,
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, flags);
|
||||
#ifdef IS_32BIT_THUNK
|
||||
// Must extract signature from host due to different data layout on 32-bit
|
||||
#error Not implemented
|
||||
#else
|
||||
wl_argument_from_va_list(((wl_proxy_private*)proxy)->interface->methods[opcode].signature,
|
||||
args, WL_CLOSURE_MAX_ARGS, ap);
|
||||
#endif
|
||||
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
|
||||
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
|
||||
char signature[64];
|
||||
fex_wl_get_method_signature(proxy, opcode, signature);
|
||||
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
|
||||
va_end(ap);
|
||||
|
||||
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
|
||||
@@ -304,18 +321,23 @@ extern "C" wl_proxy *wl_proxy_marshal_flags(wl_proxy *proxy, uint32_t opcode,
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" void wl_log_set_handler_client(wl_log_func_t handler) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
|
||||
void OnInit() {
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_output_interface), "wl_output");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_pool_interface), "wl_shm_pool");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_pointer_interface), "wl_pointer");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_compositor_interface), "wl_compositor");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_interface), "wl_shm");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_registry_interface), "wl_registry");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_buffer_interface), "wl_buffer");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_seat_interface), "wl_seat");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_surface_interface), "wl_surface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_keyboard_interface), "wl_keyboard");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_callback_interface), "wl_callback");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_output_interface), "wl_output_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_pool_interface), "wl_shm_pool_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_pointer_interface), "wl_pointer_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_compositor_interface), "wl_compositor_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_interface), "wl_shm_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_registry_interface), "wl_registry_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_buffer_interface), "wl_buffer_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_seat_interface), "wl_seat_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_surface_interface), "wl_surface_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_keyboard_interface), "wl_keyboard_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_callback_interface), "wl_callback_interface");
|
||||
}
|
||||
|
||||
LOAD_LIB_INIT(libwayland-client, OnInit)
|
||||
@@ -5,6 +5,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -19,14 +20,272 @@ $end_info$
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include <ranges>
|
||||
|
||||
template<>
|
||||
struct guest_layout<wl_argument> {
|
||||
#ifdef IS_32BIT_THUNK
|
||||
using type = uint32_t;
|
||||
#else
|
||||
using type = wl_argument;
|
||||
#endif
|
||||
type data;
|
||||
|
||||
guest_layout& operator=(const wl_argument from) {
|
||||
#ifdef IS_32BIT_THUNK
|
||||
data = from.u;
|
||||
#else
|
||||
data = from;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#include "thunkgen_host_libwayland-client.inl"
|
||||
|
||||
struct wl_proxy_private {
|
||||
wl_interface* interface;
|
||||
// Other data members omitted
|
||||
};
|
||||
// Maps guest interface to host_interfaces
|
||||
static std::unordered_map<guest_layout<const wl_interface>*, wl_interface*> guest_to_host_interface;
|
||||
|
||||
static wl_interface* get_proxy_interface(wl_proxy* proxy) {
|
||||
// wl_proxy is a private struct, but its first member is the wl_interface pointer
|
||||
return *reinterpret_cast<wl_interface**>(proxy);
|
||||
}
|
||||
|
||||
static void assert_is_valid_host_interface(const wl_interface* interface) {
|
||||
// The 32-bit data layout of wl_interface differs from the 64-bit one due to
|
||||
// its pointer members. Our repacking code takes care of these differences.
|
||||
//
|
||||
// To ensure this indeed functions properly, a simple consistency check is
|
||||
// applied here: If any of the message counts are absurdly high, it means
|
||||
// data from pointer members leaked into other members.
|
||||
|
||||
if ((uint32_t)interface->method_count >= 0x1000 || (uint32_t)interface->event_count >= 0x1000) {
|
||||
fprintf(stderr, "ERROR: Expected %p to be a host wl_interface, but it's not\n", interface);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IS_32BIT_THUNK
|
||||
static void assert_is_valid_guest_interface(guest_layout<const wl_interface*> guest_interface) {
|
||||
// Consistency check for expected data layout.
|
||||
// See assert_is_valid_host_interface for details
|
||||
|
||||
const wl_interface* as_host_interface = (const wl_interface*)guest_interface.force_get_host_pointer();
|
||||
if ((uint32_t)as_host_interface->method_count < 0x1000 && (uint32_t)as_host_interface->event_count < 0x1000) {
|
||||
fprintf(stderr, "ERROR: Expected %p to be a guest wl_interface, but it's not\n", guest_interface.force_get_host_pointer());
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void repack_guest_wl_interface_to_host(guest_layout<const wl_interface*> guest_interface_ptr, wl_interface* host_interface) {
|
||||
auto& guest_interface = *guest_interface_ptr.get_pointer();
|
||||
static_assert(sizeof(guest_interface) == 24);
|
||||
|
||||
*host_interface = host_layout<wl_interface> { guest_interface }.data;
|
||||
fex_apply_custom_repacking_entry(reinterpret_cast<host_layout<wl_interface>&>(*host_interface), guest_interface);
|
||||
}
|
||||
|
||||
// Maps guest interface pointers to host pointers
|
||||
static const wl_interface* lookup_wl_interface(guest_layout<const wl_interface*> interface) {
|
||||
// Used e.g. for wl_shm_pool_destroy
|
||||
if (interface.force_get_host_pointer() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto [host_interface_it, inserted] = guest_to_host_interface.emplace(interface.get_pointer(), nullptr);
|
||||
if (!inserted) {
|
||||
assert_is_valid_host_interface(host_interface_it->second);
|
||||
return host_interface_it->second;
|
||||
}
|
||||
|
||||
assert_is_valid_guest_interface(interface);
|
||||
|
||||
fprintf(stderr, "Unknown wayland interface %p, adding to registry\n", interface.get_pointer());
|
||||
|
||||
host_interface_it->second = new wl_interface;
|
||||
wl_interface* host_interface = host_interface_it->second;
|
||||
repack_guest_wl_interface_to_host(interface, host_interface);
|
||||
return host_interface_it->second;
|
||||
}
|
||||
|
||||
void fex_custom_repack_entry(host_layout<wl_interface>& into, guest_layout<wl_interface> const& from) {
|
||||
// NOTE: These arrays are complements to global symbols in the guest, so we
|
||||
// never explicitly free this memory
|
||||
auto& host_interface = into.data;
|
||||
into.data.methods = new wl_message[into.data.method_count];
|
||||
into.data.events = new wl_message[into.data.event_count];
|
||||
|
||||
memset((void*)host_interface.methods, 0, sizeof(wl_message) * host_interface.method_count);
|
||||
for (int i = 0; i < host_interface.method_count; ++i) {
|
||||
const auto& guest_method { from.data.methods.get_pointer()[i] };
|
||||
host_layout<wl_message> host_method { guest_method };
|
||||
fex_apply_custom_repacking_entry(host_method, guest_method);
|
||||
memcpy((void*)&host_interface.methods[i], &host_method, sizeof(host_method));
|
||||
}
|
||||
|
||||
memset((void*)host_interface.events, 0, sizeof(wl_message) * host_interface.event_count);
|
||||
for (int i = 0; i < host_interface.event_count; ++i) {
|
||||
const auto& guest_event { from.data.events.get_pointer()[i] };
|
||||
host_layout<wl_message> host_event { guest_event };
|
||||
fex_apply_custom_repacking_entry(host_event, guest_event);
|
||||
memcpy((void*)&host_interface.events[i], &host_event, sizeof(host_event));
|
||||
}
|
||||
}
|
||||
|
||||
bool fex_custom_repack_exit(guest_layout<wl_interface>&, host_layout<wl_interface> const&) {
|
||||
fprintf(stderr, "Should not be called: %s\n", __PRETTY_FUNCTION__);
|
||||
std::abort();
|
||||
}
|
||||
void fex_custom_repack_entry(host_layout<wl_message>& into, guest_layout<wl_message> const& from) {
|
||||
auto& host_method = into.data;
|
||||
auto num_types = std::ranges::count_if(std::string_view { host_method.signature }, isalpha);
|
||||
if (num_types) {
|
||||
host_method.types = new const wl_interface*[num_types];
|
||||
for (int type = 0; type < num_types; ++type) {
|
||||
auto guest_interface_addr = from.data.types.get_pointer()[type];
|
||||
host_method.types[type] = guest_interface_addr.force_get_host_pointer() ? lookup_wl_interface(guest_interface_addr) : nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool fex_custom_repack_exit(guest_layout<wl_message>&, host_layout<wl_message> const&) {
|
||||
fprintf(stderr, "Should not be called: %s\n", __PRETTY_FUNCTION__);
|
||||
std::abort();
|
||||
}
|
||||
#else
|
||||
const wl_interface* lookup_wl_interface(guest_layout<const wl_interface*> interface) {
|
||||
return interface.force_get_host_pointer();
|
||||
}
|
||||
#endif
|
||||
|
||||
static wl_proxy* fexfn_impl_libwayland_client_wl_proxy_create(wl_proxy* proxy, guest_layout<const wl_interface*> guest_interface_raw) {
|
||||
auto host_interface = lookup_wl_interface(guest_interface_raw);
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_create(proxy, host_interface);
|
||||
}
|
||||
|
||||
#define WL_CLOSURE_MAX_ARGS 20
|
||||
static auto
|
||||
fex_wl_remap_argument_list(guest_layout<wl_argument*> args, const wl_message& message) {
|
||||
#ifndef IS_32BIT_THUNK
|
||||
// Cast to host layout and return as std::span
|
||||
wl_argument* host_args = host_layout<wl_argument*> { args }.data;
|
||||
return std::span<wl_argument, WL_CLOSURE_MAX_ARGS> { host_args, WL_CLOSURE_MAX_ARGS };
|
||||
#else
|
||||
// Return a new array of elements zero-extended to 64-bit
|
||||
std::array<wl_argument, WL_CLOSURE_MAX_ARGS> host_args;
|
||||
int arg_count = std::ranges::count_if(std::string_view { message.signature }, isalpha);
|
||||
for (int i = 0; i < arg_count; ++i) {
|
||||
// NOTE: wl_argument can store a pointer argument, so for 32-bit guests
|
||||
// we need to make sure the upper 32-bits are explicitly zeroed
|
||||
std::memset(&host_args[i], 0, sizeof(host_args[i]));
|
||||
std::memcpy(&host_args[i], &args.get_pointer()[i], sizeof(args.get_pointer()[i]));
|
||||
}
|
||||
return host_args;
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
fexfn_impl_libwayland_client_wl_proxy_marshal_array(
|
||||
wl_proxy *proxy, uint32_t opcode,
|
||||
guest_layout<wl_argument*> args) {
|
||||
auto host_args = fex_wl_remap_argument_list(args, get_proxy_interface(proxy)->methods[opcode]);
|
||||
fexldr_ptr_libwayland_client_wl_proxy_marshal_array(proxy, opcode, host_args.data());
|
||||
}
|
||||
|
||||
static wl_proxy*
|
||||
fex_wl_proxy_marshal_array(
|
||||
wl_proxy *proxy, uint32_t opcode,
|
||||
guest_layout<wl_argument*> args,
|
||||
guest_layout<const wl_interface*> guest_interface,
|
||||
bool constructor, // Call the _constructor variant of the native wayland function
|
||||
std::optional<uint32_t> version,
|
||||
std::optional<uint32_t> flags) {
|
||||
auto interface = lookup_wl_interface(guest_interface);
|
||||
|
||||
assert_is_valid_host_interface(get_proxy_interface(proxy));
|
||||
|
||||
auto host_args = fex_wl_remap_argument_list(args, get_proxy_interface(proxy)->methods[opcode]);
|
||||
|
||||
if (false) {
|
||||
} else if (!constructor && !version && !flags) {
|
||||
return nullptr;
|
||||
} else if (!constructor && version && flags) {
|
||||
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
|
||||
#if WAYLAND_VERSION_MAJOR * 10000 + WAYLAND_VERSION_MINOR * 100 + WAYLAND_VERSION_MICRO >= 11991
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_flags(proxy, opcode, interface, version.value(), flags.value(), host_args.data());
|
||||
#else
|
||||
fprintf(stderr, "Host Wayland version is too old to support FEX thunking\n");
|
||||
__builtin_trap();
|
||||
#endif
|
||||
} else if (constructor && version && !flags) {
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor_versioned(proxy, opcode, host_args.data(), interface, version.value());
|
||||
} else if (constructor && version && !flags) {
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor(proxy, opcode, host_args.data(), interface);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid configuration\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" wl_proxy*
|
||||
fexfn_impl_libwayland_client_wl_proxy_marshal_array_constructor_versioned(
|
||||
wl_proxy *proxy, uint32_t opcode,
|
||||
guest_layout<wl_argument*> args,
|
||||
guest_layout<const wl_interface*> interface,
|
||||
uint32_t version) {
|
||||
return fex_wl_proxy_marshal_array(proxy, opcode, args, interface, true, version, std::nullopt);
|
||||
}
|
||||
|
||||
extern "C" wl_proxy*
|
||||
fexfn_impl_libwayland_client_wl_proxy_marshal_array_constructor(
|
||||
wl_proxy *proxy, uint32_t opcode,
|
||||
guest_layout<wl_argument*> args,
|
||||
guest_layout<const wl_interface*> interface) {
|
||||
return fex_wl_proxy_marshal_array(proxy, opcode, args, interface, true, std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
extern "C" wl_proxy*
|
||||
fexfn_impl_libwayland_client_wl_proxy_marshal_array_flags(
|
||||
wl_proxy *proxy, uint32_t opcode,
|
||||
guest_layout<const wl_interface*> interface,
|
||||
uint32_t version, uint32_t flags,
|
||||
guest_layout<wl_argument*> args) {
|
||||
return fex_wl_proxy_marshal_array(proxy, opcode, args, interface, false, version, flags);
|
||||
}
|
||||
|
||||
// Variant of CallbackUnpack::CallGuestPtr that relocates a wl_array parameter
|
||||
// for 32-bit guests. Relocating this parameter is required since it may
|
||||
// reference inaccessible memory regions (presumably due to pointing to data
|
||||
// on the host stack).
|
||||
#ifndef IS_32BIT_THUNK
|
||||
template<typename Result, typename... Args>
|
||||
const auto CallGuestPtrWithWaylandArray = CallbackUnpack<Result(Args..., wl_array*)>::CallGuestPtr;
|
||||
#else
|
||||
template<typename Result, typename... Args>
|
||||
static auto CallGuestPtrWithWaylandArray(Args... args, wl_array *array) -> Result {
|
||||
GuestcallInfo *guestcall;
|
||||
LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall);
|
||||
|
||||
using PackedArgumentsType = PackedArguments<Result, guest_layout<Args>..., guest_layout<wl_array*>>;
|
||||
|
||||
GuestStackBumpAllocator GuestStack;
|
||||
|
||||
auto* guest_array = GuestStack.New<guest_layout<wl_array>>(to_guest(to_host_layout(*array)));
|
||||
guest_layout<wl_array*> guest_array_ptr = { .data = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(guest_array)) };
|
||||
|
||||
auto& packed_args = *GuestStack.New<PackedArgumentsType>(
|
||||
to_guest(to_host_layout(args))..., guest_array_ptr
|
||||
);
|
||||
|
||||
guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &packed_args);
|
||||
|
||||
if constexpr (!std::is_void_v<Result>) {
|
||||
return packed_args.rv;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// See wayland-util.h for documentation on protocol message signatures
|
||||
template<char> struct ArgType;
|
||||
@@ -46,28 +305,34 @@ static void WaylandFinalizeHostTrampolineForGuestListener(void (*callback)()) {
|
||||
}
|
||||
|
||||
extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy *proxy,
|
||||
guest_layout<void (**)(void)> callback_raw, void* data) {
|
||||
auto guest_interface = ((wl_proxy_private*)proxy)->interface;
|
||||
guest_layout<void (**)(void)> callback_table_raw, void* data) {
|
||||
auto interface = get_proxy_interface(proxy);
|
||||
|
||||
for (int i = 0; i < guest_interface->event_count; ++i) {
|
||||
auto signature_view = std::string_view { guest_interface->events[i].signature };
|
||||
assert_is_valid_host_interface(interface);
|
||||
|
||||
auto callback_table = callback_table_raw.force_get_host_pointer();
|
||||
|
||||
for (int i = 0; i < interface->event_count; ++i) {
|
||||
auto signature_view = std::string_view { interface->events[i].signature };
|
||||
|
||||
// A leading number indicates the minimum protocol version
|
||||
uint32_t since_version = 0;
|
||||
auto [ptr, res] = std::from_chars(signature_view.begin(), signature_view.end(), since_version, 10);
|
||||
std::string signature { ptr, &*signature_view.end() };
|
||||
auto signature = std::string { signature_view.substr(ptr - signature_view.begin()) };
|
||||
|
||||
// ? just indicates that the argument may be null, so it doesn't change the signature
|
||||
signature.erase(std::remove(signature.begin(), signature.end(), '?'), signature.end());
|
||||
|
||||
auto callback = reinterpret_cast<void(*)()>(uintptr_t { callback_raw.get_pointer()[i].data });
|
||||
auto callback = callback_table[i];
|
||||
|
||||
if (signature == "") {
|
||||
// E.g. xdg_toplevel::close
|
||||
WaylandFinalizeHostTrampolineForGuestListener<>(callback);
|
||||
} else if (signature == "a") {
|
||||
// E.g. xdg_toplevel::wm_capabilities
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'a'>(callback);
|
||||
FEXCore::FinalizeHostTrampolineForGuestFunction(
|
||||
(FEXCore::HostToGuestTrampolinePtr*)callback,
|
||||
(void*)CallGuestPtrWithWaylandArray<void, void*, wl_proxy*>);
|
||||
} else if (signature == "hu") {
|
||||
// E.g. zwp_linux_dmabuf_feedback_v1::format_table
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'h', 'u'>(callback);
|
||||
@@ -85,7 +350,9 @@ extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_prox
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i'>(callback);
|
||||
} else if (signature == "iia") {
|
||||
// E.g. xdg_toplevel::configure
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i', 'a'>(callback);
|
||||
FEXCore::FinalizeHostTrampolineForGuestFunction(
|
||||
(FEXCore::HostToGuestTrampolinePtr*)callback,
|
||||
(void*)CallGuestPtrWithWaylandArray<void, void*, wl_proxy*, int32_t, int32_t>);
|
||||
} else if (signature == "iiiiissi") {
|
||||
// E.g. wl_output_listener::geometry
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i', 'i', 'i', 'i', 's', 's', 'i'>(callback);
|
||||
@@ -118,7 +385,9 @@ extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_prox
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o'>(callback);
|
||||
} else if (signature == "uoa") {
|
||||
// E.g. wl_keyboard_listener::enter
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'a'>(callback);
|
||||
FEXCore::FinalizeHostTrampolineForGuestFunction(
|
||||
(FEXCore::HostToGuestTrampolinePtr*)callback,
|
||||
(void*)CallGuestPtrWithWaylandArray<void, void*, wl_proxy*, uint32_t, wl_surface*>);
|
||||
} else if (signature == "uoff") {
|
||||
// E.g. wl_pointer_listener::enter
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'f', 'f'>(callback);
|
||||
@@ -162,15 +431,15 @@ extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_prox
|
||||
}
|
||||
|
||||
// Pass the original function pointer table to the host wayland library. This ensures the table is valid until the listener is unregistered.
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_add_listener(proxy,
|
||||
reinterpret_cast<void(**)(void)>(callback_raw.get_pointer()),
|
||||
data);
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_add_listener(proxy, callback_table, data);
|
||||
}
|
||||
|
||||
wl_interface* fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(wl_interface* guest_interface, char const* name) {
|
||||
auto host_interface = reinterpret_cast<wl_interface*>(dlsym(fexldr_ptr_libwayland_client_so, (std::string { name } + "_interface").c_str()));
|
||||
void fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(guest_layout<wl_interface*> guest_interface_raw, const char* name) {
|
||||
auto& guest_interface = *guest_interface_raw.get_pointer();
|
||||
auto& host_interface = guest_to_host_interface[reinterpret_cast<guest_layout<const wl_interface>*>(&guest_interface)];
|
||||
host_interface = reinterpret_cast<wl_interface*>(dlsym(fexldr_ptr_libwayland_client_so, name));
|
||||
if (!host_interface) {
|
||||
fprintf(stderr, "Could not find host interface corresponding to %p (%s)\n", guest_interface, name);
|
||||
fprintf(stderr, "Could not find host interface corresponding to %p (%s)\n", &guest_interface, name);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
@@ -178,23 +447,50 @@ wl_interface* fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(wl_
|
||||
// them into the rodata section of the application itself instead of the
|
||||
// library. To copy the host information to them on startup, we must
|
||||
// temporarily disable write-protection on this data hence.
|
||||
auto page_begin = reinterpret_cast<uintptr_t>(guest_interface) & ~uintptr_t { 0xfff };
|
||||
auto page_begin = reinterpret_cast<uintptr_t>(guest_interface_raw.force_get_host_pointer()) & ~uintptr_t { 0xfff };
|
||||
if (0 != mprotect((void*)page_begin, 0x1000, PROT_READ | PROT_WRITE)) {
|
||||
fprintf(stderr, "ERROR: %s\n", strerror(errno));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
#ifdef IS_32BIT_THUNK
|
||||
// Requires struct repacking for wl_interface
|
||||
#error Not implemented
|
||||
#ifndef IS_32BIT_THUNK
|
||||
memcpy(&guest_interface, host_interface, sizeof(wl_interface));
|
||||
#else
|
||||
memcpy(guest_interface, host_interface, sizeof(wl_interface));
|
||||
guest_interface = to_guest(to_host_layout(*host_interface));
|
||||
|
||||
// NOTE: These arrays are complements to global symbols in the guest, so we
|
||||
// never explicitly free this memory
|
||||
guest_interface.data.methods.data = (uintptr_t)new guest_layout<wl_message>[host_interface->method_count];
|
||||
for (int i = 0; i < host_interface->method_count; ++i) {
|
||||
guest_interface.data.methods.get_pointer()[i] = to_guest(to_host_layout(host_interface->methods[i]));
|
||||
guest_interface.data.methods.get_pointer()[i].data.types = to_guest(to_host_layout(host_interface->methods[i].types));
|
||||
}
|
||||
|
||||
guest_interface.data.events.data = (uintptr_t)new guest_layout<wl_message>[host_interface->event_count];
|
||||
for (int i = 0; i < host_interface->event_count; ++i) {
|
||||
guest_interface.data.events.get_pointer()[i] = to_guest(to_host_layout(host_interface->events[i]));
|
||||
guest_interface.data.events.get_pointer()[i].data.types = to_guest(to_host_layout(host_interface->events[i].types));
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: Disabled until we ensure the interface data is indeed stored in rodata
|
||||
// mprotect((void*)page_begin, 0x1000, PROT_READ);
|
||||
}
|
||||
|
||||
return host_interface;
|
||||
void fexfn_impl_libwayland_client_fex_wl_get_method_signature(wl_proxy* proxy, uint32_t opcode, char* out) {
|
||||
strcpy(out, get_proxy_interface(proxy)->methods[opcode].signature);
|
||||
}
|
||||
|
||||
int fexfn_impl_libwayland_client_fex_wl_get_interface_event_count(wl_proxy* proxy) {
|
||||
return get_proxy_interface(proxy)->event_count;
|
||||
}
|
||||
|
||||
void fexfn_impl_libwayland_client_fex_wl_get_interface_event_name(wl_proxy* proxy, int i, char* out) {
|
||||
strcpy(out, get_proxy_interface(proxy)->events[i].name);
|
||||
}
|
||||
|
||||
void fexfn_impl_libwayland_client_fex_wl_get_interface_event_signature(wl_proxy* proxy, int i, char* out) {
|
||||
strcpy(out, get_proxy_interface(proxy)->events[i].signature);
|
||||
}
|
||||
|
||||
EXPORTS(libwayland_client)
|
||||
@@ -16,30 +16,41 @@ struct fex_gen_param {};
|
||||
|
||||
template<> struct fex_gen_type<wl_display> : fexgen::opaque_type {};
|
||||
template<> struct fex_gen_type<wl_proxy> : fexgen::opaque_type {};
|
||||
template<> struct fex_gen_type<wl_interface> : fexgen::opaque_type {};
|
||||
|
||||
template<> struct fex_gen_type<wl_event_queue> : fexgen::opaque_type {};
|
||||
|
||||
// Passed over Wayland's wire protocol for some functions
|
||||
template<> struct fex_gen_type<wl_array> {};
|
||||
template<> struct fex_gen_type<wl_array> : fexgen::emit_layout_wrappers {};
|
||||
|
||||
#ifdef IS_32BIT_THUNK
|
||||
// wl_interface and wl_message reference each other through pointers
|
||||
template<> struct fex_gen_type<wl_interface> : fexgen::emit_layout_wrappers {};
|
||||
template<> struct fex_gen_config<&wl_interface::methods> : fexgen::custom_repack {};
|
||||
template<> struct fex_gen_config<&wl_interface::events> : fexgen::custom_repack {};
|
||||
template<> struct fex_gen_type<wl_message> : fexgen::emit_layout_wrappers {};
|
||||
template<> struct fex_gen_config<&wl_message::types> : fexgen::custom_repack {};
|
||||
#else
|
||||
template<> struct fex_gen_type<wl_interface> : fexgen::assume_compatible_data_layout {};
|
||||
#endif
|
||||
|
||||
template<> struct fex_gen_config<wl_proxy_destroy> : fexgen::custom_guest_entrypoint {};
|
||||
|
||||
template<> struct fex_gen_config<wl_display_cancel_read> {};
|
||||
template<> struct fex_gen_config<wl_display_connect> {};
|
||||
template<> struct fex_gen_config<wl_display_flush> {};
|
||||
template<> struct fex_gen_config<wl_display_cancel_read> {};
|
||||
template<> struct fex_gen_config<wl_display_create_queue> {};
|
||||
template<> struct fex_gen_config<wl_display_disconnect> {};
|
||||
template<> struct fex_gen_config<wl_display_dispatch> {};
|
||||
template<> struct fex_gen_config<wl_display_dispatch_pending> {};
|
||||
template<> struct fex_gen_config<wl_display_dispatch_queue> {};
|
||||
template<> struct fex_gen_config<wl_display_dispatch_queue_pending> {};
|
||||
template<> struct fex_gen_config<wl_display_flush> {};
|
||||
template<> struct fex_gen_config<wl_display_get_error> {};
|
||||
template<> struct fex_gen_config<wl_display_prepare_read> {};
|
||||
template<> struct fex_gen_config<wl_display_prepare_read_queue> {};
|
||||
template<> struct fex_gen_config<wl_display_read_events> {};
|
||||
template<> struct fex_gen_config<wl_display_roundtrip> {};
|
||||
template<> struct fex_gen_config<wl_display_roundtrip_queue> {};
|
||||
template<> struct fex_gen_config<wl_display_connect_to_fd> {};
|
||||
template<> struct fex_gen_config<wl_display_get_fd> {};
|
||||
|
||||
template<> struct fex_gen_config<wl_event_queue_destroy> {};
|
||||
@@ -49,8 +60,12 @@ template<> struct fex_gen_config<wl_proxy_add_listener> : fexgen::custom_host_im
|
||||
template<> struct fex_gen_param<wl_proxy_add_listener, 1, void(**)()> : fexgen::ptr_passthrough {};
|
||||
// User-provided data pointer (not used in caller-provided callback)
|
||||
template<> struct fex_gen_param<wl_proxy_add_listener, 2, void*> : fexgen::assume_compatible_data_layout {};
|
||||
template<> struct fex_gen_config<wl_proxy_create> {};
|
||||
template<> struct fex_gen_config<wl_proxy_create> : fexgen::custom_host_impl {};
|
||||
template<> struct fex_gen_param<wl_proxy_create, 1, const wl_interface*> : fexgen::ptr_passthrough {};
|
||||
template<> struct fex_gen_config<wl_proxy_create_wrapper> {};
|
||||
template<> struct fex_gen_config<wl_proxy_get_class> {};
|
||||
template<> struct fex_gen_config<wl_proxy_get_id> {};
|
||||
template<> struct fex_gen_config<wl_proxy_get_listener> {};
|
||||
template<> struct fex_gen_config<wl_proxy_get_tag> {};
|
||||
template<> struct fex_gen_config<wl_proxy_get_user_data> {};
|
||||
template<> struct fex_gen_config<wl_proxy_get_version> {};
|
||||
@@ -60,12 +75,33 @@ template<> struct fex_gen_config<wl_proxy_set_tag> {};
|
||||
template<> struct fex_gen_config<wl_proxy_set_user_data> {};
|
||||
template<> struct fex_gen_config<wl_proxy_wrapper_destroy> {};
|
||||
|
||||
template<> struct fex_gen_config<wl_proxy_marshal_array> {};
|
||||
template<> struct fex_gen_config<wl_proxy_marshal_array> : fexgen::custom_host_impl {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array, 2, wl_argument*> : fexgen::ptr_passthrough {};
|
||||
template<> struct fex_gen_config<wl_proxy_marshal_array_constructor> : fexgen::custom_host_impl {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor, 2, wl_argument*> : fexgen::ptr_passthrough {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor, 3, const wl_interface*> : fexgen::ptr_passthrough {};
|
||||
template<> struct fex_gen_config<wl_proxy_marshal_array_constructor_versioned> : fexgen::custom_host_impl {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor_versioned, 2, wl_argument*> : fexgen::ptr_passthrough {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor_versioned, 3, const wl_interface*> : fexgen::ptr_passthrough {};
|
||||
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
|
||||
#if WAYLAND_VERSION_MAJOR * 10000 + WAYLAND_VERSION_MINOR * 100 + WAYLAND_VERSION_MICRO >= 11991
|
||||
template<> struct fex_gen_config<wl_proxy_marshal_array_flags> {};
|
||||
template<> struct fex_gen_config<wl_proxy_marshal_array_flags> : fexgen::custom_host_impl {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array_flags, 2, const wl_interface*> : fexgen::ptr_passthrough {};
|
||||
template<> struct fex_gen_param<wl_proxy_marshal_array_flags, 5, wl_argument*> : fexgen::ptr_passthrough {};
|
||||
#endif
|
||||
|
||||
// Guest notifies host about its interface. Host returns its corresponding interface pointer
|
||||
wl_interface* fex_wl_exchange_interface_pointer(wl_interface*, const char* name);
|
||||
void fex_wl_exchange_interface_pointer(wl_interface*, const char* name);
|
||||
template<> struct fex_gen_config<fex_wl_exchange_interface_pointer> : fexgen::custom_host_impl {};
|
||||
template<> struct fex_gen_param<fex_wl_exchange_interface_pointer, 0, wl_interface*> : fexgen::ptr_passthrough {};
|
||||
|
||||
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
|
||||
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
|
||||
void fex_wl_get_method_signature(wl_proxy*, uint32_t opcode, char*);
|
||||
template<> struct fex_gen_config<fex_wl_get_method_signature> : fexgen::custom_host_impl {};
|
||||
int fex_wl_get_interface_event_count(wl_proxy*);
|
||||
template<> struct fex_gen_config<fex_wl_get_interface_event_count> : fexgen::custom_host_impl {};
|
||||
void fex_wl_get_interface_event_name(wl_proxy*, int, char*);
|
||||
template<> struct fex_gen_config<fex_wl_get_interface_event_name> : fexgen::custom_host_impl {};
|
||||
void fex_wl_get_interface_event_signature(wl_proxy*, int, char*);
|
||||
template<> struct fex_gen_config<fex_wl_get_interface_event_signature> : fexgen::custom_host_impl {};
|
||||
@@ -716,6 +716,9 @@ TEST_CASE_METHOD(Fixture, "Mapping guest integers to fixed-size") {
|
||||
// applied to pointees as well
|
||||
const std::string ptr = GENERATE("", " *");
|
||||
|
||||
// Run each test with and without the ptr_passthrough annotation
|
||||
const bool passthrough_guest_type = GENERATE(false, true);
|
||||
|
||||
// These types are differently sized on 32-bit guests
|
||||
SECTION("(u)intptr_t / size_t / long") {
|
||||
const std::string type = GENERATE("long", "unsigned long", "uintptr_t", "intptr_t", "size_t");
|
||||
@@ -725,8 +728,9 @@ TEST_CASE_METHOD(Fixture, "Mapping guest integers to fixed-size") {
|
||||
"#include <cstddef>\n"
|
||||
"#include <cstdint>\n"
|
||||
"void func(" + type + ptr + ");\n"
|
||||
"template<> struct fex_gen_config<func> {};\n";
|
||||
if (!ptr.empty() && guest_abi == GuestABI::X86_32) {
|
||||
"template<> struct fex_gen_config<func> : fexgen::custom_host_impl {};\n" +
|
||||
(passthrough_guest_type ? "template<> struct fex_gen_param<func, 0> : fexgen::ptr_passthrough {};\n" : "");
|
||||
if (!ptr.empty() && guest_abi == GuestABI::X86_32 && !passthrough_guest_type) {
|
||||
// Guest points to a 32-bit integer, but the host to a 64-bit one.
|
||||
// This should be detected as a failure.
|
||||
CHECK_THROWS_WITH(run_thunkgen_host("", code, guest_abi, true), Catch::Contains("initialization of 'host_layout", Catch::CaseSensitive::No));
|
||||
@@ -748,6 +752,17 @@ TEST_CASE_METHOD(Fixture, "Mapping guest integers to fixed-size") {
|
||||
has(fieldDecl(hasType(asString(expected_type))))
|
||||
))))))))
|
||||
)));
|
||||
|
||||
// For passthrough parameters, the target function signature should
|
||||
// match the guest_layout type
|
||||
if (passthrough_guest_type) {
|
||||
CHECK_THAT(output,
|
||||
matches(functionDecl(
|
||||
hasName("fexfn_impl_libtest_func"),
|
||||
parameterCountIs(1),
|
||||
hasParameter(0, hasType(asString(expected_type)))
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,7 +792,8 @@ TEST_CASE_METHOD(Fixture, "Mapping guest integers to fixed-size") {
|
||||
"#include <thunks_common.h>\n"
|
||||
"#include <cstdint>\n"
|
||||
"void func(" + type + ptr + ");\n"
|
||||
"template<> struct fex_gen_config<func> {};\n";
|
||||
"template<> struct fex_gen_config<func> : fexgen::custom_host_impl {};\n" +
|
||||
(passthrough_guest_type ? "template<> struct fex_gen_param<func, 0> : fexgen::ptr_passthrough {};\n" : "");
|
||||
const auto output = run_thunkgen_host("", code, guest_abi);
|
||||
CHECK_THAT(output,
|
||||
matches(functionDecl(
|
||||
@@ -789,6 +805,17 @@ TEST_CASE_METHOD(Fixture, "Mapping guest integers to fixed-size") {
|
||||
has(fieldDecl(hasType(asString("guest_layout<" + fixed_size_type + ptr + ">"))))
|
||||
))))))))
|
||||
)));
|
||||
|
||||
// For passthrough parameters, the target function signature should
|
||||
// match the guest_layout type
|
||||
if (passthrough_guest_type) {
|
||||
CHECK_THAT(output,
|
||||
matches(functionDecl(
|
||||
hasName("fexfn_impl_libtest_func"),
|
||||
parameterCountIs(1),
|
||||
hasParameter(0, hasType(asString("guest_layout<" + fixed_size_type + ptr + ">")))
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user