Native Client — Secure Native Code Sandbox Technology

From Unvanquished
(Redirected from NaCl)
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/.

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.

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.

Information 48x48.png

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

Game VM 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

Native Client Platform Compatibility
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
Legend: ✅️ native support, ☑️ support through a compatibility layer, ❌️ no support.

NaCl Runtime

See the Dæmon NaCl Runtime page for details.

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 (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.

  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 Dæmon 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/