aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/plugins/elf/elf-provider.cpp57
-rw-r--r--backends/plugins/elf/elf-provider.h4
-rw-r--r--base/plugins.h8
-rwxr-xr-xconfigure13
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 <cxxabi.h>
+#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
'