Skip to content

Commit 774f84f

Browse files
committed
PoC: Box64 hybrid NaCl emulation for unsupported platforms
Run x86_64 NaCl loader and amd64 .nexe game modules under Box64 emulation on platforms without native NaCl support. The native engine communicates with the emulated NaCl sandbox processes via architecture-independent Unix domain socket IPC, requiring no changes to game modules. - Add opt-in DAEMON_NACL_BOX64_EMULATION CMake option (OFF by default, Linux only) that remaps NACL_ARCH to amd64 - Replace architecture-specific NaCl dummy defines with a generic fallback for any unsupported architecture - Prepend box64 to NaCl loader args with PATH resolution - Skip bootstrap helper (incompatible with Box64 double-exec) - Disable platform qualification for emulated loader - Add cvars: vm.box64.path, workaround.box64.disableBootstrap, workaround.box64.disableQualification
1 parent 29446db commit 774f84f

File tree

3 files changed

+105
-5
lines changed

3 files changed

+105
-5
lines changed

cmake/DaemonArchitecture.cmake

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ if (LINUX OR FREEBSD)
8181
# The nexe is system agnostic so there should be no difference with armel.
8282
set(NACL_ARCH "armhf")
8383
endif()
84+
85+
set(BOX64_USAGE ppc64el riscv64)
86+
if (ARCH IN_LIST BOX64_USAGE)
87+
option(DAEMON_NACL_BOX64_EMULATION "Use Box64 to emulate x86_64 NaCl loader on unsupported platforms" ON)
88+
if (DAEMON_NACL_BOX64_EMULATION)
89+
# Use Box64 to run x86_64 NaCl loader and amd64 nexe.
90+
# Box64 must be installed and available in PATH at runtime.
91+
set(NACL_ARCH "amd64")
92+
add_definitions(-DDAEMON_NACL_BOX64_EMULATION)
93+
endif()
94+
endif()
8495
elseif(APPLE)
8596
if ("${ARCH}" STREQUAL arm64)
8697
# You can get emulated NaCl going like this:

cmake/DaemonNacl.cmake

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,12 @@ else()
7171
add_definitions( -DNACL_BUILD_SUBARCH=32 )
7272
elseif( NACL_ARCH STREQUAL "armhf" )
7373
add_definitions( -DNACL_BUILD_ARCH=arm )
74-
elseif( NACL_ARCH STREQUAL "ppc64el" OR NACL_ARCH STREQUAL "ppc64" )
75-
# NaCl does not support PPC, but these defines must be set for native
76-
# builds. Use dummy x86 values as PNaCl does for arch-independent builds.
74+
else()
75+
# NaCl does not support this architecture natively, but these defines must
76+
# be set because nacl_config.h is included unconditionally. Use dummy x86
77+
# values as PNaCl does for architecture-independent builds.
7778
add_definitions( -DNACL_BUILD_ARCH=x86 )
7879
add_definitions( -DNACL_BUILD_SUBARCH=64 )
79-
else()
80-
message(WARNING "Unknown architecture ${NACL_ARCH}")
8180
endif()
8281
endif()
8382

src/engine/framework/VirtualMachine.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,54 @@ static Cvar::Cvar<bool> workaround_naclSystem_freebsd_disableQualification(
7373
"Disable platform qualification when running Linux NaCl loader on FreeBSD through Linuxulator",
7474
Cvar::NONE, true);
7575

76+
#if defined(DAEMON_NACL_BOX64_EMULATION)
77+
static Cvar::Cvar<bool> workaround_box64_disableQualification(
78+
"workaround.box64.disableQualification",
79+
"Disable platform qualification when running amd64 NaCl loader under Box64 emulation",
80+
Cvar::NONE, true);
81+
82+
static Cvar::Cvar<bool> workaround_box64_disableBootstrap(
83+
"workaround.box64.disableBootstrap",
84+
"Disable NaCl bootstrap helper when using Box64 emulation",
85+
Cvar::NONE, true);
86+
87+
static Cvar::Cvar<std::string> vm_box64_path(
88+
"vm.box64.path",
89+
"Path to the box64 binary for NaCl emulation (empty = search PATH)",
90+
Cvar::NONE, "");
91+
92+
// Resolve box64 binary path by searching PATH if not explicitly set.
93+
static std::string ResolveBox64Path() {
94+
std::string path = vm_box64_path.Get();
95+
if (!path.empty()) {
96+
return path;
97+
}
98+
99+
const char* envPath = getenv("PATH");
100+
if (!envPath) {
101+
Sys::Error("Box64 emulation is enabled but PATH is not set and vm.box64.path is empty.");
102+
}
103+
104+
std::string pathStr(envPath);
105+
size_t start = 0;
106+
while (start < pathStr.size()) {
107+
size_t end = pathStr.find(':', start);
108+
if (end == std::string::npos) {
109+
end = pathStr.size();
110+
}
111+
std::string candidate = pathStr.substr(start, end - start) + "/box64";
112+
if (access(candidate.c_str(), X_OK) == 0) {
113+
return candidate;
114+
}
115+
start = end + 1;
116+
}
117+
118+
Sys::Error("Box64 emulation is enabled but 'box64' was not found in PATH. "
119+
"Install Box64 or set vm.box64.path to the full path of the box64 binary.");
120+
return ""; // unreachable
121+
}
122+
#endif
123+
76124
static Cvar::Cvar<bool> vm_nacl_qualification(
77125
"vm.nacl.qualification",
78126
"Enable NaCl loader platform qualification",
@@ -255,6 +303,9 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
255303
char rootSocketRedir[32];
256304
std::string module, nacl_loader, irt, bootstrap, modulePath, verbosity;
257305
FS::File stderrRedirect;
306+
#if defined(DAEMON_NACL_BOX64_EMULATION)
307+
std::string box64Path;
308+
#endif
258309
#if !defined(_WIN32) || defined(_WIN64)
259310
constexpr bool win32Force64Bit = false;
260311
#else
@@ -304,6 +355,33 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
304355
}
305356

306357
#if defined(__linux__) || defined(__FreeBSD__)
358+
#if defined(DAEMON_NACL_BOX64_EMULATION)
359+
/* Use Box64 to run the x86_64 NaCl loader on non-x86 architectures.
360+
The bootstrap helper uses a double-exec pattern that Box64 cannot handle,
361+
so we skip it and prepend "box64" to the nacl_loader command instead. */
362+
if (!workaround_box64_disableBootstrap.Get() && vm_nacl_bootstrap.Get()) {
363+
bootstrap = FS::Path::Build(naclPath, "nacl_helper_bootstrap");
364+
365+
if (!FS::RawPath::FileExists(bootstrap)) {
366+
Sys::Error("NaCl bootstrap helper not found: %s", bootstrap);
367+
}
368+
369+
args.push_back(bootstrap.c_str());
370+
args.push_back(nacl_loader.c_str());
371+
args.push_back("--r_debug=0xXXXXXXXXXXXXXXXX");
372+
args.push_back("--reserved_at_zero=0xXXXXXXXXXXXXXXXX");
373+
} else {
374+
if (workaround_box64_disableBootstrap.Get()) {
375+
Log::Notice("Skipping NaCl bootstrap helper for Box64 emulation.");
376+
} else {
377+
Log::Warn("Not using NaCl bootstrap helper.");
378+
}
379+
box64Path = ResolveBox64Path();
380+
Log::Notice("Using Box64 emulator: %s", box64Path);
381+
args.push_back(box64Path.c_str());
382+
args.push_back(nacl_loader.c_str());
383+
}
384+
#else
307385
if (vm_nacl_bootstrap.Get()) {
308386
#if defined(DAEMON_ARCH_arm64)
309387
bootstrap = FS::Path::Build(naclPath, "nacl_helper_bootstrap-armhf");
@@ -323,6 +401,7 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
323401
Log::Warn("Not using NaCl bootstrap helper.");
324402
args.push_back(nacl_loader.c_str());
325403
}
404+
#endif
326405
#else
327406
Q_UNUSED(bootstrap);
328407
args.push_back(nacl_loader.c_str());
@@ -381,6 +460,17 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
381460
enableQualification = false;
382461
}
383462
#endif
463+
464+
#if defined(DAEMON_NACL_BOX64_EMULATION)
465+
/* When running the amd64 NaCl loader under Box64, the loader's
466+
platform qualification will fail because the CPU is not actually x86_64.
467+
Disabling qualification allows the emulated loader to proceed. */
468+
469+
if (workaround_box64_disableQualification.Get()) {
470+
Log::Warn("Disabling NaCL platform qualification for Box64 emulation.");
471+
enableQualification = false;
472+
}
473+
#endif
384474
}
385475
else {
386476
Log::Warn("Not using NaCl platform qualification.");

0 commit comments

Comments
 (0)