From 1876d6b9236fb69053a638c4ea2f3c4f8f117b4c Mon Sep 17 00:00:00 2001 From: Will Campbell Date: Tue, 25 Nov 2025 23:41:42 -0800 Subject: [PATCH 1/3] Remap cmdline --- Source/Tools/CommonTools/CodeLoader.h | 11 --- Source/Tools/FEXInterpreter/ELFCodeLoader.h | 68 ++++++++++++++++--- .../EmulatedFiles/EmulatedFiles.cpp | 15 ---- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/Source/Tools/CommonTools/CodeLoader.h b/Source/Tools/CommonTools/CodeLoader.h index f10b641d6..d77b2e031 100644 --- a/Source/Tools/CommonTools/CodeLoader.h +++ b/Source/Tools/CommonTools/CodeLoader.h @@ -54,17 +54,6 @@ public: return ApplicationArgs; } - /** - * Writes out the arguments in the format of /proc/self/cmdline - */ - virtual void WriteCmdlineFD(int32_t fd) const { - const auto& Args = GetApplicationArguments(); - // cmdline is an array of null terminated arguments - for (const auto& Arg : Args) { - write(fd, Arg.c_str(), Arg.size() + 1); // Add 1 to include the null terminator - } - } - protected: fextl::vector ApplicationArgs; }; diff --git a/Source/Tools/FEXInterpreter/ELFCodeLoader.h b/Source/Tools/FEXInterpreter/ELFCodeLoader.h index 40ec1c9a5..4e13d3d50 100644 --- a/Source/Tools/FEXInterpreter/ELFCodeLoader.h +++ b/Source/Tools/FEXInterpreter/ELFCodeLoader.h @@ -29,7 +29,10 @@ #include #include #include +#include #include +#include +#include #define PAGE_START(x) ((x) & ~(uintptr_t)(4095)) #define PAGE_OFFSET(x) ((x) & 4095) @@ -707,6 +710,60 @@ public: *AuxTabSize = sizeof(AuxType) * AuxVariables.size(); } + // Get the current memory map from /proc/self/stat + static bool GetCurrentMap(struct prctl_mm_map& map) { + FILE* f = fopen("/proc/self/stat", "r"); + if (!f) { + return false; + } + + // See man proc_pid_stat + int items_read = fscanf(f, + "%*d %*s %*c %*d %*d " // 1 to 5 + "%*d %*d %*d %*u %*u " // 6 to 10 + "%*u %*u %*u %*u %*u " // 11 to 15 + "%*d %*d %*d %*d %*d " // 16 to 20 + "%*d %*u %*u %*d %*u " // 21 to 25 + "%llu %llu %llu %*u %*u " // 26 to 30 + "%*u %*u %*u %*u %*u " // 31 to 35 + "%*u %*u %*d %*d %*u " // 36 to 40 + "%*u %*u %*u %*d %llu " // 40 to 45 + "%llu %llu %llu %llu %llu " // 46 to 50 + "%llu", // 51 + &map.start_code, &map.end_code, &map.start_stack, &map.start_data, &map.end_data, &map.start_brk, + &map.arg_start, &map.arg_end, &map.env_start, &map.env_end); + fclose(f); + + if (items_read != 10) { + return false; + } + + map.brk = reinterpret_cast(sbrk(0)); + + // The kernel will leave these values unchanged, see implementation in sys.c + map.auxv = NULL; + map.auxv_size = 0; + map.exe_fd = -1; + + return true; + } + + // Point the OS to our new stack's argument data + void RemapArgumentData(uintptr_t NewArgStart, uint64_t ArgSize) { + struct prctl_mm_map map {}; + if (GetCurrentMap(map)) { + map.arg_start = NewArgStart; + map.arg_end = NewArgStart + ArgSize; + + int r = prctl(PR_SET_MM, PR_SET_MM_MAP, &map, sizeof(map), 0L); + if (r != 0) { + LogMan::Msg::EFmt("Failed to remap /proc/pid/cmdline data. prctl failed: result {}, errno {}", r, errno); + } + } else { + LogMan::Msg::EFmt("Failed to remap /proc/pid/cmdline data. GetCurrentMap failed. "); + } + } + // Setups the stack initial data (argv, envp, auxv) void SetupStack() { StackPointer += StackSize(); @@ -803,6 +860,8 @@ public: SetupPointers(StackPointer, AuxVOffset, ArgumentOffset, EnvpOffset, ApplicationArgs, EnvironmentVariables, AuxVariables, &AuxTabBase, &AuxTabSize); } + + RemapArgumentData(StackPointer + ArgumentOffset, ArgumentBackingSize); } fextl::vector GetExecveArguments() const override { @@ -816,15 +875,6 @@ public: }; } - void WriteCmdlineFD(int32_t fd) const override { - // '/proc/self/cmdline' typically maps to the stack's argv data. - // Applications can write to this to change the cmdline value, - // but in FEX they'll write to the emulated stack rather than the OS'. - // - // Expose the contents of the emulated stack's argument data. - write(fd, reinterpret_cast(StackPointer + ArgumentOffset), ArgumentBackingSize); - } - uint64_t GetBaseOffset() const override { return BaseOffset; } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp index e61f86cd6..1180be3cf 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp @@ -586,21 +586,6 @@ EmulatedFDManager::EmulatedFDManager(FEXCore::Context::Context* ctx) FDReadCreators[procAuxv] = &EmulatedFDManager::ProcAuxv; FDReadCreators["/proc/self/auxv"] = &EmulatedFDManager::ProcAuxv; - auto cmdline_handler = [&](FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode) -> int32_t { - const int FD = GenTmpFD(pathname, flags); - const auto* CodeLoader = FEX::HLE::_SyscallHandler->GetCodeLoader(); - CodeLoader->WriteCmdlineFD(FD); - - // One additional null terminator to finish the list - lseek(FD, 0, SEEK_SET); - SealTmpFD(FD); - return FD; - }; - - FDReadCreators["/proc/self/cmdline"] = cmdline_handler; - fextl::string procCmdLine = fextl::fmt::format("/proc/{}/cmdline", getpid()); - FDReadCreators[procCmdLine] = cmdline_handler; - if (ThreadsConfig > 1) { cpus_online = fextl::fmt::format("0-{}", ThreadsConfig - 1); } else { From eb47ef43a7b50d5988db14f520b94222bfa52e13 Mon Sep 17 00:00:00 2001 From: Will Campbell Date: Wed, 3 Dec 2025 19:59:13 -0800 Subject: [PATCH 2/3] Read from a fd rather than a FILE --- Source/Tools/FEXInterpreter/ELFCodeLoader.h | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Source/Tools/FEXInterpreter/ELFCodeLoader.h b/Source/Tools/FEXInterpreter/ELFCodeLoader.h index 4e13d3d50..927fa72ac 100644 --- a/Source/Tools/FEXInterpreter/ELFCodeLoader.h +++ b/Source/Tools/FEXInterpreter/ELFCodeLoader.h @@ -32,7 +32,6 @@ #include #include #include -#include #define PAGE_START(x) ((x) & ~(uintptr_t)(4095)) #define PAGE_OFFSET(x) ((x) & 4095) @@ -712,13 +711,24 @@ public: // Get the current memory map from /proc/self/stat static bool GetCurrentMap(struct prctl_mm_map& map) { - FILE* f = fopen("/proc/self/stat", "r"); - if (!f) { + int fd = open("/proc/self/stat", O_RDONLY | O_CLOEXEC); + if (fd < 0) { return false; } + // /proc/self/stat has 52 fields of at most 20 digits each (UINT64_MAX). + // 52*20 = 1040, so 2048 is a conservative upper bound + char stat_buffer[2048]; + ssize_t bytes_read = read(fd, stat_buffer, sizeof(stat_buffer) - 1); + close(fd); + + if (bytes_read <= 0) { + return false; + } + stat_buffer[bytes_read] = '\0'; // So we don't read past to garbage data + // See man proc_pid_stat - int items_read = fscanf(f, + int items_read = sscanf(stat_buffer, "%*d %*s %*c %*d %*d " // 1 to 5 "%*d %*d %*d %*u %*u " // 6 to 10 "%*u %*u %*u %*u %*u " // 11 to 15 @@ -732,7 +742,6 @@ public: "%llu", // 51 &map.start_code, &map.end_code, &map.start_stack, &map.start_data, &map.end_data, &map.start_brk, &map.arg_start, &map.arg_end, &map.env_start, &map.env_end); - fclose(f); if (items_read != 10) { return false; From d42fbb3d4d8a84fc28f8ff239270b53abc793641 Mon Sep 17 00:00:00 2001 From: Will Campbell Date: Thu, 4 Dec 2025 07:54:45 -0800 Subject: [PATCH 3/3] Use LoadFileToBuffer + cleanup --- Source/Tools/FEXInterpreter/ELFCodeLoader.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Source/Tools/FEXInterpreter/ELFCodeLoader.h b/Source/Tools/FEXInterpreter/ELFCodeLoader.h index 927fa72ac..e707517fb 100644 --- a/Source/Tools/FEXInterpreter/ELFCodeLoader.h +++ b/Source/Tools/FEXInterpreter/ELFCodeLoader.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -711,21 +712,14 @@ public: // Get the current memory map from /proc/self/stat static bool GetCurrentMap(struct prctl_mm_map& map) { - int fd = open("/proc/self/stat", O_RDONLY | O_CLOEXEC); - if (fd < 0) { - return false; - } // /proc/self/stat has 52 fields of at most 20 digits each (UINT64_MAX). // 52*20 = 1040, so 2048 is a conservative upper bound char stat_buffer[2048]; - ssize_t bytes_read = read(fd, stat_buffer, sizeof(stat_buffer) - 1); - close(fd); + ssize_t bytes_read = FEXCore::FileLoading::LoadFileToBuffer("/proc/self/stat", stat_buffer); - if (bytes_read <= 0) { - return false; - } - stat_buffer[bytes_read] = '\0'; // So we don't read past to garbage data + // Ensure we don't read past the end into garbage data + stat_buffer[std::clamp(bytes_read, 0L, static_cast(sizeof(stat_buffer)) - 1)] = '\0'; // See man proc_pid_stat int items_read = sscanf(stat_buffer, @@ -766,10 +760,10 @@ public: int r = prctl(PR_SET_MM, PR_SET_MM_MAP, &map, sizeof(map), 0L); if (r != 0) { - LogMan::Msg::EFmt("Failed to remap /proc/pid/cmdline data. prctl failed: result {}, errno {}", r, errno); + LogMan::Msg::EFmt("Failed to remap /proc/pid/cmdline data (prctl failed: result {}, errno {})", r, errno); } } else { - LogMan::Msg::EFmt("Failed to remap /proc/pid/cmdline data. GetCurrentMap failed. "); + LogMan::Msg::EFmt("Failed to remap /proc/pid/cmdline data (GetCurrentMap failed)"); } }