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 <xukl2019@sjtu.edu.cn> Co-authored-by: Mingyuan Xia <xiamy@ultrarisc.com> Signed-off-by: Ziyang Zhang <functioner@sjtu.edu.cn> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Link: https://lore.kernel.org/qemu-devel/20260711094523.622997-3-functioner@sjtu.edu.cn Signed-off-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
This commit is contained in:
committed by
Pierrick Bouvier
parent
09b7a409a7
commit
eee8e58eed
@@ -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 <guest-program> ...
|
||||
|
||||
`Lorelei <https://github.com/rover2024/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 <stdio.h>
|
||||
|
||||
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 <stdio.h>
|
||||
|
||||
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
|
||||
------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user