Native Client

From Unvanquished
(Redirected from NativeClient)
Jump to: navigation, search

Native Client (also known as NaCl) is a sandboxing technology by Google. It was used by Chrome extensions and Chrome apps. The Dæmon engine uses Native Client to securely and portably run downloadable compiled games. It replaced the Q3VM (Quake 3 Virtual Machine) in the engine.

The Native Client documentation can be found on chrome.jscn.org/docs/native-client/welcome-to-native-client/.

Status

Native Client or NaCl is the current technology used for securely virtualising the game code in the Dæmon engine.

Server-side game logic NaCl support was first shipped in Alpha 27, released in April 2014. See the blog article by Kangz and the release note by DSalt.

Client-side game logic NaCl support was first shipped in Alpha 37, released on March 2, 2015. See the release note by Kangz.

This completed the transition by porting the client VM to NaCl and this replaced entirely the legacy Quake III QVM system.

Information 48x48.png

Native Client is (not yet) dying

Thanks to the Rebuild effort for both the NaCl loader and the Saigo NaCl compiler, and the fact the Saigo compiler has been maintained by Google up to 2025, bringing Clang 21 and C++23, the NaCl technology may be relevant for another decade and this allows the Unvanquished team to focus resources on other things.

But Native Client will die one day, and we are slowly preparing the future, and WebAssembly is the best candidate for a Native Client successor.

It's possible that the actual NaCl death will not come from Google but from Apple when they will deprecate the Rosetta 2 translation layer. If that prevents to run the amd64 loader on Apple Silicon, then Native Client will be dead for macOS and would not be a viable multi-platform sandboxing solution anymore.

Comparison

Game VM comparison
Quake 3 / Tremulous Dæmon / Unvanquished
Language Old C (NOT Quake C) Modern C / C++
Compiler LCC (proprietary) Clang (open-source)
System portability Yes Yes
Architecture portability Yes No
Secure sandbox No Yes


With the Quake 3 engine and the Q3VM, the game code was programmed in C (it should not be confused with the scripting language of Quake 1 named QuakeC). This C code was real C was meant to be compiled with the proprietary LCC compiler, which only supported an old C, and the developer couldn't really rely on off-the-shelf libraries. That C code was compiled by LCC into .qvm binaries.

With the Dæmon engine, the game code can be programmed in C or C++. That C/C++ code is compiled with Clang-based open source compilers supporting modern-enough C++ versions that makes possible to reuse off-the-shelf C++ libraries that may even be shared between the engine and the game source code. The C/C+ code is compiled into .nexe binarie.

Unlike QVMs, NaCl gamelogic runs in a secure sandbox. Native Client lets you run mods downloaded from the internet without giving them free reign on your computer. QVM was equivalent to running a DLL with no protection.

Platforms

Native Client Platform compatibility
System Architecture NaCl Runtime compatibility PNaCL SDK compatibility Saigo SDK compatibility
Windows amd64 ✅️ native ✅️ native ✅️ native
i686 ✅️ native ✅️ native ✅️ native
Linux amd64 ✅️ native ✅️ native ✅️ native
i686 ✅️ native ❌️ ✅️ native
arm64 ☑️ armhf Multiarch
☑️ Box64 (WIP)
❌️ ✅️ native
armhf ✅️ native ❌️ ✅️ native
ppc64el ☑️ Box64 (WIP) ❌️ ✅️ native
riscv64 ☑️ Box64 (WIP) ❌️ ✅️ native
loong64 ☑️ Box64 (WIP, untested) ❌️ ✅️ native
macOS amd64 ✅️ native ✅️ native ✅️ native
arm64 ☑️ amd64 Rosetta2 ☑️ amd64 Rosetta2 ✅️ native
FreeBSD amd64 ☑️ Linuxulator ❌️ ✅️ native
i686 ☑️ Linuxulator (untested) ❌️ ✅️ native


It has been verified that Linux on arm64, ppc64el and riscv64 can run the amd64 loader on Box64, though on riscv64 it is very slow. The Box64 translation layer can also run on loong64 but this has not been tested yet. The Dæmon engine received patches to run Box64, but the user has to build the engine themselves and provides Box64. This is work-in-progress. Box64 integration is still work in progress (at some point we may ship Box64 with engines that can use it).

The NaCl loader (the virtual machine running the virtualised code) is available for three systems: Linux, Windows and macOS. The supported architectures differs per system:

  • Linux: amd64, i686, armhf;
  • Windows: amd64, i686;
  • macOS: amd64.

It is possible to run native engines with other platforms when there is a compatibility layer to run the NaCl loader:

  • Linux, arm64: running the armhf NaCl loader through built-in Linux Multiarch compatibility;
  • macOS, arm64: running the amd64 NaCl loader through Rosetta2 compatibility;
  • FreeBSD, amd64: running the Linux NaCl loader through built-in Linuxulator compatibility.

We provide the Multiarch libraries for running the armhf game on Linux arm64, no system modification is required on that part.

The Linux armhf NaCl loader requires a 4k PageSize kernel, this may require a system modification:

There is work in progress to remove this restriction.

The Linux i686 and armhf NaCl loaders may run on FreeBSD using Linuxulator but it is not tested.

NaCl also supports 32-bit MIPS on Linux but this architecture is not supported by the Dæmon engine and the Unvanquished game project.

NaCl upstream development has slowed down since the competing solution WebAssembly became the industry standard, so it is very unlikely support for new systems and architecture would be added. To overcome this problem, we would have to switch to WebAssembly (that unfortunately may only support using a slow interpreter for some platforms NaCl supports, like i686 or armhf), but more importantly may still lack support for exceptions or setjmp/longjmp and then not be complete enough for our needs yet.

Compilers

PNaCl Clang

PNaCl Clang is the current NaCl compiler we use to build the nexe game binaries. It is based on an old Clang 3.6 and does not support C++ above C++14. To overcome some of the related limitations we may switch to Saigo.

The PNaCl compilation workflow is to build a single .pexe then to translate them to per-architecture .nexe (amd64, i686, armhf) using a specific translator tool.

The PNaCl SDK consists on Python-based wrappers around Clang internals. It was initially using now-obsolete and hard-to-find Python2 but we ported it to Python3 to extend its lifespan.

The provided PNaCl SDK runs on Linux, Windows and macOS systems, on amd64 architectures.

While the .pexe compiler itself runs on FreeBSD with the Linuxulator, the translator does not, meaning it's not possible to build .nexe binaries on FreeBSD.

PNaCl supports C++ exception. There exist newer PNaCl SDKs (from chromium canary releases) that do not support exceptions and may not provide more what we already have with latest stable PNaCl.

Saigo

Saigo is a new NaCl compiler based on latest Clang and supporting latest C++ standards. It compiles directly to .nexe.

See Saigo for details.

The Unvanquished game code is now buildable with Saigo.

Saigo may not support C++ exceptions as far as we know but supports setjmp/longjmp.

Unlike PNaCl, Saigo do not provide released binaries, meaning a switch to Saigo requires a convenient way to provide it to contributors. Google provides some nightly Linux amd64 Saigo toolchain snapshots that aren't easy to get without some Google scripts. The Saigo compiler itself isn't hard to build (same as Clang itself), the binutils are a bit more ticky but not that hard (10 years-old configure build). What is tricky to rebuild is the libc that requires some very old NaCl GCC. Fortunately there exists libc and libc++ snapshots provided by Google that can be reused with a self-built toolchain.

The Saigo compiler and NaCl binutils can be rebuilt to run natively on many platforms (Like Clang and GNU binutils), but they will always target the usual platforms as the NaCl loader is still the same: a more recent NaCl loader is also buildable but doesn't bring any new platform support.

Moving to Saigo is considered as a migration step that can be achievable before migrating to WebAssembly, as it allows us to migrate to a new C++ standard and update libraries we use without waiting for the migration to WebAssembly.

Some redesign of our CMake configuration for Saigo may help later when migrating to WebAssembly as it makes it more generic for different VM toolchains.

Here is a GitHub issue about the ongoing efforts for migrating to Saigo:

Rebuild effort

Information 48x48.png

Native Client is not a Google thing anymore

Google publicly announced in May of 2017 the (then-)upcoming deprecation and abandonment of Native Client technologies in favor of WebAssembly, and announced the actual deprecation in 2020. But Google also supported Native Client-powered ChromeOS 138 until ChromeOS 139 in July of 2025 and as such continued developpement of some Native Client technologies. This extra development materialized in the maintenance of the loader and a toolchain named Saigo, frequently rebased on the latest LLVM upstream at the time. The loader received commits from Google until April of 2025, while Saigo received commits from Google until January of 2025 and the last rebase was over Clang 21.

Nothing about Native Client should be expected from Google anymore.

The Unvanquished team is working on rebuilding the Native Client ecosystem to benefit from the updates implemented by Google over the years. This enables newer C and C++ versions, thanks to the Saigo compiler being based on Clang 21. It also makes sure everyone can rebuild the loader and the toolchain.

The project for rebuilding the Native Client loader can be found there:

The official releases for Unvanquished are already using a rebuilt NaCl loader on Linux for the amd64 architecture.

The project for rebuilding the Saigo Native Client SDK can be found there:

It's already possible to build Unvanquished with a Saigo compiler rebuilt this way.

Interactive Debugging

Most of the time it is easiest to debug with a native DLL. But if you have a problem that cannot be reproduced outside of NaCl, you may be forced to resort to the following tutorial.

These steps for attaching the NaCl debugger[1] have been tested on Windows with a PNaCl toolchain. They should also work on binaries built with Saigo, using the old PNaCl GDB. On Linux or Mac you may experience more difficulties due to the lack of support for old binaries (meaning nacl-gdb). illwieckz reports that on amd64 Linux in 2025, using the old GDB which is a 32-bit binary, debugging an amd64 nexe does not work (despite the documentation's claims of debugger platform independence), but debugging an i686 nexe does.

  1. Build the NaCl gamelogic with debugging symbols (build type Debug or RelWithDebInfo).
  2. Launch Daemon configured for NaCl debugging. For example, ./daemonded -set vm.sgame.type 1 -set vm.sgame.debug 1 +map chasm When the relevant gamelogic starts, the engine should pause and emit the message Waiting for GDB connection on localhost:4014.
  3. Start NaCl GDB, e.g. daemon\external_deps\windows-amd64-mingw_10\nacl-gdb.exe This works better out of a command prompt rather than an MSYS window.
  4. Use the "file" command to tell the debugger where the binary with symbols is. For example, file sgame-amd64.nexe
  5. OPTIONAL, probably a waste of time: load the IRT (Integrated Runtime) symbols: nacl-irt irt_core-amd64.nexe
  6. Connect to the NaCl program with the command target remote :4014
  7. You should see a message like 0x000000000ffc00a0 in ?? () (or 0x000000000ffc00a0 in __pnacl_start () if you did step 5). This means the program is paused at the beginning.
  8. Set some breakpoints, e.g. b G_RunFrame. Alternatively, just wait for the debugger to break when the game crashes.
  9. Start the program by continuing (c).

Postmortem Debugging

See Breakpad.
  1. https://web.archive.org/web/20250909075554/https://www.chromium.org/nativeclient/how-tos/debugging-documentation/debugging-with-debug-stub-recommended/getting-started-with-debug-stub/