Merge pull request #3651 from MarcZX100/codex/libc-posix-spawn-3624
feat(libSceLibcInternal): implement posix_spawn actions
This commit is contained in:
commit
e09ee165d5
|
|
@ -1,4 +1,4 @@
|
|||
add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp Getopt.cpp UserContext.cpp)
|
||||
add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp Getopt.cpp Spawn.cpp UserContext.cpp)
|
||||
target_include_directories(libSceLibcInternal PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(libSceLibcInternal PRIVATE ${CMAKE_DL_LIBS})
|
||||
target_sources(libSceLibcInternal PRIVATE Directory.cpp)
|
||||
|
|
@ -55,6 +55,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_utime PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
|
||||
endif()
|
||||
add_test_executable(guest_posix_spawn_tests ${LIBS_INCLUDE_DIR}/tests/GuestPosixSpawn.cpp)
|
||||
target_include_directories(guest_posix_spawn_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_posix_spawn_tests PRIVATE libSceLibcInternal libc)
|
||||
configure_windows_unwind(guest_posix_spawn_tests)
|
||||
add_test(NAME guest_posix_spawn COMMAND guest_posix_spawn_tests)
|
||||
set_tests_properties(guest_posix_spawn PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_posix_spawn PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
|
||||
endif()
|
||||
add_test_executable(guest_program_name_tests ${LIBS_INCLUDE_DIR}/tests/GuestProgramName.cpp)
|
||||
target_include_directories(guest_program_name_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_program_name_tests PRIVATE libSceLibcInternal libkernel libc)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
#include "prx/libSceLibcInternal/include/Spawn.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <cstdint>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int BadDescriptor = 9;
|
||||
constexpr int OutOfMemory = 12;
|
||||
|
||||
PosixSpawnAttributes& Attributes(PosixSpawnAttributes* const* attributes, const char* function) {
|
||||
if (attributes == nullptr || *attributes == nullptr) throw std::invalid_argument(std::string(function) + ": uninitialized attributes");
|
||||
return **attributes;
|
||||
}
|
||||
|
||||
PosixSpawnFileActions& FileActions(PosixSpawnFileActions* const* actions, const char* function) {
|
||||
if (actions == nullptr || *actions == nullptr) throw std::invalid_argument(std::string(function) + ": uninitialized file actions");
|
||||
return **actions;
|
||||
}
|
||||
|
||||
int Append(PosixSpawnFileActions& actions, PosixSpawnFileAction action) {
|
||||
try {
|
||||
actions.actions.push_back(std::move(action));
|
||||
} catch (const std::bad_alloc&) {
|
||||
return OutOfMemory;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI posix_spawnattr_init_nid_postfix(PosixSpawnAttributes** attributes) {
|
||||
if (attributes == nullptr) throw std::invalid_argument("posix_spawnattr_init: null attributes");
|
||||
auto* created = new (std::nothrow) PosixSpawnAttributes{};
|
||||
if (created == nullptr) return OutOfMemory;
|
||||
*attributes = created;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawnattr_destroy_nid_postfix(PosixSpawnAttributes** attributes) {
|
||||
if (attributes == nullptr) throw std::invalid_argument("posix_spawnattr_destroy: null attributes");
|
||||
delete *attributes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawnattr_setflags_nid_postfix(PosixSpawnAttributes** attributes, std::int16_t flags) {
|
||||
Attributes(attributes, "posix_spawnattr_setflags").flags = flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawnattr_setsigdefault_nid_postfix(PosixSpawnAttributes** attributes, const GuestSignalSet* signalDefault) {
|
||||
auto& target = Attributes(attributes, "posix_spawnattr_setsigdefault");
|
||||
if (signalDefault == nullptr) throw std::invalid_argument("posix_spawnattr_setsigdefault: null signal set");
|
||||
target.signalDefault = *signalDefault;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawn_file_actions_init_nid_postfix(PosixSpawnFileActions** actions) {
|
||||
if (actions == nullptr) throw std::invalid_argument("posix_spawn_file_actions_init: null file actions");
|
||||
auto* created = new (std::nothrow) PosixSpawnFileActions{};
|
||||
if (created == nullptr) return -1;
|
||||
*actions = created;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawn_file_actions_destroy_nid_postfix(PosixSpawnFileActions** actions) {
|
||||
delete &FileActions(actions, "posix_spawn_file_actions_destroy");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawn_file_actions_addopen_nid_postfix(PosixSpawnFileActions** actions, int descriptor, const char* path, int openFlags, std::uint16_t mode) {
|
||||
if (descriptor < 0) return BadDescriptor;
|
||||
if (path == nullptr) throw std::invalid_argument("posix_spawn_file_actions_addopen: null path");
|
||||
auto& target = FileActions(actions, "posix_spawn_file_actions_addopen");
|
||||
try {
|
||||
return Append(target, {PosixSpawnFileActionKind::Open, descriptor, path, openFlags, mode, 0});
|
||||
} catch (const std::bad_alloc&) {
|
||||
return OutOfMemory;
|
||||
}
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawn_file_actions_adddup2_nid_postfix(PosixSpawnFileActions** actions, int descriptor, int newDescriptor) {
|
||||
if (descriptor < 0 || newDescriptor < 0) return BadDescriptor;
|
||||
return Append(FileActions(actions, "posix_spawn_file_actions_adddup2"), {PosixSpawnFileActionKind::Duplicate, descriptor, {}, 0, 0, newDescriptor});
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawn_file_actions_addclose_nid_postfix(PosixSpawnFileActions** actions, int descriptor) {
|
||||
if (descriptor < 0) return BadDescriptor;
|
||||
return Append(FileActions(actions, "posix_spawn_file_actions_addclose"), {PosixSpawnFileActionKind::Close, descriptor, {}, 0, 0, 0});
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawn_nid_postfix(std::int32_t* processId, const char* path, PosixSpawnFileActions* const* actions, PosixSpawnAttributes* const* attributes, char* const* arguments, char* const* environment) {
|
||||
(void)processId;
|
||||
(void)path;
|
||||
(void)actions;
|
||||
(void)attributes;
|
||||
(void)arguments;
|
||||
(void)environment;
|
||||
NotImplemented_nid_no_patch("posix_spawn: process creation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int APS5_VABI posix_spawnp_nid_postfix(std::int32_t* processId, const char* file, PosixSpawnFileActions* const* actions, PosixSpawnAttributes* const* attributes, char* const* arguments, char* const* environment) {
|
||||
(void)processId;
|
||||
(void)file;
|
||||
(void)actions;
|
||||
(void)attributes;
|
||||
(void)arguments;
|
||||
(void)environment;
|
||||
NotImplemented_nid_no_patch("posix_spawnp: process creation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCELIBCINTERNAL_INCLUDE_SPAWN_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCELIBCINTERNAL_INCLUDE_SPAWN_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct GuestSignalSet {
|
||||
std::uint32_t bits[4];
|
||||
};
|
||||
static_assert(sizeof(GuestSignalSet) == 16);
|
||||
|
||||
struct PosixSpawnAttributes {
|
||||
std::int16_t flags;
|
||||
std::int32_t processGroup;
|
||||
std::int32_t schedulingPriority;
|
||||
std::int32_t schedulingPolicy;
|
||||
GuestSignalSet signalDefault;
|
||||
GuestSignalSet signalMask;
|
||||
};
|
||||
|
||||
enum class PosixSpawnFileActionKind { Open, Duplicate, Close };
|
||||
|
||||
struct PosixSpawnFileAction {
|
||||
PosixSpawnFileActionKind kind;
|
||||
int descriptor;
|
||||
std::string path;
|
||||
int openFlags;
|
||||
std::uint16_t mode;
|
||||
int newDescriptor;
|
||||
};
|
||||
|
||||
struct PosixSpawnFileActions {
|
||||
std::vector<PosixSpawnFileAction> actions;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
#include "prx/libSceLibcInternal/include/Spawn.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI posix_spawnattr_init_nid_postfix(PosixSpawnAttributes**);
|
||||
int APS5_VABI posix_spawnattr_destroy_nid_postfix(PosixSpawnAttributes**);
|
||||
int APS5_VABI posix_spawnattr_setflags_nid_postfix(PosixSpawnAttributes**, std::int16_t);
|
||||
int APS5_VABI posix_spawnattr_setsigdefault_nid_postfix(PosixSpawnAttributes**, const GuestSignalSet*);
|
||||
int APS5_VABI posix_spawn_file_actions_init_nid_postfix(PosixSpawnFileActions**);
|
||||
int APS5_VABI posix_spawn_file_actions_destroy_nid_postfix(PosixSpawnFileActions**);
|
||||
int APS5_VABI posix_spawn_file_actions_addopen_nid_postfix(PosixSpawnFileActions**, int, const char*, int, std::uint16_t);
|
||||
int APS5_VABI posix_spawn_file_actions_adddup2_nid_postfix(PosixSpawnFileActions**, int, int);
|
||||
int APS5_VABI posix_spawn_file_actions_addclose_nid_postfix(PosixSpawnFileActions**, int);
|
||||
int APS5_VABI posix_spawn_nid_postfix(std::int32_t*, const char*, PosixSpawnFileActions* const*, PosixSpawnAttributes* const*, char* const*, char* const*);
|
||||
int APS5_VABI posix_spawnp_nid_postfix(std::int32_t*, const char*, PosixSpawnFileActions* const*, PosixSpawnAttributes* const*, char* const*, char* const*);
|
||||
}
|
||||
|
||||
static void Require(bool condition, int line) {
|
||||
if (!condition) {
|
||||
std::fprintf(stderr, "Posix spawn check failed at line %d\n", line);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
#define Check(value) Require((value), __LINE__)
|
||||
|
||||
constexpr int BadDescriptor = 9;
|
||||
|
||||
template<typename TCall>
|
||||
static bool ThrowsInvalidArgument(TCall call) {
|
||||
try {
|
||||
call();
|
||||
} catch (const std::invalid_argument&) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename TCall>
|
||||
static bool ReportsNotImplemented(TCall call, const std::string& message) {
|
||||
try {
|
||||
call();
|
||||
} catch (const std::runtime_error& error) {
|
||||
return std::string(error.what()) == message;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void CheckAttributes() {
|
||||
PosixSpawnAttributes* attributes = nullptr;
|
||||
Check(posix_spawnattr_init_nid_postfix(&attributes) == 0 && attributes != nullptr);
|
||||
Check(attributes->flags == 0 && attributes->processGroup == 0 && attributes->schedulingPriority == 0 && attributes->schedulingPolicy == 0);
|
||||
const GuestSignalSet empty{};
|
||||
Check(std::memcmp(&attributes->signalDefault, &empty, sizeof(empty)) == 0 && std::memcmp(&attributes->signalMask, &empty, sizeof(empty)) == 0);
|
||||
|
||||
Check(posix_spawnattr_setflags_nid_postfix(&attributes, 0x30) == 0 && attributes->flags == 0x30);
|
||||
Check(posix_spawnattr_setflags_nid_postfix(&attributes, -1) == 0 && attributes->flags == -1);
|
||||
Check(posix_spawnattr_setflags_nid_postfix(&attributes, 0) == 0 && attributes->flags == 0);
|
||||
|
||||
const GuestSignalSet defaults{{0x00004002u, 0x80000000u, 1u, 0xffffffffu}};
|
||||
Check(posix_spawnattr_setsigdefault_nid_postfix(&attributes, &defaults) == 0);
|
||||
Check(std::memcmp(&attributes->signalDefault, &defaults, sizeof(defaults)) == 0);
|
||||
Check(std::memcmp(&attributes->signalMask, &empty, sizeof(empty)) == 0);
|
||||
|
||||
PosixSpawnAttributes* uninitialized = nullptr;
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawnattr_setflags_nid_postfix(&uninitialized, 1); }));
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawnattr_setsigdefault_nid_postfix(&uninitialized, &defaults); }));
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawnattr_setsigdefault_nid_postfix(&attributes, nullptr); }));
|
||||
Check(ThrowsInvalidArgument([] { posix_spawnattr_init_nid_postfix(nullptr); }));
|
||||
|
||||
Check(posix_spawnattr_destroy_nid_postfix(&attributes) == 0);
|
||||
Check(posix_spawnattr_destroy_nid_postfix(&uninitialized) == 0);
|
||||
}
|
||||
|
||||
static void CheckFileActions() {
|
||||
PosixSpawnFileActions* actions = nullptr;
|
||||
Check(posix_spawn_file_actions_init_nid_postfix(&actions) == 0 && actions != nullptr && actions->actions.empty());
|
||||
|
||||
char path[] = "/app0/log.txt";
|
||||
Check(posix_spawn_file_actions_addopen_nid_postfix(&actions, 3, path, 0x601, 0644) == 0);
|
||||
std::strcpy(path, "/changed");
|
||||
Check(posix_spawn_file_actions_adddup2_nid_postfix(&actions, 3, 1) == 0);
|
||||
Check(posix_spawn_file_actions_addclose_nid_postfix(&actions, 3) == 0);
|
||||
Check(posix_spawn_file_actions_adddup2_nid_postfix(&actions, 0, 0) == 0);
|
||||
|
||||
Check(posix_spawn_file_actions_addopen_nid_postfix(&actions, -1, "/app0/other", 0, 0) == BadDescriptor);
|
||||
Check(posix_spawn_file_actions_adddup2_nid_postfix(&actions, -1, 1) == BadDescriptor);
|
||||
Check(posix_spawn_file_actions_adddup2_nid_postfix(&actions, 1, -1) == BadDescriptor);
|
||||
Check(posix_spawn_file_actions_addclose_nid_postfix(&actions, -2) == BadDescriptor);
|
||||
|
||||
const auto& recorded = actions->actions;
|
||||
Check(recorded.size() == 4);
|
||||
Check(recorded[0].kind == PosixSpawnFileActionKind::Open && recorded[0].descriptor == 3);
|
||||
Check(recorded[0].path == "/app0/log.txt" && recorded[0].openFlags == 0x601 && recorded[0].mode == 0644);
|
||||
Check(recorded[1].kind == PosixSpawnFileActionKind::Duplicate && recorded[1].descriptor == 3 && recorded[1].newDescriptor == 1);
|
||||
Check(recorded[2].kind == PosixSpawnFileActionKind::Close && recorded[2].descriptor == 3);
|
||||
Check(recorded[3].kind == PosixSpawnFileActionKind::Duplicate && recorded[3].descriptor == 0 && recorded[3].newDescriptor == 0);
|
||||
|
||||
PosixSpawnFileActions* uninitialized = nullptr;
|
||||
Check(posix_spawn_file_actions_addclose_nid_postfix(&uninitialized, -1) == BadDescriptor);
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawn_file_actions_addclose_nid_postfix(&uninitialized, 1); }));
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawn_file_actions_adddup2_nid_postfix(&uninitialized, 1, 2); }));
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawn_file_actions_addopen_nid_postfix(&uninitialized, 1, "/app0/a", 0, 0); }));
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawn_file_actions_addopen_nid_postfix(&actions, 1, nullptr, 0, 0); }));
|
||||
Check(ThrowsInvalidArgument([&] { posix_spawn_file_actions_destroy_nid_postfix(&uninitialized); }));
|
||||
Check(ThrowsInvalidArgument([] { posix_spawn_file_actions_init_nid_postfix(nullptr); }));
|
||||
Check(actions->actions.size() == 4);
|
||||
|
||||
Check(posix_spawn_file_actions_destroy_nid_postfix(&actions) == 0);
|
||||
}
|
||||
|
||||
static void CheckSpawn() {
|
||||
PosixSpawnAttributes* attributes = nullptr;
|
||||
PosixSpawnFileActions* actions = nullptr;
|
||||
Check(posix_spawnattr_init_nid_postfix(&attributes) == 0 && posix_spawn_file_actions_init_nid_postfix(&actions) == 0);
|
||||
char program[] = "/app0/tool.elf";
|
||||
char* arguments[] = {program, nullptr};
|
||||
char* environment[] = {nullptr};
|
||||
std::int32_t processId = 77;
|
||||
Check(ReportsNotImplemented([&] { posix_spawn_nid_postfix(&processId, program, &actions, &attributes, arguments, environment); },
|
||||
"posix_spawn: process creation not implemented"));
|
||||
Check(ReportsNotImplemented([&] { posix_spawnp_nid_postfix(&processId, "tool.elf", nullptr, nullptr, arguments, nullptr); },
|
||||
"posix_spawnp: process creation not implemented"));
|
||||
Check(processId == 77);
|
||||
Check(posix_spawn_file_actions_destroy_nid_postfix(&actions) == 0 && posix_spawnattr_destroy_nid_postfix(&attributes) == 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
CheckAttributes();
|
||||
CheckFileActions();
|
||||
CheckSpawn();
|
||||
}
|
||||
|
|
@ -12,4 +12,5 @@
|
|||
- [Thread exit destructors](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) run when a libkernel thread finishes; on the main or a host thread only `_sceLibcInternalThreadDtors` runs them, as `exit` does, else they are dropped
|
||||
- [fdopendir](../../../core/libs/prx/libSceLibcInternal/Directory.cpp) (libSceLibcInternal) - the directory stream starts at the directory's first entry, while FreeBSD's [`fdopendir`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/lib/libc/gen/opendir.c#L67-L74) reads from the descriptor's current offset, and `FD_CLOEXEC` is not set on the descriptor. As in FreeBSD, a descriptor that is not open fails with `EBADF`, one that is not a directory with `ENOTDIR` (both leave it open), and `closedir` closes it
|
||||
- [fstatvfs](../../../core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp) (libSceLibcInternal) converts `_fstatfs` to the 88-byte `struct statvfs` as FreeBSD 9.2's `sfs2svfs` does (`f_bsize` from the transfer size, `f_frsize` from the block size, `f_favail` equal to `f_ffree`, `f_fsid` 0, `ST_RDONLY` and `ST_NOSUID` only). Differences: `f_namemax` is the `statfs` value, not `fpathconf(_PC_NAME_MAX)`; negative `f_bavail` and `f_ffree` wrap to unsigned without `EOVERFLOW`, as the same-width copy in FreeBSD does; inode counts are 0 on Windows, inherited from `_fstatfs`; a null output returns `EFAULT` after the descriptor is validated, where FreeBSD would fault. Layout and conversion are unverified against the PS5 library (PS5MC imports `fstatvfs` but was not run)
|
||||
- [posix_spawn and posix_spawnp](../../../core/libs/prx/libSceLibcInternal/Spawn.cpp) (libSceLibcInternal) throw: creating a child process is not supported. PPSA99305 (Dusklight) and PPSA99309 (Wind Waker) stop on `main` at unresolved import `44hlATrd47U` (`posix_spawnattr_init`); a run with this PR has not established how far either title gets, and process creation still throws. The attribute and file action calls keep FreeBSD 11.4's objects, so a real `posix_spawn` would have to apply them in the child. Null or uninitialized objects, which FreeBSD dereferences, throw.
|
||||
- [getopt, getopt_long](../../../core/libs/prx/libSceLibcInternal/Getopt.cpp) (libSceLibcInternal) follow FreeBSD 11 [getopt.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/getopt.c) and [getopt_long.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/getopt_long.c) with `GNU_COMPATIBLE`: `getopt` does not permute, `getopt_long` permutes unless `POSIXLY_CORRECT` is set in the guest environment or the option string starts with `+`, and the exported `opterr`, `optind`, `optopt`, `optreset` and `optarg` start at 1, 1, 0, 0 and null. Warnings go to the guest's current `stderr` prefixed with `__progname`, where FreeBSD's `warnx` keeps the `stderr` of its first call. A null `getopt` option string, a null argument vector or argument, a negative `optind` and an `optind` moved inside a pending permutation throw where FreeBSD faults. `getopt_long_only` is not exported. That the console's `libSceLibcInternal` uses this FreeBSD code is unverified
|
||||
|
|
|
|||
Loading…
Reference in New Issue