Files
Mihawk 8a457a133f Seven more cores: PS1, N64, Saturn, C64, MAME, DS and 3DS
Beetle PSX HW, Mupen64Plus-Next, Beetle Saturn, VICE x64sc, MAME 0.289,
DeSmuME and Azahar, each built from my fork of it at a pinned revision
(tools/core-fork.sh, tools/build-<core>.sh), with the PS5 defaults at the
most each offers that holds full speed.

The loader grew to fit them. A core's file and image come from direct memory,
up to 1 GiB (MAME's image is 413 MB), and each image is zeroed when it is
reserved: direct memory is not, and a reloaded core's .bss held its
predecessor's bytes, which faulted every core on a reload. Each image's
.eh_frame is registered with the title's unwinder, for the cores that throw.
Initializers are capped at 65,536 (Azahar has 2,476). A load that misses imports
lists all of them. Aligned operator new comes from the core heap, a core's
atexit belongs to the core, and the imports the new cores need are bound to the
platform layer's getcwd, tmpfile, mkstemp and the rest (SDK fork ebd0fe2).
2026-09-28 15:06:47 -04:00

77 lines
2.0 KiB
C++

/* Core-local C++ destructor ownership. Never register unloadable code with the
* process-wide atexit list. The native loader executes .fini_array before unmap. */
#include <cstdlib>
#include <pthread.h>
namespace
{
struct Destructor
{
void (*callback)(void *);
void *argument;
void *dso;
Destructor *next;
};
Destructor *pending = nullptr;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
} // namespace
extern "C"
{
void *__dso_handle = &__dso_handle;
int __cxa_atexit(void (*callback)(void *), void *argument, void *dso)
{
auto *entry = static_cast<Destructor *>(std::malloc(sizeof(Destructor)));
if (!entry)
return -1;
entry->callback = callback;
entry->argument = argument;
entry->dso = dso;
pthread_mutex_lock(&lock);
entry->next = pending;
pending = entry;
pthread_mutex_unlock(&lock);
return 0;
}
void __cxa_finalize(void *dso)
{
for (;;)
{
pthread_mutex_lock(&lock);
Destructor **slot = &pending;
while (*slot && dso && (*slot)->dso != dso)
slot = &(*slot)->next;
Destructor *entry = *slot;
if (entry)
*slot = entry->next;
pthread_mutex_unlock(&lock);
if (!entry)
break;
auto callback = entry->callback;
void *argument = entry->argument;
std::free(entry);
callback(argument);
}
}
/* C's atexit belongs to the core too: a handler on the process-wide list
* would outlive the module that holds it. */
static void call_atexit_handler(void *handler)
{
reinterpret_cast<void (*)()>(handler)();
}
int atexit(void (*handler)())
{
return __cxa_atexit(call_atexit_handler, reinterpret_cast<void *>(handler), &__dso_handle);
}
}
__attribute__((destructor)) static void finish_core()
{
__cxa_finalize(nullptr);
pthread_mutex_destroy(&lock);
}