Skip to content

Attempt plugin load from global symbol table #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 26, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions inc/libfunctionality/Common.h
Original file line number Diff line number Diff line change
@@ -64,9 +64,24 @@ Copyright 2014 Sensics, Inc.
/** @brief Utility macro for token pasting aka concatenation */
#define LIBFUNC_DETAIL_CAT(A, B) LIBFUNC_DETAIL_CAT_IMPL(A##B)

/** @brief Utility macro used in stringification of macros. */
#define LIBFUNC_DETAIL_STRINGIFY_IMPL(X) #X

/** @brief Utility macro for stringification of macro expansions. */
#define LIBFUNC_DETAIL_STRINGIFY(X) LIBFUNC_DETAIL_STRINGIFY_IMPL(X)

/** @brief The prefix appended to a plugin name to generate a unique entry point
* name. */
#define LIBFUNC_DETAIL_EP_PREFIX libfunc_ep_

/** @brief Utility macro (second-level expansion) for unique entry point
* generation */
#define LIBFUNC_DETAIL_EP_NAME_IMPL(X, PLUGINNAME) \
LIBFUNC_DETAIL_CAT(X, PLUGINNAME)

/** @brief Generate the unique entry point name for each plugin. */
#define LIBFUNC_DETAIL_EP_NAME(PLUGINNAME) \
LIBFUNC_DETAIL_CAT(libfunc_ep_, PLUGINNAME)
LIBFUNC_DETAIL_EP_NAME_IMPL(LIBFUNC_DETAIL_EP_PREFIX, PLUGINNAME)

/** @brief Return type of the entry point function. */
typedef char libfunc_ep_return_t;
@@ -90,7 +105,8 @@ typedef char libfunc_ep_return_t;
/** @brief The string name of the common entry point.
@todo make this use c preproc stringize while dodging portability problems.
*/
#define LIBFUNC_DETAIL_EP_COMMON_NAME_STRING "libfunc_entry_point"
#define LIBFUNC_DETAIL_EP_COMMON_NAME_STRING \
LIBFUNC_DETAIL_STRINGIFY(LIBFUNC_DETAIL_EP_COMMON_NAME)

/** @brief Declaration of the common entry point used in dynamic mode. */
#define LIBFUNC_DETAIL_EP_COMMON_DECLARATION \
2 changes: 1 addition & 1 deletion inc/libfunctionality/PluginInterface.h
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ Copyright 2014 Sensics, Inc.
* In dynamic mode, we have to create the common entry point as a trampoline to
* the unique one. */
#define LIBFUNC_DETAIL_PLUGIN(PLUGINNAME) \
LIBFUNC_DETAIL_EP_DECLARATION(PLUGINNAME); \
LIBFUNC_DETAIL_EP_DECORATION LIBFUNC_DETAIL_EP_DECLARATION(PLUGINNAME); \
LIBFUNC_DETAIL_EP_DECORATION LIBFUNC_DETAIL_EP_COMMON_DECLARATION { \
return LIBFUNC_DETAIL_EP_NAME(PLUGINNAME)(LIBFUNC_DETAIL_PARAM_NAME); \
}
81 changes: 53 additions & 28 deletions src/libfunctionality/LoadPluginLibdl.h
Original file line number Diff line number Diff line change
@@ -45,46 +45,71 @@ PluginHandle loadPluginByName(const char *n, void *opaque) {
return loadPluginByName(std::string(n), opaque);
}

PluginHandle loadPluginByName(std::string const &n, void *opaque) {
if (n.empty()) {
throw exceptions::BadPluginName();
}

LibraryHandle lib(RAIILoadLibrary(n + LIBFUNC_MODULE_SUFFIX));

if (!lib) {
throw exceptions::CannotLoadPlugin(n);
}
typedef void *(*DlsymReturn)();

/// Mini helper wrapper around dlsym, because that cast is nasty.
static inline DlsymReturn retrieveEntryPoint(void *handle, const char *name) {
DlsymReturn raw_ep;
// Appropriate, but odd, syntax per the dlopen(3) man page.
// and
// http://stackoverflow.com/questions/1354537/dlsym-dlopen-with-runtime-arguments
dlerror(); // clear errors
dlerror(); // clear the error
/// @todo This is more C than C++, but it's Posix-recommended,
*(void **)(&raw_ep) = dlsym(handle, name);
return raw_ep;
}

/// @todo Pick one of these two implementations - one is more C and
/// Posix-recommended,
/// the other is simpler C++.
#if 1
typedef void *(*DlsymReturn)();
DlsymReturn raw_ep;
*(void **)(&raw_ep) =
dlsym(lib.get(), LIBFUNC_DETAIL_EP_COMMON_NAME_STRING);
if (dlerror() != NULL || raw_ep == NULL) {
throw exceptions::CannotLoadEntryPoint(n);
}
/// internal helper function used by both the global symbol table entry point
/// path as well as the load from the dlopened handle path
static inline entry_point_t convertAndCallEntryPoint(std::string const &n,
DlsymReturn raw_ep,
void *opaque) {
entry_point_t ep = reinterpret_cast<entry_point_t>(raw_ep);
#else
entry_point_t ep = reinterpret_cast<entry_point_t>(
dlsym(lib.get(), LIBFUNC_DETAIL_EP_COMMON_NAME_STRING));
#endif

if (dlerror() != NULL || ep == NULL) {
if (ep == NULL) {
throw exceptions::CannotLoadEntryPoint(n);
}
libfunc_ep_return_t result = (*ep)(opaque);
if (result != LIBFUNC_RETURN_SUCCESS) {
throw exceptions::PluginEntryPointFailed(n);
}
return ep;
}

PluginHandle loadPluginByName(std::string const &n, void *opaque) {
if (n.empty()) {
throw exceptions::BadPluginName();
}

// attempt to load the symbol from the global symbol table. If successful,
// plugin is already pre-loaded
{
std::string ep_name =
std::string(LIBFUNC_DETAIL_STRINGIFY(LIBFUNC_DETAIL_EP_PREFIX)) + n;
DlsymReturn raw_ep = retrieveEntryPoint(RTLD_DEFAULT, ep_name.c_str());

const char *err = dlerror();
if (err == NULL && raw_ep != NULL) {
convertAndCallEntryPoint(n, raw_ep, opaque);
// Yes, returning an empty PluginHandle here works fine, since it's
// just for lifetime management.
return PluginHandle();
}
}

LibraryHandle lib(RAIILoadLibrary(n + LIBFUNC_MODULE_SUFFIX));

if (!lib) {
throw exceptions::CannotLoadPlugin(n);
}

{
DlsymReturn raw_ep =
retrieveEntryPoint(lib.get(), LIBFUNC_DETAIL_EP_COMMON_NAME_STRING);
if (dlerror() != NULL || raw_ep == NULL) {
throw exceptions::CannotLoadEntryPoint(n);
}
convertAndCallEntryPoint(n, raw_ep, opaque);
}

return PluginHandle(lib);
}
15 changes: 15 additions & 0 deletions tests/cplusplus/LoadTest.cpp
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@

// Internal Includes
#include <libfunctionality/LoadPlugin.h>
#include <libfunctionality/PluginInterface.h>

// Library/third-party includes
#include "gtest/gtest.h"
@@ -35,6 +36,20 @@

using std::string;

// loading from the global symbol table is only implemented currently in the libdl path
// @todo implement global symbol table plugin loading in the win32 path and remove this ifdef
#ifdef LIBFUNC_DL_LIBDL

LIBFUNC_PLUGIN_NO_PARAM(com_sensics_libfunc_tests_staticplugin) {
return LIBFUNC_RETURN_SUCCESS;
}

TEST(load_static_plugin, load_from_global) {
ASSERT_NO_THROW((libfunc::loadPluginByName("com_sensics_libfunc_tests_staticplugin", NULL)));
}

#endif

TEST(load_dummy_plugin, cstr_name_null_data) {
ASSERT_NO_THROW((libfunc::loadPluginByName("DummyPlugin", NULL)));
}