feat(libkernel): implement pthread_getname_np
Hades (PPSA03355) stops at load on main with "undefined symbol: 9HzfhdtESio", the NID of pthread_getname_np, which it calls on pthread_self() with a 32-byte buffer. Measured on a PS5 (firmware 13.60) through dlopen of libkernel: the function takes (thread, buffer), copies the name and its terminator, returns an empty name for an unnamed thread, ESRCH for a null or joined thread and EFAULT for a null buffer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6b56175a42
commit
3c7859c41d
|
|
@ -190,6 +190,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_thread_self PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_pthread_getname_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadGetname.cpp)
|
||||
target_include_directories(guest_pthread_getname_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_pthread_getname_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_pthread_getname_tests)
|
||||
add_test(NAME guest_pthread_getname COMMAND guest_pthread_getname_tests)
|
||||
set_tests_properties(guest_pthread_getname PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_pthread_getname PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_pthread_attr_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadAttr.cpp)
|
||||
target_include_directories(guest_pthread_attr_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_pthread_attr_tests PRIVATE libkernel libc)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ extern "C" int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec*
|
|||
|
||||
namespace PosixThread {
|
||||
|
||||
constexpr int GUEST_ESRCH = 3;
|
||||
constexpr int GUEST_EFAULT = 14;
|
||||
constexpr int GUEST_EINVAL = 22;
|
||||
constexpr int GUEST_ETIMEDOUT = 60;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ int APS5_VABI scePthreadDetach(Pthread thread);
|
|||
void APS5_VABI scePthreadExit(void* retval);
|
||||
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
|
||||
int APS5_VABI scePthreadRename(Pthread thread, const char* name);
|
||||
int APS5_VABI scePthreadGetname(Pthread thread, char* name);
|
||||
Pthread APS5_VABI scePthreadSelf();
|
||||
int APS5_VABI scePthreadSetcancelstate(int state, int* old_state);
|
||||
void APS5_VABI scePthreadTestcancel();
|
||||
|
|
@ -68,6 +69,12 @@ int APS5_VABI pthread_rename_np_nid_postfix(Pthread thread, const char* name) {
|
|||
return PosixThread::ToErrno(scePthreadRename(thread, name));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_getname_np_nid_postfix(Pthread thread, char* name) {
|
||||
if (!thread) return PosixThread::GUEST_ESRCH;
|
||||
if (!name) return PosixThread::GUEST_EFAULT;
|
||||
return PosixThread::ToErrno(scePthreadGetname(thread, name));
|
||||
}
|
||||
|
||||
Pthread APS5_VABI pthread_self_nid_postfix(void) {
|
||||
return scePthreadSelf();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
Pthread APS5_VABI pthread_self_nid_postfix(void);
|
||||
int APS5_VABI pthread_rename_np_nid_postfix(Pthread thread, const char* name);
|
||||
int APS5_VABI pthread_getname_np_nid_postfix(Pthread thread, char* name);
|
||||
int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
|
||||
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
|
||||
}
|
||||
|
||||
static constexpr int GUEST_ESRCH = 3;
|
||||
static constexpr int GUEST_EFAULT = 14;
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
static std::array<unsigned char, 64> Read(Pthread thread, int expected) {
|
||||
std::array<unsigned char, 64> buffer{};
|
||||
buffer.fill(0xAA);
|
||||
Require(pthread_getname_np_nid_postfix(thread, reinterpret_cast<char*>(buffer.data())) == expected);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void RequireName(Pthread thread, const std::string& name) {
|
||||
const auto buffer = Read(thread, 0);
|
||||
Require(std::memcmp(buffer.data(), name.c_str(), name.size() + 1) == 0);
|
||||
for (std::size_t index = name.size() + 1; index < buffer.size(); ++index) Require(buffer[index] == 0xAA);
|
||||
}
|
||||
|
||||
static void* APS5_VABI Unnamed(void*) {
|
||||
RequireName(pthread_self_nid_postfix(), "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main() {
|
||||
const Pthread self = pthread_self_nid_postfix();
|
||||
for (const char* name : {"", "A", "AnyPS5Probe", "0123456789012345678901234567890"}) {
|
||||
Require(pthread_rename_np_nid_postfix(self, name) == 0);
|
||||
RequireName(self, name);
|
||||
}
|
||||
Pthread worker = nullptr;
|
||||
Require(scePthreadCreate(&worker, nullptr, Unnamed, nullptr, nullptr) == 0);
|
||||
Require(scePthreadJoin(worker, nullptr) == 0);
|
||||
|
||||
const auto untouched = Read(nullptr, GUEST_ESRCH);
|
||||
for (unsigned char byte : untouched) Require(byte == 0xAA);
|
||||
Require(pthread_getname_np_nid_postfix(self, nullptr) == GUEST_EFAULT);
|
||||
}
|
||||
|
|
@ -135,6 +135,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [std_execute_once](../../core/libs/prx/libc/Export.cpp) (libc) - assumed to be the plain-named twin of `std::_Execute_once` (`_ZSt13_Execute_onceRSt9once_flagPFiPvS1_PS1_ES1_`, the callee of Dinkumware `call_once`): it delegates there and gets the [thread.once.callonce] semantics already verified for that symbol (one invocation per flag, concurrent callers block until it completes, and a failing or throwing invocation leaves the flag unready so the next caller runs it). The name equivalence is not confirmed on a title, and the callback convention (called with (null, arg, null), non-zero return meaning success) is the twin's, unverified for this symbol
|
||||
- [__progname](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export
|
||||
- [pthread_barrierattr_setpshared](../../core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp) (libkernel) - PTHREAD_PROCESS_SHARED is accepted on the assumption that the console follows FreeBSD 11.0 libthr, which accepts it (9.0 rejects it with EINVAL); unverified on hardware. Guest code runs in one process, so a shared barrier behaves as a private one
|
||||
- [pthread_getname_np](../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - a thread that has already been joined returns `ESRCH` (3) on a PS5 (firmware 13.60), but its object is freed after the join here, so the pointer cannot be told apart from a live thread and its name is read as for a live one
|
||||
- [sceSslClose](../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature
|
||||
- [sceSslGetSerialNumber](../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature
|
||||
- [X+4jdIS75P0](../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - unknown name, signature
|
||||
|
|
|
|||
Loading…
Reference in New Issue