diff --git a/core/libs/prx/libc/src/Environment.cpp b/core/libs/prx/libc/src/Environment.cpp index 03996644bf..cbb171729b 100644 --- a/core/libs/prx/libc/src/Environment.cpp +++ b/core/libs/prx/libc/src/Environment.cpp @@ -9,10 +9,6 @@ #include #include "prx/libc/include/general/VabiMacros.hpp" -#ifndef _WIN32 -extern char** environ; -#endif - namespace { struct EnvironmentEntry { std::unique_ptr owned; @@ -26,20 +22,7 @@ struct EnvironmentEntry { }; std::mutex environmentMutex; std::vector& Environment() { - // Seed once from the host. Guest mutations remain local to the guest runtime. - static std::vector entries = [] { - std::vector result; -#ifdef _WIN32 - char** source = _environ; -#else - char** source = environ; -#endif - if (source) for (; *source; ++source) { - if (**source && **source != '=' && std::strchr(*source, '=')) - result.push_back({std::make_unique(*source), nullptr}); - } - return result; - }(); + static std::vector entries; return entries; } bool ValidEnvironmentName(const char* name) { diff --git a/core/libs/tests/GuestEnvironment.cpp b/core/libs/tests/GuestEnvironment.cpp index 7bd0c7fb88..46e2b2b11b 100644 --- a/core/libs/tests/GuestEnvironment.cpp +++ b/core/libs/tests/GuestEnvironment.cpp @@ -13,18 +13,25 @@ static void Require(bool value) { if (!value) std::abort(); } int main() { const char* key = "ANYPS5_GUEST_ENV_TEST_4C27"; #ifdef _WIN32 - Require(_putenv_s(key, "inherited") == 0); + Require(_putenv_s(key, "host") == 0); #else - Require(::setenv(key, "inherited", 1) == 0); + Require(::setenv(key, "host", 1) == 0); #endif - Require(std::strcmp(getenv_nid_postfix(key), "inherited") == 0); + Require(getenv_nid_postfix(key) == nullptr); +#ifdef _WIN32 + Require(_putenv_s("HOME", "C:\\Users\\host") == 0); +#else + Require(::setenv("HOME", "/home/host", 1) == 0); +#endif + for (const char* host : {"HOME", "PATH"}) Require(std::getenv(host) != nullptr && getenv_nid_postfix(host) == nullptr); + Require(setenv_nid_postfix(key, "inherited", 0) == 0); Require(setenv_nid_postfix(key, "kept out", 0) == 0); Require(std::strcmp(getenv_nid_postfix(key), "inherited") == 0); char value[] = "copied"; Require(setenv_nid_postfix(key, value, 1) == 0); value[0] = 'X'; Require(std::strcmp(getenv_nid_postfix(key), "copied") == 0); - Require(std::strcmp(std::getenv(key), "inherited") == 0); + Require(std::strcmp(std::getenv(key), "host") == 0); Require(setenv_nid_postfix(key, "", 1) == 0); Require(getenv_nid_postfix(key) && *getenv_nid_postfix(key) == 0); char borrowed[] = "ANYPS5_GUEST_ENV_TEST_4C27=one"; diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 37468e999c..907400a5c0 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -50,6 +50,7 @@ - [sinhf](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `sinhf` of the `libc.prx` bundled with PPSA28319 (disassembly): FreeBSD msun's [`e_sinhf.c`](https://github.com/freebsd/freebsd-src/blob/5aa554c7e5b6fd9042986df07f12ac5687e41392/lib/msun/src/e_sinhf.c) from before `__ldexp_expf`, which squares `expf(|x| / 2)` between `logf(FLT_MAX)` and the overflow threshold, calling msun's [`expm1f`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/s_expm1f.c) and [`expf`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/e_expf.c), whose paths for the arguments it passes are inlined here. Past the threshold Sony's error helper sets `errno` to `ERANGE` before returning `x * 1e37`; msun itself sets no `errno`. That code, run on a Windows host, gives the same bits as this implementation for every finite argument up to the threshold, of both signs; it was not run on Linux. That the system `libSceLibcInternal`, which Ruffle (PPSA68091) and RetroArch (PPSA99169) import while bundling a zero-return `libc.prx`, has the same code is assumed - [asinl](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `asinl` of the `libc.prx` bundled with PPSA28319 (disassembly), FreeBSD msun's [`e_asinl.c`](https://github.com/freebsd/freebsd-src/blob/9f0432041b8fb69832f10c32da50a67f34feeb61/lib/msun/src/e_asinl.c) with the ld80 [`invtrig.c`](https://github.com/freebsd/freebsd-src/blob/9f0432041b8fb69832f10c32da50a67f34feeb61/lib/msun/ld80/invtrig.c) coefficients, which the binary holds as the same 80-bit constants. For |x| > 1, the infinities and NaN, Sony's error helper raises `FE_INVALID` and sets `errno` to `EDOM` before returning `(x - x) / (x - x)`, so a NaN argument also sets `EDOM`; msun itself sets no `errno`. 20 million random arguments in (-1, 1) give the same bits as that code run on a Windows host. The result needs the x87 precision control at 64 bits, as FreeBSD amd64 starts threads (control word `0x37f`), but guest threads do not set it: on Windows a thread started by `_beginthreadex` runs at 53 bits (`0x27f`), where `asinl(0.75)` returns mantissa `0xd91a98ae3406e000` instead of `0xd91a98ae3406e041`. The system `libSceLibcInternal` that RetroArch (PPSA99169) imports is assumed to have the same code - [atanhf](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `atanhf` of the `libc.prx` bundled with PPSA28319 (disassembly): FreeBSD msun's [`e_atanhf.c`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/e_atanhf.c) calling msun's [`log1pf`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/s_log1pf.c), whose paths for the positive arguments it passes are inlined here; the exported `log1pf` is still the host's. For |x| > 1, the infinities and NaN, Sony's error helper raises `FE_INVALID` and sets `errno` to `EDOM` before returning `(x - x) / (x - x)`, and for |x| = 1 it raises `FE_DIVBYZERO` and sets `errno` to `ERANGE` before returning `x / 0`; msun itself sets no `errno`. That code, run on a Windows host, gives the same bits, `errno` and floating-point exceptions as this implementation for all 2^32 arguments; it was not run on Linux. That the system `libSceLibcInternal`, which Dusklight (PPSA99305) imports while bundling a zero-return `libc.prx`, has the same code is assumed +- [getenv, setenv, unsetenv, putenv](../../../core/libs/prx/libc/src/Environment.cpp) (libc) keep one guest environment that starts empty, like the `environ` libkernel exports, and never reads or changes the host's. A title sees only the variables it sets itself; host variables such as `HOME` and `PATH` name host paths a guest cannot use (PS5MC, PPSA99420, exits with `XBMC_Run returned -1` when it gets the host's `HOME`). Whether the console starts a title with any variables set is not measured - On macOS the [write watch](../../../core/libs/prx/libc/src/GuestWriteWatch.cpp) write-protects watched pages and records the first write to each from a SIGBUS/SIGSEGV handler, because macOS has no counterpart to `userfaultfd` write-protect or `MEM_WRITE_WATCH`. The protection is real, so: - a system call that writes into a protected page fails with `EFAULT` instead of faulting. `GuestArena::HostWrite` lifts the protection for the calls it brackets (file reads and socket receives); a host call that writes guest memory without it (`clock_gettime` and the like into a watched page) fails; - code that changes the protection of watched pages, or maps over them, has to call `GuestWriteWatchProtectionChanged`, and code that asks the kernel for a page's protection has to ask `GuestWriteWatchProtection` first, since a watched page is read-only between a collection and the next write. Nothing calls either yet: the macOS memory paths are not in the tree;