mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
Atari's mintelf platform has ELF support but its libc does not provide
__cxa_atexit / __cxa_finalize, so -fuse-cxa-atexit cannot be used out of
the box. Add a minimal shim (plus a __dso_handle definition for the main
executable) so plugins can register and finalize their function-local
static destructors per DSO on unload.
Note: earlier version ran __cxa_finalize from the host via
findSymbol("__dso_handle"). That works as long as exactly one
__dso_handle ends up in the plugin's symtab (the plugin's own).
When the main binary also defines __dso_handle (so the host's
function-local statics can link with -fuse-cxa-atexit), it breaks: the
plugin link uses --just-symbols=<main binary> and imports that as a
second __dso_handle, and findSymbol can then return the host-imported
one instead of the plugin's own. The two addresses differ, so
__cxa_finalize matches nothing.
The plugin-side helper avoids the ambiguity on every target by using
whichever __dso_handle the plugin's own code already resolved against --
the same one embedded in its __cxa_atexit calls. (PSP2's plugin runtime
has always called __cxa_finalize from inside the plugin for the same
reason; see backends/plugins/psp2/plugin.cpp.)
117 lines
3.8 KiB
C++
117 lines
3.8 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/scummsys.h"
|
|
|
|
#if defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER) && defined(ELF_LOADER_CXA_ATEXIT) && defined(__MINT__)
|
|
|
|
#include "common/debug.h"
|
|
#include "common/textconsole.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
/**
|
|
* Minimal __cxa_atexit / __cxa_finalize for targets whose libc lacks them
|
|
* (e.g. mintlib on Atari).
|
|
*
|
|
* With -fuse-cxa-atexit, GCC registers function-local static destructors via
|
|
* __cxa_atexit(&dtor, &obj, &__dso_handle). The third argument tags which
|
|
* DSO owns the registration. On plugin unload, ELFPlugin::unloadPlugin()
|
|
* calls __cxa_finalize(&plugin_dso_handle) to run and remove only that
|
|
* plugin's entries -- preventing dangling atexit pointers into freed memory.
|
|
*
|
|
* See also the detailed comment in elf-provider.cpp.
|
|
*/
|
|
|
|
#define CXA_ATEXIT_MAX 256
|
|
|
|
struct CxaAtexitEntry {
|
|
void (*destructor)(void *);
|
|
void *arg;
|
|
void *dso_handle;
|
|
};
|
|
|
|
static CxaAtexitEntry s_entries[CXA_ATEXIT_MAX];
|
|
static int s_count;
|
|
|
|
extern "C" {
|
|
|
|
// GCC with -fuse-cxa-atexit emits references to __dso_handle in every TU that
|
|
// has function-local statics with non-trivial destructors. It is normally
|
|
// provided by crtbegin.o; on targets whose startup files don't supply one,
|
|
// define it here for the main executable. Its address uniquely identifies
|
|
// the main DSO.
|
|
void *__dso_handle = &__dso_handle;
|
|
|
|
int __cxa_atexit(void (*destructor)(void *), void *arg, void *dso_handle) {
|
|
if (s_count >= CXA_ATEXIT_MAX) {
|
|
warning("elfloader: cxa-atexit table full (max=%d), dropping dtor %p dso=%p",
|
|
CXA_ATEXIT_MAX, (void *)destructor, dso_handle);
|
|
return -1;
|
|
}
|
|
|
|
s_entries[s_count].destructor = destructor;
|
|
s_entries[s_count].arg = arg;
|
|
s_entries[s_count].dso_handle = dso_handle;
|
|
s_count++;
|
|
|
|
debug(2, "elfloader: cxa-atexit register[%d] dtor=%p arg=%p dso=%p",
|
|
s_count - 1, (void *)destructor, arg, dso_handle);
|
|
return 0;
|
|
}
|
|
|
|
void __cxa_finalize(void *dso_handle) {
|
|
int ran = 0, matched = 0;
|
|
|
|
for (int i = s_count - 1; i >= 0; i--) {
|
|
if (s_entries[i].destructor == NULL)
|
|
continue;
|
|
|
|
if (dso_handle == NULL || s_entries[i].dso_handle == dso_handle) {
|
|
matched++;
|
|
void (*f)(void *) = s_entries[i].destructor;
|
|
void *a = s_entries[i].arg;
|
|
s_entries[i].destructor = NULL; // mark before calling (re-entrancy)
|
|
f(a);
|
|
ran++;
|
|
}
|
|
}
|
|
|
|
debug(2, "elfloader: cxa-atexit finalize(dso=%p): matched=%d ran=%d (total registered=%d)",
|
|
dso_handle, matched, ran, s_count);
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
// Ensure the main binary's own statics are finalized at process exit.
|
|
// Registered early via constructor so it runs late in the atexit sequence.
|
|
static void cxa_finalize_all() {
|
|
__cxa_finalize(NULL);
|
|
}
|
|
|
|
static void register_cxa_cleanup() __attribute__((constructor, used));
|
|
static void register_cxa_cleanup() {
|
|
// debug() / stdio aren't ready at constructor time, so no log here.
|
|
atexit(cxa_finalize_all);
|
|
}
|
|
|
|
#endif // defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER) && defined(ELF_LOADER_CXA_ATEXIT) && defined(__MINT__)
|