aboutsummaryrefslogtreecommitdiff
path: root/backends/plugins/elf/elf-provider.cpp
blob: 6d461c79df83b3c4615fe45f8fe5469ec7404c31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/* 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "common/scummsys.h"

#if defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER)

#ifdef ELF_LOADER_CXA_ATEXIT
#include <cxxabi.h>
#endif

#include "backends/plugins/elf/elf-provider.h"
#include "backends/plugins/dynamic-plugin.h"
#include "backends/plugins/elf/memory-manager.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;

	if (_dlHandle)
		func = _dlHandle->symbol(symbol);

	if (!func) {
		if (!_dlHandle)
			warning("elfloader: Failed loading symbol '%s' from plugin '%s' (Handle is NULL)", symbol, _filename.c_str());
		else
			warning("elfloader: Failed loading symbol '%s' from plugin '%s'", symbol, _filename.c_str());
	}

	// FIXME HACK: This is a HACK to circumvent a clash between the ISO C++
	// standard and POSIX: ISO C++ disallows casting between function pointers
	// and data pointers, but dlsym always returns a void pointer. For details,
	// see e.g. <http://www.trilithium.com/johan/2004/12/problem-with-dlsym/>.
	assert(sizeof(VoidFunc) == sizeof(func));
	VoidFunc tmp;
	memcpy(&tmp, &func, sizeof(VoidFunc));
	return tmp;
}

 /**
  * Test the size of the plugin.
  */
void ELFPlugin::trackSize() {
	// All we need to do is create our object, track its size, then delete it
	DLObject *obj = makeDLObject();

	obj->trackSize(_filename.c_str());
	delete obj;
}

bool ELFPlugin::loadPlugin() {
	assert(!_dlHandle);

	DLObject *obj = makeDLObject();
	if (obj->open(_filename.c_str())) {
		_dlHandle = obj;
	} else {
		delete obj;
		_dlHandle = 0;
	}

	if (!_dlHandle) {
		warning("elfloader: Failed loading plugin '%s'", _filename.c_str());
		return false;
	}

	CharFunc buildDateFunc = (CharFunc)findSymbol("PLUGIN_getBuildDate");
	if (!buildDateFunc) {
		unloadPlugin();
		warning("elfloader: plugin '%s' is missing symbols", _filename.c_str());
		return false;
	}

	if (strncmp(gScummVMPluginBuildDate, buildDateFunc(), strlen(gScummVMPluginBuildDate))) {
		unloadPlugin();
		warning("elfloader: plugin '%s' has a different build date", _filename.c_str());
		return false;
	}

	bool ret = DynamicPlugin::loadPlugin();

#ifdef ELF_LOADER_CXA_ATEXIT
	if (ret) {
		// 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->discardSymtab();

	return ret;
}

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());

		delete _dlHandle;
		_dlHandle = 0;
	}
}

 /**
  * We override this function in FilePluginProvider to allow the single
  * plugin method to create a non-fragmenting memory allocation. We take
  * the plugins found and tell the memory manager to allocate space for
  * them.
  */
PluginList ELFPluginProvider::getPlugins() {
	PluginList pl = FilePluginProvider::getPlugins();

#if defined(UNCACHED_PLUGINS) && !defined(ELF_NO_MEM_MANAGER)
	if (!pl.empty()) {
		// This static downcast is safe because all of the plugins must
		// be ELF plugins
		for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) {
			(static_cast<ELFPlugin *>(*p))->trackSize();
		}

		// The Memory Manager should now allocate space based on the information
		// it collected
		ELFMemMan.allocateHeap();
	}
#endif

	return pl;
}

bool ELFPluginProvider::isPluginFilename(const Common::FSNode &node) const {
	// Check the plugin suffix
	Common::String filename = node.getName();

	if (!filename.hasSuffix(".PLG") && !filename.hasSuffix(".plg") &&
			!filename.hasSuffix(".PLUGIN") && !filename.hasSuffix(".plugin"))
		return false;

	return true;
}

#endif // defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER)