ThunkLibs/README.md
FEX supports special guest libraries that call out to host code for speed and compatibility.
We support both guest->host thunks, as well as host->guest callbacks
The thunked libraries can be built via the guest-libs and host-libs targets of the main FEX project. The outputs are in $BUILDDIR/Guest and $BUILDDIR/Host
After that, a guest rootfs is needed with the guest-libs installed. Typically this is done with symlinks that replace the native guest libraries. eg
# Unlink original guest lib
unlink $ROOTFS/lib/x86_64-linux-gnu/libX11.so.6
# Make it point to thunked version
ln -s $BUILDDIR/Guest/libX11-guest.so $ROOTFS/lib/x86_64-linux-gnu/libX11.so.6
Finally, FEX needs to be told where to look for the matching host libraries with -t /Host/Libs/Path. eg
FEX_THUNKHOSTLIBS= $BUILDDIR/Host FEX /PATH/TO/ELF
We currently don't have any unit tests for the guest libraries, only for OP_THUNK.
There are several parts that make this possible. This is a rough outline.
In FEX
library:function that directly follows the Guest opcode.Context::HandleCallback does the Host -> Guest transition, and returns when the Guest function returns.fex:loadlib is used to load and initialize a matching host lib. For more details, look in ThunkHandler_impl::LoadLibThunkHandler_impl::CallCallback is provided to the host libs, so they can call callbacks. It prepares guest arguments and uses Context::HandleCallbackThunkLibs, Library loading
fex:loadlib thunk, with the library name and callback unpackers, if any.fexthunks_exports_$libname(CallCallbackPtr, GuestUnpackers) is called to initialize the host library.ThunkLibs, Guest -> Host
ThunkLibs, Host -> Guest. This is only possible while handling a Guest -> Host call (ie, callbacks).
ThunkHandler_impl::CallCallback is called with the Guest unpacker, and Guest function as argumentsBoilerplate code is automated using a dedicated code generator tool, which parses a C++ source file (libX_interface.cpp) that specializes
a templated fex_gen_config struct for each thunked function. The generator will pull all required function signatures from the original
library's header files and emit the appropriate boilerplate (guest->host thunks, argument packers/unpackers, host library loader, ...).
In most cases, an empty fex_gen_config specialization is sufficient, but if needed the generator behavior can be customized on a
function-by-function basis using an annotation-syntax: Binary properties are toggled by inheriting from a fixed set of tag types
(e.g. fexgen::custom_host_impl), whereas complicated properties are customized by defining struct members/aliases with a magic name
detected by the generator (e.g. using uniform_va_type = char).
For each thunked library, the generator outputs the following files:
thunks.inl: Guest -> Host transition functions that use 0xF 0x3Ffunction_packs.inl: Guest argument packers / rv handling, private to the SO. These are used to solve symbol resolution issues with glxGetProc*, etc.function_packs_public.inl: Guest argument packers / rv handling, exported from the SO. These are identical to the function_packs, but exported from the SOfunction_unpacks.inl: Host argument unpackers / rv handlingldr.inl: Host loader that dlopens/dlsyms the "real" host library for the implementation functions.ldr_ptrs.inl: Host loader pointer declarations, used by ldr and function_unpackstab_function_unpacks.inl: Host function unpackers list, passed to FEX after Host library init so it can resolve the Guest Thunks to Host functionsThere are two kinds of libs, simpler ones with no callbacks, and complex ones with callbacks. You can see how libX11 is implemented for a callbacks example, and libasound for a non-callbacks example.
Getting started
libName/libName_interface.cpp and customize the fex_gen_config template for each thunked function. See some existing lib for details.libName/libName_Guest.cpp and libName/libName_Host.cpp. Copy & rename from some existing lib is the way to go.GuestLibs/CMakeLists.txt and HostLibs/CMakeLists.txt to add the new targets, similar to how other libs are done.Now the host and the guest libs should be built as part of guest-libs and host-libs