Skip to content

Commit

Permalink
Core: replace RTLD_DEEPBIND (dlopen flag) with a new plugin flag
Browse files Browse the repository at this point in the history
Using RTLD_DEEPBIND flag when loading plugin helps to solve issue
with some 3rd party libraries (e.g. librdkafka) if the library
redefines a common symbol. However, using this dlopen flag globally
for all plugins caused segfault when using C++ iostream (e.g.
"std::cout << "some text";)

Plugins can use IPX_PF_DEEPBIND flag in ipx_plugin_info to
instruct the collector to use RTLD_DEEPBIND when loading the plugin.
  • Loading branch information
Lukas955 committed Jul 10, 2020
1 parent 05add4c commit af9cdf2
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
17 changes: 16 additions & 1 deletion include/ipfixcol2/plugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ typedef struct ipx_ctx_ext ipx_ctx_ext_t;
*/
#define IPX_PT_OUTPUT 3U

/**
* \def IPX_PF_DEEPBIND
* \brief Use deep bind when resolving symbols of a plugin (and additional depending libraries)
*
* Some plugins might depend on an external library which redefines one or more common symbols
* (e.g. thrd_create) used by the collector (or other plugins). Since common version of these
* symbols is resolved before any plugin is loaded, these redefined symbols are ignored.
* Therefore, the plugin (or third party libraries) might not be able to correctly work.
*
* This flag instructs the collector to use RTLD_DEEPBIND (see manual page of dlopen) which
* solves this issue. However, it might not be supported by non-glibc implementations
* (as it is a GNU extension) and might break some other functions. Use only if really required!
*/
#define IPX_PF_DEEPBIND 1U

/**
* \brief Identification of a plugin
*
Expand All @@ -149,7 +164,7 @@ struct ipx_plugin_info {
const char *dsc;
/** Plugin type (one of #IPX_PT_INPUT, #IPX_PT_INTERMEDIATE, #IPX_PT_OUTPUT) */
uint16_t type;
/** Configuration flags (reserved for future use) */
/** Configuration flags (zero or more IPX_PF_* values might be ORed here) */
uint16_t flags;
/** Plugin version string (like "1.2.3") */
const char *version;
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ if (POLICY CMP0063)
cmake_policy(SET CMP0063 NEW)
endif()

include(CheckSymbolExists)
check_symbol_exists("RTLD_DEEPBIND" "dlfcn.h" HAVE_RTLD_DEEPBIND)

# Configure a header file to pass some CMake variables
configure_file(
"${PROJECT_SOURCE_DIR}/src/build_config.h.in"
Expand Down
3 changes: 3 additions & 0 deletions src/build_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@
#define IPX_BUILD_ARCH "unknown"
#endif

// Deep bind support
#cmakedefine HAVE_RTLD_DEEPBIND

/**@}*/

#endif /* _BUILD_CONFIG_ */
Expand Down
51 changes: 43 additions & 8 deletions src/core/configurator/plugin_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ ipx_plugin_mgr::cache_add_file(const char *path)
dlerror();

// Try to load the plugin (and unload it automatically)
const int flags = RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND;
const int flags = RTLD_LAZY | RTLD_LOCAL;
auto delete_fn = [](void *handle) {dlclose(handle);};
std::unique_ptr<void, std::function<void(void*)>> handle(dlopen(path, flags), delete_fn);
if (!handle) {
Expand Down Expand Up @@ -308,7 +308,7 @@ ipx_plugin_mgr::plugin_list()
dlerror();

const char *path = cache_entry.path.c_str();
const int flags = RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND;
const int flags = RTLD_LAZY | RTLD_LOCAL;
auto delete_fn = [](void *handle) {dlclose(handle);};

std::unique_ptr<void, std::function<void(void*)>> handle(dlopen(path, flags), delete_fn);
Expand Down Expand Up @@ -348,6 +348,15 @@ ipx_plugin_mgr::plugin_list()
continue;
}

if (info->flags & IPX_PF_DEEPBIND) {
#ifdef HAVE_RTLD_DEEPBIND
plugin_entry.msg_notes.emplace_back("Deep bind (RTLD_DEEPBIND) required");
#else
plugin_entry.msg_warning.emplace_back(
"Deep bind (RTLD_DEEPBIND) required but not supported by C library");
#endif
}

if (!version_ok) {
std::stringstream ss;
ss << "incompatible with this collector version (min. required: ";
Expand Down Expand Up @@ -503,18 +512,44 @@ ipx_plugin_mgr::version_check(const std::string &min_version)
* \param[in] path Path to the plugin to load
* \param[in] auto_unload Automatically unload the plugin on destroy (can be changed later)
*/
ipx_plugin_mgr::plugin::plugin(const std::string &path, bool auto_unload)
ipx_plugin_mgr::plugin::plugin(const std::string &path, bool auto_unload)
: unload(auto_unload)
{
auto delete_fn = [](void *handle) {dlclose(handle);};
std::unique_ptr<void, std::function<void(void*)>> handle_wrap(nullptr, delete_fn);

const struct ipx_plugin_info *info;
int load_flags = RTLD_NOW | RTLD_LOCAL;

// Initialize default parameters
ref_cnt = 0;
std::memset(&cbs, 0, sizeof(cbs));

// Load the plugin
auto delete_fn = [](void *handle) {dlclose(handle);};
int flags = RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND;
dlerror(); // Clear all errors first
std::unique_ptr<void, std::function<void(void*)>> handle_wrap(dlopen(path.c_str(), flags), delete_fn);
// Determine whether or not to use deep bind
dlerror();
handle_wrap.reset(dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL));
if (!handle_wrap) {
// Failed to open the file
std::string err_msg = "Failed to load a plugin from '" + path + "': " + dlerror();
throw ipx_plugin_mgr::error(err_msg);
}

*(void **)(&info) = symbol_get(handle_wrap.get(), "ipx_plugin_info", false);
if (info->flags & IPX_PF_DEEPBIND) {
#ifdef HAVE_RTLD_DEEPBIND
IPX_DEBUG(comp_str, "Loading plugin from '%s' using RTLD_DEEPBIND flag!", path.c_str());
load_flags |= RTLD_DEEPBIND;
#else
std::string err_msg = "Deep bind (RTLD_DEEPBIND) required but not supported by C library";
throw ipx_plugin_mgr::error("Failed to load a plugin from '" + path + "': " + err_msg);
#endif
}

// Close the plugin and reload it
handle_wrap.reset();

dlerror();
handle_wrap.reset(dlopen(path.c_str(), load_flags));
if (!handle_wrap) {
// Failed to open the file
std::string err_msg = "Failed to load a plugin from '" + path + "': " + dlerror();
Expand Down

0 comments on commit af9cdf2

Please sign in to comment.