Skip to content

Commit

Permalink
plugin-interface: use extern "C" for plugin init function
Browse files Browse the repository at this point in the history
Currently, smartvmi loads plugins dynamically by loading their
shared object. From that shared object, the main entry function is
searched. The problem with that is that the current approach depends
on the hard-coded C++-mangled symbol name. This however depends on
the toolchain (compiler version etc.). Specifically, when compiled
on NixOS, the name didn't match the upstream source code version.

As this symbol name is only relevant when loading a shared object into
memory as part of the shared object's metadata analysis, this name
doesn't have to be unique across plugins. Therefore, to get rid of
the hardcoded symbol name, we use "extern C" to make things easier
and more portable.

The function was renamed from "init" to "init_plugin" to have a
more expressive name.

The disadvantage is that the caller can no longer implicitly verify
the function signature used when the plugin was compiled.

However, with a few more assertions, we can mitigate this mostly.
  • Loading branch information
phip1611 committed Sep 11, 2024
1 parent 1f25821 commit 12765a4
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 13 deletions.
8 changes: 6 additions & 2 deletions plugins/apitracing/src/lib/ApiTracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,14 @@ namespace ApiTracing
}
}

std::unique_ptr<IPlugin>
VmiCore::Plugin::init(PluginInterface* pluginInterface,
extern "C" std::unique_ptr<IPlugin>
VmiCore::Plugin::init_plugin(PluginInterface* pluginInterface,
std::shared_ptr<IPluginConfig> config, // NOLINT(performance-unnecessary-value-param)
std::vector<std::string> args)
{
if (pluginInterface == nullptr) {
fprintf(stderr, "Passed invalid parameter `pluginInterface`: null\n");
return nullptr;
}
return std::make_unique<ApiTracing::ApiTracing>(pluginInterface, *config, args);
}
8 changes: 6 additions & 2 deletions plugins/inmemoryscanner/src/lib/InMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ namespace InMemoryScanner
}
}

std::unique_ptr<IPlugin>
VmiCore::Plugin::init(PluginInterface* pluginInterface,
extern "C" std::unique_ptr<IPlugin>
VmiCore::Plugin::init_plugin(PluginInterface* pluginInterface,
std::shared_ptr<IPluginConfig> config, // NOLINT(performance-unnecessary-value-param)
std::vector<std::string> args)
{
if (pluginInterface == nullptr) {
fprintf(stderr, "Passed invalid parameter `pluginInterface`: null\n");
return nullptr;
}
return std::make_unique<InMemoryScanner::InMemory>(pluginInterface, *config, args);
}
11 changes: 8 additions & 3 deletions plugins/template/src/lib/Template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,17 @@ namespace Template
}
}

// This is the init function. It is called by VmiCore and is responsible for creating an instance of a plugin.
// This is the init function. It is linked and called dynamically at runtime by
// VmiCore and is responsible for creating an instance of a plugin.
// All plugins have to inherit from IPlugin.
std::unique_ptr<IPlugin>
VmiCore::Plugin::init(PluginInterface* pluginInterface,
extern "C" std::unique_ptr<IPlugin>
VmiCore::Plugin::init_plugin(PluginInterface* pluginInterface,
std::shared_ptr<IPluginConfig> config, // NOLINT(performance-unnecessary-value-param)
std::vector<std::string> args)
{
if (pluginInterface == nullptr) {
fprintf(stderr, "Passed invalid parameter `pluginInterface`: null\n");
return nullptr;
}
return std::make_unique<Template::Template>(pluginInterface, *config, args);
}
4 changes: 2 additions & 2 deletions vmicore/src/include/vmicore/plugins/IPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ namespace VmiCore::Plugin
* @param args Commandline arguments passed to this specific plugin. Elements are whitespace separated.
* @return An instance of the plugin. Has to implement the IPlugin interface. Lifetime will be managed by VMICore.
*/
extern std::unique_ptr<IPlugin>
init(PluginInterface* pluginInterface, std::shared_ptr<IPluginConfig> config, std::vector<std::string> args);
extern "C" std::unique_ptr<IPlugin>
init_plugin(PluginInterface* pluginInterface, std::shared_ptr<IPluginConfig> config, std::vector<std::string> args);
}

#endif // APITRACING_IPLUGIN_H
9 changes: 5 additions & 4 deletions vmicore/src/lib/plugins/PluginSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,18 @@ namespace VmiCore
Plugin::PluginInterface::API_VERSION));
}

auto pluginInitFunction = std::bit_cast<decltype(Plugin::init)*>(
dlsym(libraryHandle,
"_ZN7VmiCore6Plugin4initEPNS0_15PluginInterfaceENSt3__110shared_ptrINS0_13IPluginConfigEEENS3_"
"6vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENSB_ISD_EEEE"));
auto pluginInitFunction = std::bit_cast<decltype(Plugin::init_plugin)*>(
dlsym(libraryHandle, PLUGIN_INIT_FUNCTION.c_str()));
dlErrorMessage = dlerror();
if (dlErrorMessage != nullptr)
{
throw PluginException(pluginName, fmt::format("Unable to retrieve init function: {}", dlErrorMessage));
}

auto plugin = pluginInitFunction(dynamic_cast<Plugin::PluginInterface*>(this), std::move(config), args);
if (plugin == nullptr) {
throw PluginException(pluginName, fmt::format("init function returned null"));
}

plugins.emplace_back(pluginName, std::move(plugin));
}
Expand Down
2 changes: 2 additions & 0 deletions vmicore/src/lib/plugins/PluginSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace VmiCore
{
static const std::string PLUGIN_INIT_FUNCTION = "init_plugin";

class IPluginSystem : public Plugin::PluginInterface
{
public:
Expand Down

0 comments on commit 12765a4

Please sign in to comment.