From eee8e58eed452fb8a7be4fd9c7868d0512abbd6a Mon Sep 17 00:00:00 2001 From: Ziyang Zhang Date: Sat, 11 Jul 2026 17:45:23 +0800 Subject: [PATCH] docs/about/emulation: document the dlcall plugin Document the dlcall plugin under Example Plugins: what it does, the trusted- guests and guest_base == 0 constraints, how to load it, and a pointer to Lorelei, one end-to-end userspace implementation, for the toolchain and a runnable example. Co-authored-by: Kailiang Xu Co-authored-by: Mingyuan Xia Signed-off-by: Ziyang Zhang Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260711094523.622997-3-functioner@sjtu.edu.cn Signed-off-by: Pierrick Bouvier --- docs/about/emulation.rst | 158 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 3b4c365933..b861501e85 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -1046,6 +1046,164 @@ Count traps This plugin counts the number of interrupts (asynchronous events), exceptions (synchronous events) and host calls (e.g. semihosting) per cpu. +Dynamic Linking Call +.................... + +``contrib/plugins/dlcall.c`` + +This plugin provides a dynamic linking function call interception mechanism +for linux-user guests: the guest hands a call off to the host, where the plugin +runs native code in its place instead of the guest emulating it. Interception +alone enables several uses, for instance tracing or auditing guest calls. +One use is acceleration by leveraging the host's native shared libraries. For +example, a thunk layer can run the stock zlib ``minizip`` utility under +emulation while forwarding its ``deflate`` calls to the host's native zlib +library (libz). This avoids emulating those selected library calls instruction +by instruction. + +The guest issues a reserved "magic" system call (4096 by default, configurable +with ``syscall_num=N``) whose first argument selects a pass-through operation: +dlopen/dlclose a host library, dlsym a symbol, and invoke a resolved host +function. The plugin performs the operation on the host and consumes the +syscall, so the real kernel never sees it. + +.. warning:: + + Trusted guests only. The guest can load arbitrary host libraries and run + arbitrary code in the QEMU host process. The plugin is not a sandbox and + provides no isolation. It also requires ``guest_base == 0`` (qemu-user's + default), as guest pointers are dereferenced as host addresses with no + translation. + +The plugin intentionally keeps the QEMU side lightweight and knows nothing +about any particular library or its calling convention. Turning a real library +into working thunks, including argument marshalling, callbacks and variadic +functions, is done entirely in userspace, and any toolchain can implement the +interface. + +Loading the plugin is all that is required from QEMU's side: + +.. code-block:: shell + + qemu-x86_64 -plugin contrib/plugins/libdlcall.so ... + +`Lorelei `_ is one end-to-end userspace +implementation of this: it provides the guest and host runtimes and an +automated toolchain that generates the thunks from a library's headers, so guest +library calls run on the host's native libraries. It supports an x86_64 guest +running on an x86_64, aarch64 or riscv64 host. + +A minimal end-to-end example uses a one-function library, ``libhello.so``, built +two ways: the guest build tags its output ``(from the guest)`` and the host +build ``(from the host)``. An unmodified guest program ``main`` calls +``hello("World", 7)``, and the thunk makes that same binary reach the host build +in place of its own. The sources live under ``src/``: + +.. code-block:: c + + /* src/hello.h */ + void hello(const char *name, int lucky); + +.. code-block:: c + + /* src/hello_guest.c */ + #include "hello.h" + #include + + void hello(const char *name, int lucky) + { + printf("Hello, %s! Your lucky number is %d. (from the guest)\n", name, lucky); + } + +.. code-block:: c + + /* src/hello_host.c */ + #include "hello.h" + #include + + void hello(const char *name, int lucky) + { + printf("Hello, %s! Your lucky number is %d. (from the host)\n", name, lucky); + } + +.. code-block:: c + + /* src/main.c */ + #include "hello.h" + + int main(void) + { + hello("World", 7); + return 0; + } + +Lorelei ships a prebuilt toolchain (a "devkit") in its releases. Download the +one for your host and unpack it: + +.. code-block:: shell + + # ARCH is your host's architecture: x86_64, aarch64 or riscv64. This example uses aarch64. + # See https://github.com/rover2024/lorelei/releases + ARCH=aarch64 + VERSION=$(curl -fsSL -o /dev/null -w '%{url_effective}' \ + https://github.com/rover2024/lorelei/releases/latest | sed 's|.*/tag/v||') + wget "https://github.com/rover2024/lorelei/releases/download/v$VERSION/lorelei-devkit-$ARCH-$VERSION.tar.xz" + tar -xf lorelei-devkit-$ARCH-$VERSION.tar.xz + DEVKIT=lorelei-devkit-$ARCH + +Build the guest ``libhello.so`` (x86_64) and the host ``libhello.so`` (this +host's architecture), then the guest program: + +.. code-block:: shell + + mkdir -p build/guest build/host + $DEVKIT/bin/x86_64-linux-gnu-clang -shared -fPIC src/hello_guest.c -o build/guest/libhello.so + cc -shared -fPIC src/hello_host.c -o build/host/libhello.so + $DEVKIT/bin/x86_64-linux-gnu-clang src/main.c -Isrc -Lbuild/guest -lhello -o build/guest/main + +Run it under qemu: + +.. code-block:: shell + + qemu-x86_64 -L /usr/x86_64-linux-gnu/ -E LD_LIBRARY_PATH=build/guest build/guest/main + +which prints:: + + Hello, World! Your lucky number is 7. (from the guest) + +Now generate the thunk from the host ``libhello.so``. This produces a guest-side +``libhello.so`` that stands in for the guest build, and a host-side thunk library +that dispatches to the host build: + +.. code-block:: shell + + $DEVKIT/bin/LoreMakeThunk.py --name hello --lib build/host/libhello.so \ + --header hello.h -o thunks -- -Isrc + +Run the same ``main`` under the plugin. The call reaches the host build now: + +.. code-block:: shell + + LD_LIBRARY_PATH=$DEVKIT/lib:build/host \ + qemu-x86_64 -plugin contrib/plugins/libdlcall.so \ + -E LD_LIBRARY_PATH=$DEVKIT/x86_64/lib:thunks/x86_64 \ + -L /usr/x86_64-linux-gnu/ \ + build/guest/main + +which prints:: + + Hello, World! Your lucky number is 7. (from the host) + +.. list-table:: Dynamic Linking Call arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - syscall_num=N + - The magic syscall number the guest issues (default 4096). Must be high + enough not to clash with a real syscall. + Other emulation features ------------------------