From 044628ab13291d2d42e05e52d2693f1568072261 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 16 Oct 2023 20:49:52 +0300 Subject: [PATCH] rewrote libc_cxa --- CMakeLists.txt | 2 + src/Core/PS4/HLE/LibC.cpp | 60 +------- src/Core/PS4/HLE/LibC.h | 2 - src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp | 147 +++++++++++++++++++ src/Emulator/HLE/Libraries/LibC/libc_cxa.h | 11 ++ 5 files changed, 164 insertions(+), 58 deletions(-) create mode 100644 src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp create mode 100644 src/Emulator/HLE/Libraries/LibC/libc_cxa.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 590c9823..1da14c4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ set(LIBC_SOURCES src/Emulator/HLE/Libraries/LibC/Libc.cpp src/Emulator/HLE/Libraries/LibC/Libc.h src/Emulator/HLE/Libraries/LibC/printf.h src/Emulator/HLE/Libraries/LibC/va_ctx.h + src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp + src/Emulator/HLE/Libraries/LibC/libc_cxa.h ) set(USERSERVICE_SOURCES src/Emulator/HLE/Libraries/LibUserService/user_service.cpp src/Emulator/HLE/Libraries/LibUserService/user_service.h diff --git a/src/Core/PS4/HLE/LibC.cpp b/src/Core/PS4/HLE/LibC.cpp index e2b7068e..ab6c9ccd 100644 --- a/src/Core/PS4/HLE/LibC.cpp +++ b/src/Core/PS4/HLE/LibC.cpp @@ -5,6 +5,7 @@ #include "../Loader/Elf.h" #include "Emulator/HLE/Libraries/LibC/libc.h" +#include "Emulator/HLE/Libraries/LibC/libc_cxa.h" #include "ErrorCodes.h" #include "Libs.h" @@ -17,60 +18,6 @@ static PS4_SYSV_ABI void init_env() // every game/demo should probably // dummy no need atm } -static pthread_mutex_t __guard_mutex; -static pthread_once_t __once_control = PTHREAD_ONCE_INIT; - -static void recursiveMutex() { - pthread_mutexattr_t recursiveMutexAttr; - pthread_mutexattr_init(&recursiveMutexAttr); - pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr); -} - -static pthread_mutex_t* mutex_quard() { - pthread_once(&__once_control, &recursiveMutex); - return &__guard_mutex; -} - -int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) { - if ((*((uint8_t*)guard_object) != 0)) // low 8 bits checks if its already init - { - return 0; - } - int result = ::pthread_mutex_lock(mutex_quard()); - if (result != 0) { - BREAKPOINT(); - } - - // Check if another thread has completed initializer run - if ((*((uint8_t*)guard_object) != 0)) { // check again if other thread init it - int result = ::pthread_mutex_unlock(mutex_quard()); - if (result != 0) { - BREAKPOINT(); - } - return 0; - } - - if (((uint8_t*)guard_object)[1] != 0) { // the second lowest byte marks if it's being used by a thread - BREAKPOINT(); - } - - // mark this guard object as being in use - ((uint8_t*)guard_object)[1] = 1; - - return 1; -} - -void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object) { - *((uint8_t*)guard_object) = 1; // mark it as done - - // release global mutex - int result = ::pthread_mutex_unlock(mutex_quard()); - if (result != 0) { - BREAKPOINT(); - } -} - static PS4_SYSV_ABI void catchReturnFromMain(int status) { // dummy } @@ -110,8 +57,9 @@ void PS4_SYSV_ABI qsort(void* ptr, size_t count,size_t size, int(PS4_SYSV_ABI* c void LibC_Register(SymbolsResolver* sym) { LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env); - LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, __cxa_guard_acquire); - LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, __cxa_guard_release); + LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_acquire); + LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_release); + LIB_FUNCTION("2emaaluWzUw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_abort); LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcmp); LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcpy); LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memset); diff --git a/src/Core/PS4/HLE/LibC.h b/src/Core/PS4/HLE/LibC.h index ea3915e2..c9887c4b 100644 --- a/src/Core/PS4/HLE/LibC.h +++ b/src/Core/PS4/HLE/LibC.h @@ -9,7 +9,5 @@ namespace HLE::Libs::LibC { static PS4_SYSV_ABI void init_env(); static PS4_SYSV_ABI void _Assert(); static PS4_SYSV_ABI void catchReturnFromMain(int status); - int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object); - void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object); }; \ No newline at end of file diff --git a/src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp b/src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp new file mode 100644 index 00000000..2dbe9dec --- /dev/null +++ b/src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp @@ -0,0 +1,147 @@ +#include "libc_cxa.h" +#include "debug.h" +#include "Util/log.h" + +// adapted from https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx.auto.html + +namespace Emulator::HLE::Libraries::LibC::Cxa { +constexpr bool log_file_cxa = true; // disable it to disable logging + +// This file implements the __cxa_guard_* functions as defined at: +// http://www.codesourcery.com/public/cxx-abi/abi.html +// +// The goal of these functions is to support thread-safe, one-time +// initialization of function scope variables. The compiler will generate +// code like the following: +// +// if ( obj_guard.first_byte == 0 ) { +// if ( __cxa_guard_acquire(&obj_guard) ) { +// try { +// ... initialize the object ...; +// } +// catch (...) { +// __cxa_guard_abort(&obj_guard); +// throw; +// } +// ... queue object destructor with __cxa_atexit() ...; +// __cxa_guard_release(&obj_guard); +// } +// } +// +// Notes: +// ojb_guard is a 64-bytes in size and statically initialized to zero. +// +// Section 6.7 of the C++ Spec says "If control re-enters the declaration +// recursively while the object is being initialized, the behavior is +// undefined". This implementation calls abort(). +// + +// Note don't use function local statics to avoid use of cxa functions... +static pthread_mutex_t __guard_mutex; +static pthread_once_t __once_control = PTHREAD_ONCE_INIT; + +static void makeRecusiveMutex() { + pthread_mutexattr_t recursiveMutexAttr; + pthread_mutexattr_init(&recursiveMutexAttr); + pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr); +} + +__attribute__((noinline)) static pthread_mutex_t* guard_mutex() { + pthread_once(&__once_control, &makeRecusiveMutex); + return &__guard_mutex; +} + +// helper functions for getting/setting flags in guard_object +static bool initializerHasRun(u64* guard_object) { return (*((u08*)guard_object) != 0); } + +static void setInitializerHasRun(u64* guard_object) { *((u08*)guard_object) = 1; } + +static bool inUse(u64* guard_object) { return (((u08*)guard_object)[1] != 0); } + +static void setInUse(u64* guard_object) { ((u08*)guard_object)[1] = 1; } + +static void setNotInUse(u64* guard_object) { ((u08*)guard_object)[1] = 0; } + +// +// Returns 1 if the caller needs to run the initializer and then either +// call __cxa_guard_release() or __cxa_guard_abort(). If zero is returned, +// then the initializer has already been run. This function blocks +// if another thread is currently running the initializer. This function +// aborts if called again on the same guard object without an intervening +// call to __cxa_guard_release() or __cxa_guard_abort(). +// +int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) { + // Double check that the initializer has not already been run + if (initializerHasRun(guard_object)) return 0; + + // We now need to acquire a lock that allows only one thread + // to run the initializer. If a different thread calls + // __cxa_guard_acquire() with the same guard object, we want + // that thread to block until this thread is done running the + // initializer and calls __cxa_guard_release(). But if the same + // thread calls __cxa_guard_acquire() with the same guard object, + // we want to abort. + // To implement this we have one global pthread recursive mutex + // shared by all guard objects, but only one at a time. + + int result = ::pthread_mutex_lock(guard_mutex()); + if (result != 0) { + LOG_TRACE_IF(log_file_cxa, "__cxa_guard_acquire(): pthread_mutex_lock failed with {}\n",result); + } + // At this point all other threads will block in __cxa_guard_acquire() + + // Check if another thread has completed initializer run + if (initializerHasRun(guard_object)) { + int result = ::pthread_mutex_unlock(guard_mutex()); + if (result != 0) { + LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): pthread_mutex_unlock failed with {}\n",result); + } + return 0; + } + + // The pthread mutex is recursive to allow other lazy initialized + // function locals to be evaluated during evaluation of this one. + // But if the same thread can call __cxa_guard_acquire() on the + // *same* guard object again, we call abort(); + if (inUse(guard_object)) { + LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): initializer for function local static variable called enclosing function\n"); + } + + // mark this guard object as being in use + setInUse(guard_object); + + // return non-zero to tell caller to run initializer + return 1; +} + +// +// Sets the first byte of the guard_object to a non-zero value. +// Releases any locks acquired by __cxa_guard_acquire(). +// +void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object) { + // first mark initalizer as having been run, so + // other threads won't try to re-run it. + setInitializerHasRun(guard_object); + + // release global mutex + int result = ::pthread_mutex_unlock(guard_mutex()); + if (result != 0) { + LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): pthread_mutex_unlock failed with {}\n",result); + } +} + +// +// Releases any locks acquired by __cxa_guard_acquire(). +// +void PS4_SYSV_ABI __cxa_guard_abort(u64* guard_object) { + int result = ::pthread_mutex_unlock(guard_mutex()); + if (result != 0) { + LOG_TRACE_IF(log_file_cxa,"__cxa_guard_abort(): pthread_mutex_unlock failed with {}\n",result); + } + + // now reset state, so possible to try to initialize again + setNotInUse(guard_object); +} + +} // namespace Emulator::HLE::Libraries::LibC::Cxa \ No newline at end of file diff --git a/src/Emulator/HLE/Libraries/LibC/libc_cxa.h b/src/Emulator/HLE/Libraries/LibC/libc_cxa.h new file mode 100644 index 00000000..228aae1a --- /dev/null +++ b/src/Emulator/HLE/Libraries/LibC/libc_cxa.h @@ -0,0 +1,11 @@ +#pragma once +#define _TIMESPEC_DEFINED + +#include +#include + +namespace Emulator::HLE::Libraries::LibC::Cxa { +int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object); +void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object); +void PS4_SYSV_ABI __cxa_guard_abort(u64* guard_object); +} // namespace Emulator::HLE::Libraries::LibC::Cxa \ No newline at end of file