Skip to content
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

Included utils to load, unload and get symbols from shared libraries #215

Merged
merged 35 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
268533b
Included utils to load, unload and get symbols from shared libraries
ahcorde Mar 10, 2020
076723e
platform independent code moved outside of the conditional blocks
ahcorde Mar 10, 2020
e87960b
Included feedback
ahcorde Mar 11, 2020
696e0ea
Exported CMake_DL_LIBS
ahcorde Mar 12, 2020
955e9ae
Included more feedback
ahcorde Mar 12, 2020
d1926b7
Improved tests
ahcorde Mar 12, 2020
277b748
Fixed argument checks in rcutils_has_symbol
ahcorde Mar 13, 2020
c91089c
fixed naming rcutils_unload_library to rcutils_unload_shared_library
ahcorde Mar 13, 2020
e07d449
Added feedback
ahcorde Mar 13, 2020
a7456f2
deallocated memory when open fails
ahcorde Mar 17, 2020
55fdb7d
Added null after deallocate memory
ahcorde Mar 17, 2020
f0213b6
Updated doc shared_library
ahcorde Mar 18, 2020
28cd1d5
Improved error handling
ahcorde Mar 18, 2020
0b6d171
Fixed uncrustify and cppcheck
ahcorde Mar 18, 2020
2fb659a
Added allocator argument to rcutils_load_shared_library funcion
ahcorde Mar 23, 2020
4763b69
checked inputs and deduplicate code
ahcorde Mar 23, 2020
791214a
added more test to shared_library
ahcorde Mar 23, 2020
16a3173
fixed unscrustify test_shared_library
ahcorde Mar 23, 2020
f5da00b
Fixed docs
ahcorde Mar 26, 2020
60fe43c
Update include/rcutils/shared_library.h
ahcorde Mar 26, 2020
13fe9ff
Improved docs
ahcorde Mar 26, 2020
af0d73e
update shared library docs
ahcorde Mar 27, 2020
8dc66df
Avoided leak when a library is loaded two time without unload
ahcorde Mar 30, 2020
e154f9c
changed the order to deallocate memory
ahcorde Mar 31, 2020
124cf99
Added extension for different platforms in test_shared_libraries
ahcorde Mar 31, 2020
c6bbb2c
fixed ccplint
ahcorde Mar 31, 2020
80b7ea1
Fixed dummy library name
ahcorde Mar 31, 2020
2779d6b
Right library name in windows in test_shared_library
ahcorde Mar 31, 2020
8ab106f
preprocessor directives not indented
ahcorde Mar 31, 2020
d941e44
Added rcutils_get_platform_library_name fucntion to shared_library
ahcorde Mar 31, 2020
26b7157
fixed rcutils_snprintf buffer size
ahcorde Mar 31, 2020
66187a3
Added target_compile_definitions for dummy_shared_library
ahcorde Apr 1, 2020
510bf35
defined len of str in rcutils_get_platform_library_name
ahcorde Apr 1, 2020
e049c20
Removed limitation on the path size for the library name
ahcorde Apr 1, 2020
5cc468e
fixed condition len string in rcutils_get_platform_library_name
ahcorde Apr 1, 2020
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
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ set(rcutils_sources
src/logging.c
src/process.c
src/repl_str.c
src/shared_library.c
src/snprintf.c
src/split.c
src/strdup.c
Expand Down Expand Up @@ -99,6 +100,8 @@ add_library(
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME} PRIVATE "RCUTILS_BUILDING_DLL")

target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS})
dirk-thomas marked this conversation as resolved.
Show resolved Hide resolved

# Needed if pthread is used for thread local storage.
if(IOS AND IOS_SDK_VERSION LESS 10.0)
ament_export_libraries(pthread)
Expand Down Expand Up @@ -312,6 +315,17 @@ if(BUILD_TESTING)
target_link_libraries(test_repl_str ${PROJECT_NAME})
endif()

set(append_library_dirs "$<TARGET_FILE_DIR:${PROJECT_NAME}>")

ament_add_gtest(test_shared_library test/test_shared_library.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)

if(TARGET test_shared_library)
add_library(dummy_shared_library test/dummy_shared_library/dummy_shared_library.c)
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
target_link_libraries(test_shared_library ${PROJECT_NAME})
endif()

rcutils_custom_add_gtest(test_time
test/test_time.cpp
ENV ${memory_tools_test_env_vars})
Expand Down Expand Up @@ -350,7 +364,7 @@ endif()

ament_export_dependencies(ament_cmake)
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
ament_export_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS})
ament_package()

install(
Expand Down
149 changes: 149 additions & 0 deletions include/rcutils/shared_library.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCUTILS__SHARED_LIBRARY_H_
#define RCUTILS__SHARED_LIBRARY_H_

#ifdef __cplusplus
extern "C"
{
#endif

#include <string.h>

#ifndef _WIN32
#include <dlfcn.h>
typedef void * rcutils_shared_library_handle_t;
#else
#include <windows.h>
typedef HINSTANCE rcutils_shared_library_handle_t;
#endif // _WIN32

#include "rcutils/allocator.h"
#include "rcutils/types/rcutils_ret.h"
#include "rcutils/macros.h"
#include "rcutils/visibility_control.h"

/// Handle to a loaded shared library.
typedef struct RCUTILS_PUBLIC_TYPE rcutils_shared_library_t
{
/// The pointer to the shared library
rcutils_shared_library_handle_t lib_pointer;
/// The path of the shared_library
char * library_path;
/// allocator
rcutils_allocator_t allocator;
} rcutils_shared_library_t;

/// Return an empty shared library struct.
/*
* This function returns an empty and zero initialized shared library struct.
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
*
* Example:
*
* ```c
* // Do not do this:
* // rcutils_shared_library_t foo;
* // rcutils_ret_t ret = rcutils_load_shared_library(
* // &foo,
* // "library_name",
* // rcutils_get_default_allocator()); // undefined behavior!
* // or
* // rcutils_ret_t ret = rcutils_unload_shared_library(&foo); // undefined behavior!
*
* // Do this instead:
* rcutils_shared_library_t bar = rcutils_get_zero_initialized_shared_library();
* rcutils_load_shared_library(&bar, "library_name", rcutils_get_default_allocator()); // ok
* void * symbol = rcutils_get_symbol(&bar, "bazinga"); // ok
* bool is_bazinga_symbol = rcutils_has_symbol(&bar, "bazinga"); // ok
* rcutils_ret_t ret = rcutils_unload_shared_library(&bar); // ok
* if (ret != RCUTILS_RET_ERROR) {
* // error handling
* }
* ```
* */
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
rcutils_shared_library_t
rcutils_get_zero_initialized_shared_library(void);

/// Return shared library pointer.
/**
* \param[inout] lib struct with the shared library pointer and shared library path name
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
* \param[in] library_path string with the path of the library
* \param[in] allocator to be used to allocate and deallocate memory
* \return `RCUTILS_RET_OK` if successful, or
* \return `RCUTILS_RET_BAD_ALLOC` if memory allocation fails, or
* \return `RCUTILS_RET_ERROR` if an unknown error occurs, or
* \return `RCUTILS_RET_INVALID_ARGUMENT` for invalid arguments
dirk-thomas marked this conversation as resolved.
Show resolved Hide resolved
*/
RCUTILS_PUBLIC
wjwwood marked this conversation as resolved.
Show resolved Hide resolved
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_load_shared_library(
rcutils_shared_library_t * lib,
const char * library_path,
rcutils_allocator_t allocator);

/// Return shared library symbol pointer.
/**
* \param[in] lib struct with the shared library pointer and shared library path name
* \param[in] symbol_name name of the symbol inside the shared library
* \return shared library symbol pointer, if the symbol doesn't exist then returns NULL.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
void *
wjwwood marked this conversation as resolved.
Show resolved Hide resolved
rcutils_get_symbol(const rcutils_shared_library_t * lib, const char * symbol_name);

/// Return true if the shared library contains a specific symbol name otherwise returns false.
/**
* \param[in] lib struct with the shared library pointer and shared library path name
* \param[in] symbol_name name of the symbol inside the shared library
* \return if symbols exists returns true, otherwise returns false.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
bool
rcutils_has_symbol(const rcutils_shared_library_t * lib, const char * symbol_name);

/// Unload the shared library.
/**
* \param[in] lib rcutils_shared_library_t to be finalized
* \return `RCUTILS_RET_OK` if successful, or
* \return `RCUTILS_RET_INVALID_ARGUMENT` for invalid arguments, or
* \return `RCUTILS_RET_ERROR` if an unknown error occurs
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_unload_shared_library(rcutils_shared_library_t * lib);

/// Get the library name for the compiled platform
/**
* \param[in] library_name library base name (without prefix and extension)
* \param[out] library_name_platform library name for the compiled platform
* \return `RCUTILS_RET_OK` if successful, or
* \return `RCUTILS_RET_ERROR` if an unknown error occurs
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_get_platform_library_name(const char * library_name, char * library_name_platform);
dirk-thomas marked this conversation as resolved.
Show resolved Hide resolved

#ifdef __cplusplus
}
#endif

#endif // RCUTILS__SHARED_LIBRARY_H_
185 changes: 185 additions & 0 deletions src/shared_library.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
#include <stdlib.h>

#include "rcutils/error_handling.h"
#include "rcutils/shared_library.h"
#include "rcutils/strdup.h"

ahcorde marked this conversation as resolved.
Show resolved Hide resolved
rcutils_shared_library_t
rcutils_get_zero_initialized_shared_library(void)
{
rcutils_shared_library_t zero_initialized_shared_library;
zero_initialized_shared_library.library_path = NULL;
zero_initialized_shared_library.lib_pointer = NULL;
zero_initialized_shared_library.allocator = rcutils_get_zero_initialized_allocator();
return zero_initialized_shared_library;
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
}

rcutils_ret_t
rcutils_load_shared_library(
rcutils_shared_library_t * lib,
const char * library_path,
rcutils_allocator_t allocator)
{
RCUTILS_CHECK_ARGUMENT_FOR_NULL(lib, RCUTILS_RET_INVALID_ARGUMENT);
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
RCUTILS_CHECK_ARGUMENT_FOR_NULL(library_path, RCUTILS_RET_INVALID_ARGUMENT);
RCUTILS_CHECK_ALLOCATOR(&allocator, return RCUTILS_RET_INVALID_ARGUMENT);

if (lib->library_path != NULL) {
lib->allocator.deallocate(lib->library_path, lib->allocator.state);
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
}

lib->allocator = allocator;

lib->library_path = rcutils_strdup(library_path, lib->allocator);
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
if (NULL == lib->library_path) {
RCUTILS_SET_ERROR_MSG("unable to allocate memory");
return RCUTILS_RET_BAD_ALLOC;
}

#ifndef _WIN32
lib->lib_pointer = dlopen(lib->library_path, RTLD_LAZY);
if (!lib->lib_pointer) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("LoadLibrary error: %s", dlerror());
#else
lib->lib_pointer = LoadLibrary(lib->library_path);
if (!lib->lib_pointer) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("LoadLibrary error: %lu", GetLastError());
#endif // _WIN32
lib->allocator.deallocate(lib->library_path, lib->allocator.state);
lib->library_path = NULL;
return RCUTILS_RET_ERROR;
}
return RCUTILS_RET_OK;
}

void *
rcutils_get_symbol(const rcutils_shared_library_t * lib, const char * symbol_name)
{
if (!lib || !lib->lib_pointer || (symbol_name == NULL)) {
RCUTILS_SET_ERROR_MSG("invalid inputs arguments");
return NULL;
}

#ifndef _WIN32
void * lib_symbol = dlsym(lib->lib_pointer, symbol_name);
wjwwood marked this conversation as resolved.
Show resolved Hide resolved
char * error = dlerror();
if (error != NULL) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Error getting the symbol '%s'. Error '%s'",
symbol_name, error);
return NULL;
}
#else
void * lib_symbol = GetProcAddress(lib->lib_pointer, symbol_name);
if (lib_symbol == NULL) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Error getting the symbol '%s'. Error '%d'",
symbol_name, GetLastError());
return NULL;
}
#endif // _WIN32
if (!lib_symbol) {
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
"symbol '%s' does not exist in the library '%s'",
symbol_name, lib->library_path);
return NULL;
}
return lib_symbol;
}

bool
rcutils_has_symbol(const rcutils_shared_library_t * lib, const char * symbol_name)
{
if (!lib || !lib->lib_pointer || symbol_name == NULL) {
return false;
}

#ifndef _WIN32
// the correct way to test for an error is to call dlerror() to clear any old error conditions,
// then call dlsym(), and then call dlerror() again, saving its return value into a variable,
// and check whether this saved value is not NULL.
dlerror(); /* Clear any existing error */
void * lib_symbol = dlsym(lib->lib_pointer, symbol_name);
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
return dlerror() == NULL && lib_symbol != 0;
#else
void * lib_symbol = GetProcAddress(lib->lib_pointer, symbol_name);
return GetLastError() == 0 && lib_symbol != 0;
#endif // _WIN32
}

rcutils_ret_t
rcutils_unload_shared_library(rcutils_shared_library_t * lib)
{
RCUTILS_CHECK_ARGUMENT_FOR_NULL(lib, RCUTILS_RET_INVALID_ARGUMENT);
RCUTILS_CHECK_ARGUMENT_FOR_NULL(lib->lib_pointer, RCUTILS_RET_INVALID_ARGUMENT);
RCUTILS_CHECK_ARGUMENT_FOR_NULL(lib->library_path, RCUTILS_RET_INVALID_ARGUMENT);
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
RCUTILS_CHECK_ALLOCATOR(&lib->allocator, return RCUTILS_RET_INVALID_ARGUMENT);

rcutils_ret_t ret = RCUTILS_RET_OK;
#ifndef _WIN32
// The function dlclose() returns 0 on success, and nonzero on error.
int error_code = dlclose(lib->lib_pointer);
if (error_code) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("dlclose error: %s", dlerror());
#else
// If the function succeeds, the return value is nonzero.
int error_code = FreeLibrary(lib->lib_pointer);
if (!error_code) {
wjwwood marked this conversation as resolved.
Show resolved Hide resolved
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("FreeLibrary error: %lu", GetLastError());
#endif // _WIN32
ret = RCUTILS_RET_ERROR;
}

lib->allocator.deallocate(lib->library_path, lib->allocator.state);
lib->library_path = NULL;
lib->lib_pointer = NULL;
lib->allocator = rcutils_get_zero_initialized_allocator();
return ret;
}

rcutils_ret_t
rcutils_get_platform_library_name(const char * library_name, char * library_name_platform)
{
int written = 0;

#ifdef __linux__
written = rcutils_snprintf(
library_name_platform, strlen(library_name) + 7, "lib%s.so", library_name);
#elif __APPLE__
written = rcutils_snprintf(
library_name_platform, strlen(library_name) + 10, "lib%s.dylib", library_name);
#elif _WIN32
written = rcutils_snprintf(
library_name_platform, strlen(library_name) + 5, "%s.dll", library_name);
#endif
if (written < 0) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to format library name: '%s'\n",
library_name);
return RCUTILS_RET_ERROR;
}
return RCUTILS_RET_OK;
}

#ifdef __cplusplus
}
#endif
Loading