From ae408db07f05407c41ed4831e4e23991574588d6 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Mon, 6 Sep 2010 20:34:00 +0000 Subject: 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 --- backends/plugins/elf/elf-provider.cpp | 57 +++++++++++++++++++++++++++++++++++ backends/plugins/elf/elf-provider.h | 4 ++- base/plugins.h | 8 +++++ configure | 13 +++++--- 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/backends/plugins/elf/elf-provider.cpp b/backends/plugins/elf/elf-provider.cpp index 7bb39d4347..f3f6bace07 100644 --- a/backends/plugins/elf/elf-provider.cpp +++ b/backends/plugins/elf/elf-provider.cpp @@ -25,11 +25,52 @@ #if defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) +#ifdef ELF_LOADER_CXA_ATEXIT +#include +#endif + #include "backends/plugins/elf/elf-provider.h" #include "backends/plugins/dynamic-plugin.h" +#include "common/debug.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) { void *func = 0; @@ -71,6 +112,14 @@ bool ELFPlugin::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(); return ret; @@ -80,6 +129,14 @@ void ELFPlugin::unloadPlugin() { DynamicPlugin::unloadPlugin(); 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()) warning("elfloader: Failed unloading plugin '%s'", _filename.c_str()); diff --git a/backends/plugins/elf/elf-provider.h b/backends/plugins/elf/elf-provider.h index 5dc61539e1..6918183f1f 100644 --- a/backends/plugins/elf/elf-provider.h +++ b/backends/plugins/elf/elf-provider.h @@ -45,13 +45,15 @@ class ELFPlugin : public DynamicPlugin { protected: DLObject *_dlHandle; Common::String _filename; + void *_dso_handle; virtual VoidFunc findSymbol(const char *symbol); public: ELFPlugin(const Common::String &filename) : _dlHandle(0), - _filename(filename) { + _filename(filename), + _dso_handle(0) { } virtual ~ELFPlugin() { diff --git a/base/plugins.h b/base/plugins.h index 84b1af5599..eb7e93ddba 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -90,6 +90,13 @@ extern int pluginTypeVersions[PLUGIN_TYPE_MAX]; #define PLUGIN_ENABLED_DYNAMIC(ID) \ (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 * 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) \ extern "C" { \ + PLUGIN_DYNAMIC_EXTRA_DECL; \ PLUGIN_EXPORT int32 PLUGIN_getVersion() { return PLUGIN_VERSION; } \ PLUGIN_EXPORT int32 PLUGIN_getType() { return TYPE; } \ PLUGIN_EXPORT int32 PLUGIN_getTypeVersion() { return TYPE##_VERSION; } \ diff --git a/configure b/configure index 3f17cb848b..db903cf83c 100755 --- a/configure +++ b/configure @@ -1424,6 +1424,7 @@ case $_host_os in 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 -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" if test "$_dynamic_modules" = no ; then LDFLAGS="$LDFLAGS -Wl,--gc-sections" @@ -1495,6 +1496,7 @@ case $_host_os in ;; psp) CXXFLAGS="$CXXFLAGS -O3 -I$PSPSDK/include -D_PSP_FW_VERSION=150" + CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit" ;; solaris*) DEFINES="$DEFINES -DSOLARIS -DSYSTEM_NOT_SUPPORTING_D_TYPE" @@ -1505,6 +1507,7 @@ case $_host_os in wii) CXXFLAGS="$CXXFLAGS -Os -mrvl -mcpu=750 -meabi -mhard-float" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched" + CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit" CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include" # libogc is required to link the cc tests (includes _start()) LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii -logc" @@ -2004,7 +2007,7 @@ POST_OBJS_FLAGS := -Wl,--no-whole-archive ' ;; 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=' #define PLUGIN_PREFIX "" #define PLUGIN_SUFFIX ".plg" @@ -2015,7 +2018,7 @@ PLUGIN_PREFIX := PLUGIN_SUFFIX := .plg PLUGIN_EXTRA_DEPS = $(EXECUTABLE) 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 POST_OBJS_FLAGS := -Wl,--no-whole-archive ' @@ -2038,7 +2041,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive ' ;; 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=' #define PLUGIN_PREFIX "" #define PLUGIN_SUFFIX ".plg" @@ -2108,7 +2111,7 @@ POST_OBJS_FLAGS := -Wl,--no-whole-archive ' ;; psp) -DEFINES="$DEFINES -DELF_LOADER_TARGET -DMIPS_TARGET" +DEFINES="$DEFINES -DELF_LOADER_TARGET -DMIPS_TARGET -DELF_LOADER_CXA_ATEXIT" _def_plugin=' #define PLUGIN_PREFIX "" #define PLUGIN_SUFFIX ".plg" @@ -2120,7 +2123,7 @@ PLUGIN_SUFFIX := .plg PLUGIN_EXTRA_DEPS = $(EXECUTABLE) CXXFLAGS += -DDYNAMIC_MODULES 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 POST_OBJS_FLAGS := -Wl,--no-whole-archive ' -- cgit v1.2.3