@samitouri / QOSamiQemu / commits / eee8e58eed

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>

Ziyang Zhang committed Jul 11, 2026 at 17:45 UTC eee8e58eed452fb8a7be4fd9c7868d0512abbd6a
1 file changed +158
docs/about/emulation.rst
+158
@@ -1046,6 +1046,164 @@ Count traps
1046 This plugin counts the number of interrupts (asynchronous events), exceptions
1047 (synchronous events) and host calls (e.g. semihosting) per cpu.
1048
1049 +Dynamic Linking Call
1050 +....................
1051 +
1052 +``contrib/plugins/dlcall.c``
1053 +
1054 +This plugin provides a dynamic linking function call interception mechanism
1055 +for linux-user guests: the guest hands a call off to the host, where the plugin
1056 +runs native code in its place instead of the guest emulating it. Interception
1057 +alone enables several uses, for instance tracing or auditing guest calls.
1058 +One use is acceleration by leveraging the host's native shared libraries. For
1059 +example, a thunk layer can run the stock zlib ``minizip`` utility under
1060 +emulation while forwarding its ``deflate`` calls to the host's native zlib
1061 +library (libz). This avoids emulating those selected library calls instruction
1062 +by instruction.
1063 +
1064 +The guest issues a reserved "magic" system call (4096 by default, configurable
1065 +with ``syscall_num=N``) whose first argument selects a pass-through operation:
1066 +dlopen/dlclose a host library, dlsym a symbol, and invoke a resolved host
1067 +function. The plugin performs the operation on the host and consumes the
1068 +syscall, so the real kernel never sees it.
1069 +
1070 +.. warning::
1071 +
1072 + Trusted guests only. The guest can load arbitrary host libraries and run
1073 + arbitrary code in the QEMU host process. The plugin is not a sandbox and
1074 + provides no isolation. It also requires ``guest_base == 0`` (qemu-user's
1075 + default), as guest pointers are dereferenced as host addresses with no
1076 + translation.
1077 +
1078 +The plugin intentionally keeps the QEMU side lightweight and knows nothing
1079 +about any particular library or its calling convention. Turning a real library
1080 +into working thunks, including argument marshalling, callbacks and variadic
1081 +functions, is done entirely in userspace, and any toolchain can implement the
1082 +interface.
1083 +
1084 +Loading the plugin is all that is required from QEMU's side:
1085 +
1086 +.. code-block:: shell
1087 +
1088 + qemu-x86_64 -plugin contrib/plugins/libdlcall.so <guest-program> ...
1089 +
1090 +`Lorelei <https://github.com/rover2024/lorelei>`_ is one end-to-end userspace
1091 +implementation of this: it provides the guest and host runtimes and an
1092 +automated toolchain that generates the thunks from a library's headers, so guest
1093 +library calls run on the host's native libraries. It supports an x86_64 guest
1094 +running on an x86_64, aarch64 or riscv64 host.
1095 +
1096 +A minimal end-to-end example uses a one-function library, ``libhello.so``, built
1097 +two ways: the guest build tags its output ``(from the guest)`` and the host
1098 +build ``(from the host)``. An unmodified guest program ``main`` calls
1099 +``hello("World", 7)``, and the thunk makes that same binary reach the host build
1100 +in place of its own. The sources live under ``src/``:
1101 +
1102 +.. code-block:: c
1103 +
1104 + /* src/hello.h */
1105 + void hello(const char *name, int lucky);
1106 +
1107 +.. code-block:: c
1108 +
1109 + /* src/hello_guest.c */
1110 + #include "hello.h"
1111 + #include <stdio.h>
1112 +
1113 + void hello(const char *name, int lucky)
1114 + {
1115 + printf("Hello, %s! Your lucky number is %d. (from the guest)\n", name, lucky);
1116 + }
1117 +
1118 +.. code-block:: c
1119 +
1120 + /* src/hello_host.c */
1121 + #include "hello.h"
1122 + #include <stdio.h>
1123 +
1124 + void hello(const char *name, int lucky)
1125 + {
1126 + printf("Hello, %s! Your lucky number is %d. (from the host)\n", name, lucky);
1127 + }
1128 +
1129 +.. code-block:: c
1130 +
1131 + /* src/main.c */
1132 + #include "hello.h"
1133 +
1134 + int main(void)
1135 + {
1136 + hello("World", 7);
1137 + return 0;
1138 + }
1139 +
1140 +Lorelei ships a prebuilt toolchain (a "devkit") in its releases. Download the
1141 +one for your host and unpack it:
1142 +
1143 +.. code-block:: shell
1144 +
1145 + # ARCH is your host's architecture: x86_64, aarch64 or riscv64. This example uses aarch64.
1146 + # See https://github.com/rover2024/lorelei/releases
1147 + ARCH=aarch64
1148 + VERSION=$(curl -fsSL -o /dev/null -w '%{url_effective}' \
1149 + https://github.com/rover2024/lorelei/releases/latest | sed 's|.*/tag/v||')
1150 + wget "https://github.com/rover2024/lorelei/releases/download/v$VERSION/lorelei-devkit-$ARCH-$VERSION.tar.xz"
1151 + tar -xf lorelei-devkit-$ARCH-$VERSION.tar.xz
1152 + DEVKIT=lorelei-devkit-$ARCH
1153 +
1154 +Build the guest ``libhello.so`` (x86_64) and the host ``libhello.so`` (this
1155 +host's architecture), then the guest program:
1156 +
1157 +.. code-block:: shell
1158 +
1159 + mkdir -p build/guest build/host
1160 + $DEVKIT/bin/x86_64-linux-gnu-clang -shared -fPIC src/hello_guest.c -o build/guest/libhello.so
1161 + cc -shared -fPIC src/hello_host.c -o build/host/libhello.so
1162 + $DEVKIT/bin/x86_64-linux-gnu-clang src/main.c -Isrc -Lbuild/guest -lhello -o build/guest/main
1163 +
1164 +Run it under qemu:
1165 +
1166 +.. code-block:: shell
1167 +
1168 + qemu-x86_64 -L /usr/x86_64-linux-gnu/ -E LD_LIBRARY_PATH=build/guest build/guest/main
1169 +
1170 +which prints::
1171 +
1172 + Hello, World! Your lucky number is 7. (from the guest)
1173 +
1174 +Now generate the thunk from the host ``libhello.so``. This produces a guest-side
1175 +``libhello.so`` that stands in for the guest build, and a host-side thunk library
1176 +that dispatches to the host build:
1177 +
1178 +.. code-block:: shell
1179 +
1180 + $DEVKIT/bin/LoreMakeThunk.py --name hello --lib build/host/libhello.so \
1181 + --header hello.h -o thunks -- -Isrc
1182 +
1183 +Run the same ``main`` under the plugin. The call reaches the host build now:
1184 +
1185 +.. code-block:: shell
1186 +
1187 + LD_LIBRARY_PATH=$DEVKIT/lib:build/host \
1188 + qemu-x86_64 -plugin contrib/plugins/libdlcall.so \
1189 + -E LD_LIBRARY_PATH=$DEVKIT/x86_64/lib:thunks/x86_64 \
1190 + -L /usr/x86_64-linux-gnu/ \
1191 + build/guest/main
1192 +
1193 +which prints::
1194 +
1195 + Hello, World! Your lucky number is 7. (from the host)
1196 +
1197 +.. list-table:: Dynamic Linking Call arguments
1198 + :widths: 20 80
1199 + :header-rows: 1
1200 +
1201 + * - Option
1202 + - Description
1203 + * - syscall_num=N
1204 + - The magic syscall number the guest issues (default 4096). Must be high
1205 + enough not to clash with a real syscall.
1206 +
1207 Other emulation features
1208 ------------------------
1209