mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-28 12:05:06 +00:00
guest_system_load_exec_standard_error compared the host standard error of the re-executed child with "started <mode>\n" exactly. On a Linux kernel without asynchronous userfaultfd write protection (before 6.7, such as WSL2's 6.6) libc prints "[memory] write watch unavailable: ..." to the host standard error at start-up, so the comparison failed and the test aborted on every run. That one diagnostic line is now skipped when it comes first; the rest of the host stream must still be the child's line exactly, and the guest pipe must still be empty. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01En5cnt6RwCiNXDJwm9t8Tf
84 lines
2.7 KiB
C++
84 lines
2.7 KiB
C++
#include "prx/libkernel/include/Api.hpp"
|
|
#include "prx/libSceSystemService/include/SystemService.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
namespace {
|
|
|
|
constexpr const char* Marker = "APS5_LOAD_EXEC_STANDARD_ERROR";
|
|
constexpr const char* AfterLoadExec = "--after-load-exec";
|
|
|
|
void Require(bool value) {
|
|
if (!value) std::abort();
|
|
}
|
|
|
|
std::string Drain(int descriptor) {
|
|
std::string text;
|
|
char buffer[4096];
|
|
for (ssize_t count; (count = ::read(descriptor, buffer, sizeof(buffer))) > 0;) text.append(buffer, static_cast<std::size_t>(count));
|
|
::close(descriptor);
|
|
return text;
|
|
}
|
|
|
|
bool HostOutputIs(const std::string& text, const std::string& line) {
|
|
constexpr std::string_view WriteWatchUnavailable = "[memory] write watch unavailable: ";
|
|
std::size_t begin = 0;
|
|
if (text.compare(0, WriteWatchUnavailable.size(), WriteWatchUnavailable) == 0) {
|
|
const std::size_t end = text.find('\n');
|
|
if (end == std::string::npos) return false;
|
|
begin = end + 1;
|
|
}
|
|
return text.compare(begin, std::string::npos, line) == 0;
|
|
}
|
|
|
|
void Run(const char* mode, const char* const* arguments) {
|
|
int host[2] = {-1, -1};
|
|
int guest[2] = {-1, -1};
|
|
Require(::pipe(host) == 0);
|
|
Require(::pipe(guest) == 0);
|
|
const pid_t child = ::fork();
|
|
Require(child >= 0);
|
|
if (child == 0) {
|
|
::close(host[0]);
|
|
::close(guest[0]);
|
|
if (::dup2(host[1], 2) != 2) ::_exit(70);
|
|
::close(host[1]);
|
|
if (dup2_nid_postfix(guest[1], 2) != 2) ::_exit(71);
|
|
::close(guest[1]);
|
|
if (::setenv(Marker, mode, 1) != 0) ::_exit(72);
|
|
sceSystemServiceLoadExec("/app0/eboot.bin", arguments);
|
|
::_exit(73);
|
|
}
|
|
::close(host[1]);
|
|
::close(guest[1]);
|
|
const std::string hostText = Drain(host[0]);
|
|
const std::string guestText = Drain(guest[0]);
|
|
int status = 0;
|
|
Require(::waitpid(child, &status, 0) == child);
|
|
Require(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
|
Require(HostOutputIs(hostText, std::string("started ") + mode + "\n"));
|
|
Require(guestText.empty());
|
|
}
|
|
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (const char* mode = std::getenv(Marker); mode != nullptr) {
|
|
if (std::strcmp(mode, "list") == 0) Require(argc == 1 && std::strcmp(argv[0], AfterLoadExec) == 0);
|
|
else Require(std::strcmp(mode, "none") == 0 && argc == 1);
|
|
std::fprintf(stderr, "started %s\n", mode);
|
|
return 0;
|
|
}
|
|
const char* const arguments[] = {AfterLoadExec, nullptr};
|
|
Run("list", arguments);
|
|
Run("none", nullptr);
|
|
std::puts("LoadExec standard error tests passed");
|
|
return 0;
|
|
}
|