rewrote libc_cxa
This commit is contained in:
parent
f916fd48b0
commit
044628ab13
|
@ -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/Libc.h
|
||||||
src/Emulator/HLE/Libraries/LibC/printf.h
|
src/Emulator/HLE/Libraries/LibC/printf.h
|
||||||
src/Emulator/HLE/Libraries/LibC/va_ctx.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
|
set(USERSERVICE_SOURCES src/Emulator/HLE/Libraries/LibUserService/user_service.cpp
|
||||||
src/Emulator/HLE/Libraries/LibUserService/user_service.h
|
src/Emulator/HLE/Libraries/LibUserService/user_service.h
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "../Loader/Elf.h"
|
#include "../Loader/Elf.h"
|
||||||
#include "Emulator/HLE/Libraries/LibC/libc.h"
|
#include "Emulator/HLE/Libraries/LibC/libc.h"
|
||||||
|
#include "Emulator/HLE/Libraries/LibC/libc_cxa.h"
|
||||||
#include "ErrorCodes.h"
|
#include "ErrorCodes.h"
|
||||||
#include "Libs.h"
|
#include "Libs.h"
|
||||||
|
|
||||||
|
@ -17,60 +18,6 @@ static PS4_SYSV_ABI void init_env() // every game/demo should probably
|
||||||
// dummy no need atm
|
// 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) {
|
static PS4_SYSV_ABI void catchReturnFromMain(int status) {
|
||||||
// dummy
|
// 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) {
|
void LibC_Register(SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
|
LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
|
||||||
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, __cxa_guard_acquire);
|
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_acquire);
|
||||||
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, __cxa_guard_release);
|
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("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("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcpy);
|
||||||
LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memset);
|
LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memset);
|
||||||
|
|
|
@ -9,7 +9,5 @@ namespace HLE::Libs::LibC {
|
||||||
static PS4_SYSV_ABI void init_env();
|
static PS4_SYSV_ABI void init_env();
|
||||||
static PS4_SYSV_ABI void _Assert();
|
static PS4_SYSV_ABI void _Assert();
|
||||||
static PS4_SYSV_ABI void catchReturnFromMain(int status);
|
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);
|
|
||||||
|
|
||||||
};
|
};
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
#define _TIMESPEC_DEFINED
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
|
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
|
Loading…
Reference in New Issue