Native Client — Secure Native Code Sandbox Technology
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/.
The NaCl support consists of:
- Dæmon NaCl Runtime — a modern Native Client runtime to run NaCl executables;
- Dæmon Saigo SDK — a modern toolchain for Native Client with compilers and a debugger to build NaCl executables;
- Dæmon Breakpad — an enhanced crash reporting library with support for NaCl executables built with Saigo.
Contents
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 Q3VM system.
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.
Native Client is (not yet) dying
Thanks to the Native Client Rebuild effort for both the Dæmon NaCl Runtime and the Dæmon Saigo SDK, 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. WebAssembly is the best candidate for a NaCl successor.
It's possible that the actual NaCl death will not come from Google but from Apple as they progressively deprecate the Rosetta 2 translation layer. When that will prevent 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
| Quake 3 / Tremulous | Dæmon / Unvanquished | |
|---|---|---|
| Technology | Q3VM | NaCl |
| 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.
Platform support
| System | Architecture | Dæmon NaCl Runtime | Google PNaCL SDK | Dæmon Saigo SDK |
|---|---|---|---|---|
| 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 |
NaCl Runtime
See the Dæmon NaCl Runtime page for details.
Rebuild effort
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 (from the Dæmon Saigo SDK being based on Clang 21. It also makes sure everyone can rebuild the runtime and the toolchain.
The project for rebuilding the Dæmon NaCl Runtime can be found there:
The official releases for Unvanquished are already using the Dæmon NaCl Runtime on Linux for the amd64 architecture, other systems are still using the Google NaCl Runtime.
The project for rebuilding the Dæmon Saigo SDK can be found there:
It's already possible to build Unvanquished with a Saigo compiler rebuilt this way.
Compilers
Google PNaCl SDK
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 using Google PNaCl.
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.
Dæmon Saigo SDK
Saigo is a new NaCl compiler based on latest Clang and supporting latest C++ standards. It compiles directly to .nexe.
The Unvanquished team publishes frequent releases of the Dæmon Saigo SDK with ready-to-use prebuilt packages for a large number of platforms:
The Unvanquished game code is already buildable with Saigo. Saigo is meant to replace PNaCl for Unvanquished release builds.
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 (from the Dæmon Saigo SDK) being based on Clang 21. It also makes sure everyone can rebuild the runtime and the toolchain.
See Dæmon Saigo SDK for details.
Saigo may not support C++ exceptions as far as we know but supports setjmp/longjmp.
Unlike PNaCl, Saigo wasn't released by Google, meaning a switch from PNaCl to Saigo required a convenient way to provide it to contributors. Google provided 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 tricky 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 runtime 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:
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 GDB from the PNaCl SDK or the GDB provided with the Dæmon Saigo SDK. 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.
- Build the NaCl gamelogic with debugging symbols (build type Debug or RelWithDebInfo).
- Launch Daemon configured for NaCl debugging. For example,
./daemonded -set vm.sgame.type 1 -set vm.sgame.debug 1 +map chasmWhen the relevant gamelogic starts, the engine should pause and emit the messageWaiting for GDB connection on localhost:4014. - Start NaCl GDB, e.g.
daemon\external_deps\windows-amd64-mingw_10\nacl-gdb.exeThis works better out of a command prompt rather than an MSYS window. - Use the "file" command to tell the debugger where the binary with symbols is. For example,
file sgame-amd64.nexe - OPTIONAL, probably a waste of time: load the IRT (Integrated Runtime) symbols:
nacl-irt irt_core-amd64.nexe - Connect to the NaCl program with the command
target remote :4014 - You should see a message like
0x000000000ffc00a0 in ?? ()(or0x000000000ffc00a0 in __pnacl_start ()if you did step 5). This means the program is paused at the beginning. - Set some breakpoints, e.g.
b G_RunFrame. Alternatively, just wait for the debugger to break when the game crashes. - Start the program by continuing (
c).
