Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 24 additions & 177 deletions src/engine/framework/VirtualMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ static Cvar::Cvar<bool> workaround_naclSystem_freebsd_disableQualification(
// Many BSDs have a Linuxulator, for now we only tested one.
#if defined(__linux__) || defined(__FreeBSD__)
#define DAEMON_NACL_RUNTIME_LINUX
#if YOKAI_ARCH_I686 || YOKAI_ARCH_ARM64 || YOKAI_ARCH_ARMHF
#define DAEMON_NACL_BOOSTRAP_ENABLED
#endif // !(YOKAI_ARCH_I686 || YOKAI_ARCH_ARM64 || YOKAI_ARCH_ARMHF)
#endif // defined(__linux__) || defined(DAEMON_LINUXULATOR)
#endif // defined(DAEMON_NACL_RUNTIME_ENABLED)

Expand Down Expand Up @@ -182,6 +179,7 @@ static Cvar::Cvar<bool> vm_nacl_qualification(

namespace VM {

#if defined(DAEMON_NACL_RUNTIME_LINUX)
// https://github.com/Unvanquished/Unvanquished/issues/944#issuecomment-744454772
static void CheckMinAddressSysctlTooLarge()
{
Expand All @@ -206,6 +204,7 @@ static void CheckMinAddressSysctlTooLarge()
}
#endif // __linux__
}
#endif // DAEMON_NACL_RUNTIME_LINUX

#if defined(DAEMON_NACL_RUNTIME_LINUX) && (defined(YOKAI_ARCH_ARM64) || defined(YOKAI_ARCH_ARMHF))
static bool OnArm64()
Expand Down Expand Up @@ -358,86 +357,6 @@ static std::pair<Sys::OSHandle, IPC::Socket> InternalLoadModule(std::pair<IPC::S
#endif
}

#if defined(DAEMON_NACL_RUNTIME_LINUX) && defined(YOKAI_ARCH_ARM64)
enum class ExeSupport
{
Supported,
Unsupported,
Unknown,
};

static void SilenceOutput()
{
int devnull = open("/dev/null", O_WRONLY);

if (devnull < 0) {
_exit(200);
}

dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);

close(devnull);
}

static ExeSupport CheckExeSupport(Str::StringRef probe)
{
pid_t pid = fork();
if (pid < 0) {
return ExeSupport::Unknown;
}

if (pid == 0) {
SilenceOutput();

char *const argv[] = {
const_cast<char *>(probe.data()),
nullptr,
};

execve(probe.data(), argv, environ);

switch (errno)
{
case ENOEXEC:
// The kernel cannot execute the probe.
_exit(100);

case ENOENT:
case EACCES:
// Missing or inaccessible probe.
_exit(101);

default:
_exit(102);
}
}

int status;
if (waitpid(pid, &status, 0) < 0) {
return ExeSupport::Unknown;
}

if (!WIFEXITED(status)) {
return ExeSupport::Unknown;
}

switch (WEXITSTATUS(status))
{
case 100:
return ExeSupport::Unsupported;

case 101:
case 102:
return ExeSupport::Unknown;

default:
// The probe has been executed properly.
return ExeSupport::Supported;
}
}
#endif // defined(DAEMON_NACL_RUNTIME_LINUX) && defined(NACL_ARCH_ARM64)

#if defined(DAEMON_NACL_RUNTIME_ENABLED)
static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket, IPC::Socket> pair, Str::StringRef name, bool debug, bool extract, int debugLoader) {
const std::string& libPath = FS::GetLibPath();
Expand All @@ -462,80 +381,15 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
bool i686ForceAmd64 = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
#endif // !( !defined(_WIN32) || defined(_WIN64) )

#if defined(DAEMON_NACL_RUNTIME_LINUX) && defined(YOKAI_ARCH_ARM64)
constexpr bool hasMultiarch = true;
#else
constexpr bool hasMultiarch = false;
#endif

std::string bootstrapPath;

#if defined(DAEMON_NACL_BOOSTRAP_ENABLED)
bool hasBootstrap = true;
#else // !defined(DAEMON_NACL_BOOSTRAP_ENABLED)
constexpr bool hasBootstrap = false;
Q_UNUSED(bootstrapPath);
#endif // !defined(DAEMON_NACL_BOOSTRAP_ENABLED)

std::string box64Path;

#if defined(DAEMON_NACL_BOX64_EMULATION)
constexpr bool hasBox64 = true;
#else // !defined(DAEMON_NACL_BOX64_EMULATION)
constexpr bool hasBox64 = false;
Q_UNUSED(box64Path);
#endif // !defined(DAEMON_NACL_BOX64_EMULATION)

#if defined(DAEMON_NACL_RUNTIME_LINUX)
#if defined(YOKAI_ARCH_ARM64)
/* We later turn this boolean off when we detect armhf support is not
provided on arm64. */
bool hasArmhf = true;
#elif defined(YOKAI_ARCH_ARMHF)
constexpr bool hasArmhf = false;
#endif // !defined(YOKAI_ARCH_ARMHF)
#endif // !defined(DAEMON_NACL_RUNTIME_LINUX)

bool useMultiarch = hasMultiarch;
bool useBox64 = hasBox64;

#if defined(DAEMON_NACL_RUNTIME_LINUX) && defined(YOKAI_ARCH_ARM64)
const std::string libs_linux_armhf = "lib-armhf";
const std::string libld_linux_armhf = "ld-linux-armhf";

std::string libsDirPath = FS::Path::Build(libPath, libs_linux_armhf);
std::string libLoaderPath = FS::Path::Build(libsDirPath, libld_linux_armhf);

switch (CheckExeSupport(libLoaderPath))
{
case ExeSupport::Supported:
Log::Notice("Working multiarch support.");
break;

case ExeSupport::Unsupported:
Log::Notice("Unsupported multiarch.");
hasArmhf = false;
break;

case ExeSupport::Unknown:
Log::Warn("Cannot determine multiarch support.");
hasArmhf = false;
break;
}

if (!hasArmhf) {
useMultiarch = false;
}
#endif // defined(DAEMON_NACL_RUNTIME_LINUX) && defined(YOKAI_ARCH_ARM64)

if (useMultiarch && useBox64) {
Log::Notice("Preferring multiarch over Box64 emulation.");
useBox64 = false;
}

bool useBootstrap = hasBootstrap && !useBox64;

std::string arch = (useBox64 || i686ForceAmd64) ? "amd64" : DAEMON_NACL_ARCH_STRING;
std::string arch = i686ForceAmd64 ? "amd64" : DAEMON_NACL_ARCH_STRING;

std::string moduleName = Str::Format("%s-%s.nexe", name, arch);
std::string irt = FS::Path::Build(naclPath, Str::Format("irt_core-%s.nexe", arch));
Expand Down Expand Up @@ -574,39 +428,35 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
Sys::Error("NaCl integrated runtime not found: %s", irt);
}

if (useBootstrap) {
CheckMinAddressSysctlTooLarge();

bootstrapPath = FS::Path::Build(naclPath, Str::Format("nacl_helper_bootstrap-%s", arch));

if (FS::RawPath::FileExists(bootstrapPath)) {
args.push_back(bootstrapPath.c_str());
} else {
Log::Warn("NaCl bootstrap helper not found: %s", bootstrapPath);
useBootstrap = false;
}
}
else {
if (hasBootstrap) {
Log::Notice("Not using NaCl bootstrap helper.");
}

#if defined(DAEMON_NACL_BOX64_EMULATION)
if (useBox64) {
box64Path = ResolveBox64Path(naclPath);
Log::Notice("Using Box64 emulator: %s", box64Path);
args.push_back(box64Path.c_str());
inheritEnvironment = true;
}
std::string box64Path = ResolveBox64Path(naclPath);
Log::Notice("Using Box64 emulator: %s", box64Path);
args.push_back(box64Path.c_str());
inheritEnvironment = true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will use box64 unconditionally even if another architecture is supported and preferred, and will even attempt to run the armhf nexe with box64.

@illwieckz illwieckz Nov 10, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the approval noise, GitHub switched me to some new interface that is messed up and I don't even know how to dismiss the approval neither how to return to the good old interface.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will use box64 unconditionally even if another architecture is supported and preferred, and will even attempt to run the armhf nexe with box64.

There is currently no possibility in CMake to have more than one supported NaCl runtime for any Linux platform. armhf_usage and box64_usage do not intersect. If box64 is enabled, it is the only way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything is fully implemented since maybe 6 months, and what is not merged yet is just waiting in line to be.

There is no reason to re-introduce limitations that were already removed, and to reintroduce incompatibilities that were already solved.

#endif
}

args.push_back(nacl_loader.c_str());
#if defined(DAEMON_NACL_RUNTIME_LINUX)
std::string bootstrapPath;
// Mutually exclusive with box64 since the NaCl arch is amd64 there.
bool useBootstrap = arch != "amd64";

if (useBootstrap) {
CheckMinAddressSysctlTooLarge();

bootstrapPath = FS::Path::Build(naclPath, Str::Format("nacl_helper_bootstrap-%s", arch));
if (!FS::RawPath::FileExists(bootstrapPath)) {
Sys::Error("NaCl bootstrap helper not found: %s", bootstrapPath);
}
args.push_back(bootstrapPath.c_str());
args.push_back(nacl_loader.c_str());
args.push_back("--r_debug=0xXXXXXXXXXXXXXXXX");
args.push_back("--reserved_at_zero=0xXXXXXXXXXXXXXXXX");
} else {
args.push_back(nacl_loader.c_str());
}
#else
args.push_back(nacl_loader.c_str());
#endif

bool enableQualification = vm_nacl_qualification.Get();

Expand All @@ -616,8 +466,6 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,

#if defined(DAEMON_NACL_RUNTIME_LINUX) && (defined(YOKAI_ARCH_ARM64) || defined(YOKAI_ARCH_ARMHF))
if (enableQualification
&& hasArmhf
&& !useBox64
&& workaround_naclArchitecture_arm64_disableQualification.Get()) {
/* This is required to run armhf NaCl loader on arm64 kernel
otherwise nexe loading fails with this message:
Expand All @@ -643,7 +491,6 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,

#if defined(DAEMON_NACL_BOX64_EMULATION)
if (enableQualification
&& useBox64
&& workaround_box64_disableQualification.Get()) {
/* When running the amd64 NaCl loader under Box64, the loader's
platform qualification will fail because the CPU is not actually x86_64.
Expand Down
Loading