PLUGINS: Use the C++ ABI to call dtors when unloading a plugin.

Avoid linking all plugins against libstdc++ to free up some memory
(about ~40kb on Wii per plugin). Enable it on GameCube, Wii, DS and PSP
(PS2 doesn't have __cxa_atexit support in its libc).

svn-id: r52607
This commit is contained in:
Andre Heider 2010-09-06 20:34:00 +00:00
parent c2cafe426e
commit ae408db07f
4 changed files with 76 additions and 6 deletions

View file

@ -25,11 +25,52 @@
#if defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) #if defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET)
#ifdef ELF_LOADER_CXA_ATEXIT
#include <cxxabi.h>
#endif
#include "backends/plugins/elf/elf-provider.h" #include "backends/plugins/elf/elf-provider.h"
#include "backends/plugins/dynamic-plugin.h" #include "backends/plugins/dynamic-plugin.h"
#include "common/debug.h"
#include "common/fs.h" #include "common/fs.h"
/* Note about ELF_LOADER_CXA_ATEXIT:
*
* consider the code:
*
* class Foobar {
* const char *work() {
* static String foo = "bar";
* return s.c_str();
* }
* }
*
* When instantiating Foobar and calling work() for the first time the String
* foo will be constructed. GCC automatically registers its destruction via
* either atexit() or __cxa_atexit(). Only the latter will add information
* about which DSO did the construction (Using &__dso_handle).
*
* __cxa_atexit allows plugins to reference C++ ABI symbols in the main
* executable without code duplication (No need to link the plugin against
* libstdc++), since we can distinguish which registered exit functions belong
* to a specific DSO. When unloading a plugin, we just use the C++ ABI call
* __cxa_finalize(&__dso_handle) to call all destructors of only that DSO.
*
* Prerequisites:
* - The used libc needs to support __cxa_atexit
* - -fuse-cxa-atexit in CXXFLAGS
* - Every plugin needs its own hidden __dso_handle symbol
* This is automatically done via REGISTER_PLUGIN_DYNAMIC, see base/plugins.h
*
* When __cxa_atexit can not be used, each plugin needs to link against
* libstdc++ to embed its own set of C++ ABI symbols. When not doing so,
* registered destructors of already unloaded plugins will crash the
* application upon returning from main().
*
* See "3.3.5 DSO Object Destruction API" of the C++ ABI
*/
DynamicPlugin::VoidFunc ELFPlugin::findSymbol(const char *symbol) { DynamicPlugin::VoidFunc ELFPlugin::findSymbol(const char *symbol) {
void *func = 0; void *func = 0;
@ -71,6 +112,14 @@ bool ELFPlugin::loadPlugin() {
bool ret = DynamicPlugin::loadPlugin(); bool ret = DynamicPlugin::loadPlugin();
#ifdef ELF_LOADER_CXA_ATEXIT
// FIXME HACK: Reverse HACK of findSymbol() :P
VoidFunc tmp;
tmp = findSymbol("__dso_handle");
memcpy(&_dso_handle, &tmp, sizeof(VoidFunc));
debug(2, "elfloader: __dso_handle is %p", _dso_handle);
#endif
_dlHandle->discard_symtab(); _dlHandle->discard_symtab();
return ret; return ret;
@ -80,6 +129,14 @@ void ELFPlugin::unloadPlugin() {
DynamicPlugin::unloadPlugin(); DynamicPlugin::unloadPlugin();
if (_dlHandle) { if (_dlHandle) {
#ifdef ELF_LOADER_CXA_ATEXIT
if (_dso_handle) {
debug(2, "elfloader: calling __cxa_finalize");
__cxxabiv1::__cxa_finalize(_dso_handle);
_dso_handle = 0;
}
#endif
if (!_dlHandle->close()) if (!_dlHandle->close())
warning("elfloader: Failed unloading plugin '%s'", _filename.c_str()); warning("elfloader: Failed unloading plugin '%s'", _filename.c_str());

View file

@ -45,13 +45,15 @@ class ELFPlugin : public DynamicPlugin {
protected: protected:
DLObject *_dlHandle; DLObject *_dlHandle;
Common::String _filename; Common::String _filename;
void *_dso_handle;
virtual VoidFunc findSymbol(const char *symbol); virtual VoidFunc findSymbol(const char *symbol);
public: public:
ELFPlugin(const Common::String &filename) : ELFPlugin(const Common::String &filename) :
_dlHandle(0), _dlHandle(0),
_filename(filename) { _filename(filename),
_dso_handle(0) {
} }
virtual ~ELFPlugin() { virtual ~ELFPlugin() {

View file

@ -90,6 +90,13 @@ extern int pluginTypeVersions[PLUGIN_TYPE_MAX];
#define PLUGIN_ENABLED_DYNAMIC(ID) \ #define PLUGIN_ENABLED_DYNAMIC(ID) \
(ENABLE_##ID && (ENABLE_##ID == DYNAMIC_PLUGIN) && DYNAMIC_MODULES) (ENABLE_##ID && (ENABLE_##ID == DYNAMIC_PLUGIN) && DYNAMIC_MODULES)
// see comments in backends/plugins/elf/elf-provider.cpp
#if defined(ELF_LOADER_TARGET) && defined(ELF_LOADER_CXA_ATEXIT)
#define PLUGIN_DYNAMIC_EXTRA_DECL uint32 __dso_handle __attribute__((visibility ("hidden"))) = 0
#else
#define PLUGIN_DYNAMIC_EXTRA_DECL
#endif
/** /**
* REGISTER_PLUGIN_STATIC is a convenience macro which is used to declare * REGISTER_PLUGIN_STATIC is a convenience macro which is used to declare
* the plugin interface for static plugins. Code (such as game engines) * the plugin interface for static plugins. Code (such as game engines)
@ -119,6 +126,7 @@ extern int pluginTypeVersions[PLUGIN_TYPE_MAX];
*/ */
#define REGISTER_PLUGIN_DYNAMIC(ID,TYPE,PLUGINCLASS) \ #define REGISTER_PLUGIN_DYNAMIC(ID,TYPE,PLUGINCLASS) \
extern "C" { \ extern "C" { \
PLUGIN_DYNAMIC_EXTRA_DECL; \
PLUGIN_EXPORT int32 PLUGIN_getVersion() { return PLUGIN_VERSION; } \ PLUGIN_EXPORT int32 PLUGIN_getVersion() { return PLUGIN_VERSION; } \
PLUGIN_EXPORT int32 PLUGIN_getType() { return TYPE; } \ PLUGIN_EXPORT int32 PLUGIN_getType() { return TYPE; } \
PLUGIN_EXPORT int32 PLUGIN_getTypeVersion() { return TYPE##_VERSION; } \ PLUGIN_EXPORT int32 PLUGIN_getTypeVersion() { return TYPE##_VERSION; } \

13
configure vendored
View file

@ -1424,6 +1424,7 @@ case $_host_os in
CXXFLAGS="$CXXFLAGS -isystem $DEVKITPRO/libnds/include -isystem $DEVKITPRO/devkitARM/arm-eabi/include" CXXFLAGS="$CXXFLAGS -isystem $DEVKITPRO/libnds/include -isystem $DEVKITPRO/devkitARM/arm-eabi/include"
CXXFLAGS="$CXXFLAGS -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer -mthumb-interwork" CXXFLAGS="$CXXFLAGS -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer -mthumb-interwork"
CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fno-strict-aliasing" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fno-strict-aliasing"
CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit"
LDFLAGS="$LDFLAGS -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt" LDFLAGS="$LDFLAGS -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt"
if test "$_dynamic_modules" = no ; then if test "$_dynamic_modules" = no ; then
LDFLAGS="$LDFLAGS -Wl,--gc-sections" LDFLAGS="$LDFLAGS -Wl,--gc-sections"
@ -1495,6 +1496,7 @@ case $_host_os in
;; ;;
psp) psp)
CXXFLAGS="$CXXFLAGS -O3 -I$PSPSDK/include -D_PSP_FW_VERSION=150" CXXFLAGS="$CXXFLAGS -O3 -I$PSPSDK/include -D_PSP_FW_VERSION=150"
CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit"
;; ;;
solaris*) solaris*)
DEFINES="$DEFINES -DSOLARIS -DSYSTEM_NOT_SUPPORTING_D_TYPE" DEFINES="$DEFINES -DSOLARIS -DSYSTEM_NOT_SUPPORTING_D_TYPE"
@ -1505,6 +1507,7 @@ case $_host_os in
wii) wii)
CXXFLAGS="$CXXFLAGS -Os -mrvl -mcpu=750 -meabi -mhard-float" CXXFLAGS="$CXXFLAGS -Os -mrvl -mcpu=750 -meabi -mhard-float"
CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched"
CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit"
CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include" CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include"
# libogc is required to link the cc tests (includes _start()) # libogc is required to link the cc tests (includes _start())
LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii -logc" LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii -logc"
@ -2004,7 +2007,7 @@ POST_OBJS_FLAGS := -Wl,--no-whole-archive
' '
;; ;;
ds) ds)
DEFINES="$DEFINES -DELF_LOADER_TARGET -DONE_PLUGIN_AT_A_TIME" DEFINES="$DEFINES -DELF_LOADER_TARGET -DELF_LOADER_CXA_ATEXIT -DONE_PLUGIN_AT_A_TIME"
_def_plugin=' _def_plugin='
#define PLUGIN_PREFIX "" #define PLUGIN_PREFIX ""
#define PLUGIN_SUFFIX ".plg" #define PLUGIN_SUFFIX ".plg"
@ -2015,7 +2018,7 @@ PLUGIN_PREFIX :=
PLUGIN_SUFFIX := .plg PLUGIN_SUFFIX := .plg
PLUGIN_EXTRA_DEPS = $(EXECUTABLE) PLUGIN_EXTRA_DEPS = $(EXECUTABLE)
CXXFLAGS += -DDYNAMIC_MODULES CXXFLAGS += -DDYNAMIC_MODULES
PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols,$(EXECUTABLE),-T$(srcdir)/backends/plugins/ds/plugin.ld -lstdc++ -lc -mthumb-interwork -mno-fpu -Wl,--retain-symbols-file,$(srcdir)/backends/plugins/elf/plugin.syms PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols,$(EXECUTABLE),-T$(srcdir)/backends/plugins/ds/plugin.ld -mthumb-interwork -mno-fpu -Wl,--retain-symbols-file,$(srcdir)/backends/plugins/elf/plugin.syms
PRE_OBJS_FLAGS := -Wl,--whole-archive PRE_OBJS_FLAGS := -Wl,--whole-archive
POST_OBJS_FLAGS := -Wl,--no-whole-archive POST_OBJS_FLAGS := -Wl,--no-whole-archive
' '
@ -2038,7 +2041,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive
' '
;; ;;
gamecube | wii) gamecube | wii)
DEFINES="$DEFINES -DELF_LOADER_TARGET -DPPC_TARGET -DONE_PLUGIN_AT_A_TIME" DEFINES="$DEFINES -DELF_LOADER_TARGET -DPPC_TARGET -DELF_LOADER_CXA_ATEXIT -DONE_PLUGIN_AT_A_TIME"
_def_plugin=' _def_plugin='
#define PLUGIN_PREFIX "" #define PLUGIN_PREFIX ""
#define PLUGIN_SUFFIX ".plg" #define PLUGIN_SUFFIX ".plg"
@ -2108,7 +2111,7 @@ POST_OBJS_FLAGS := -Wl,--no-whole-archive
' '
;; ;;
psp) psp)
DEFINES="$DEFINES -DELF_LOADER_TARGET -DMIPS_TARGET" DEFINES="$DEFINES -DELF_LOADER_TARGET -DMIPS_TARGET -DELF_LOADER_CXA_ATEXIT"
_def_plugin=' _def_plugin='
#define PLUGIN_PREFIX "" #define PLUGIN_PREFIX ""
#define PLUGIN_SUFFIX ".plg" #define PLUGIN_SUFFIX ".plg"
@ -2120,7 +2123,7 @@ PLUGIN_SUFFIX := .plg
PLUGIN_EXTRA_DEPS = $(EXECUTABLE) PLUGIN_EXTRA_DEPS = $(EXECUTABLE)
CXXFLAGS += -DDYNAMIC_MODULES CXXFLAGS += -DDYNAMIC_MODULES
LDFLAGS += -Wl,-T$(srcdir)/backends/platform/psp/main_prog.ld LDFLAGS += -Wl,-T$(srcdir)/backends/platform/psp/main_prog.ld
PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols,$(EXECUTABLE),--retain-symbols-file,$(srcdir)/backends/plugins/elf/plugin.syms,-T$(srcdir)/backends/plugins/psp/plugin.ld -lstdc++ -lc -lm -Wl,--wrap,memcpy PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols,$(EXECUTABLE),--retain-symbols-file,$(srcdir)/backends/plugins/elf/plugin.syms,-T$(srcdir)/backends/plugins/psp/plugin.ld -lc -Wl,--wrap,memcpy
PRE_OBJS_FLAGS := -Wl,--whole-archive PRE_OBJS_FLAGS := -Wl,--whole-archive
POST_OBJS_FLAGS := -Wl,--no-whole-archive POST_OBJS_FLAGS := -Wl,--no-whole-archive
' '