diff --git a/.gitignore b/.gitignore index 888035d..2350365 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ build tags **/tags +*.core +.cache +.idea +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index aaba856..1103b0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,9 @@ set(LIBTOOL_INTERFACE 2) set(LIBTOOL_REVISION 3) set(LIBTOOL_AGE 2) +set(GOTCHA_VERSION "(1, 0, 7)") + + set(DEFAULT_SYMBOL_VISIBILITY hidden) if(GOTCHA_ENABLE_TESTS) @@ -22,6 +25,7 @@ if(GOTCHA_ENABLE_TESTS) endif() endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) add_subdirectory(include) add_subdirectory(src) if(GOTCHA_ENABLE_TESTS) @@ -50,3 +54,10 @@ configure_package_config_file( install( FILES "${CMAKE_CURRENT_BINARY_DIR}/gotcha-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/gotcha-config-version.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/gotcha") + +# Write the configure file +configure_file("${CMAKE_SOURCE_DIR}/cmake/gotcha_config.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/gotcha/gotcha_config.h" @ONLY) +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/include/gotcha/gotcha_config.h" + DESTINATION "${gotcha_INSTALL_INCLUDE_DIR}/gotcha/gotcha_config.h") diff --git a/README.md b/README.md index bf85e7c..07f966e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -GOTCHA v1.0.6 +GOTCHA v1.0.7 ============ [![GOTCHA Build and Test](https://github.com/LLNL/GOTCHA/actions/workflows/build-and-test.yaml/badge.svg)](https://github.com/LLNL/GOTCHA/actions/workflows/build-and-test.yaml) [![Coverage Status](https://coveralls.io/repos/github/LLNL/GOTCHA/badge.svg?branch=develop)](https://coveralls.io/github/LLNL/GOTCHA?branch=develop) diff --git a/cmake/gotcha_config.h.in b/cmake/gotcha_config.h.in new file mode 100644 index 0000000..14a4a4d --- /dev/null +++ b/cmake/gotcha_config.h.in @@ -0,0 +1,10 @@ +#ifndef GOTCHA_CONFIG_H +#define GOTCHA_CONFIG_H + +#define GOTCHA_GET_VERSION(MAJOR, MINOR, PATCH) (MAJOR * 100000 + MINOR * 100 + PATCH) +#define GOTCHA_VERSION (GOTCHA_GET_VERSION @GOTCHA_VERSION@) +#define GOTCHA_VERSION_MAJOR (GOTCHA_VERSION / 100000) +#define GOTCHA_VERSION_MINOR ((GOTCHA_VERSION / 100) % 1000) +#define GOTCHA_VERSION_PATCH (GOTCHA_VERSION % 100) + +#endif /* GOTCHA_CONFIG_H */ diff --git a/docs/api.rst b/docs/api.rst index 0f76c40..a67392b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -178,5 +178,30 @@ The default filter of gotcha selects all libraries loaded. This function set the .. explicit external hyperlink targets +--------------------------- +Using Gotcha Version Macros +--------------------------- + +The source version of GOTCHA is defined by the `GOTCHA_VERSION` macro which uses the XYYYZZ format. +**Available since version 1.0.7.** +Here, X signifies the major version, Y is the minor version, and Z is the patch. +Additionally, we define `GOTCHA_VERSION_MAJOR`, `GOTCHA_VERSION_MINOR`, and `GOTCHA_VERSION_PATCH` macros for convienience. +The codes can use the macros like + + +.. code-block:: c + + #if GOTCHA_VERSION > 100006 // this will check of version greater than 1.0.6 + #endif + + #if GOTCHA_VERSION_MAJOR > 1 // this will check of version greater than 2.0.0 + #endif + + #if GOTCHA_VERSION > GOTCHA_GET_VERSION(1,0,6) // this will check of version greater than 1.0.6 + #endif + + + + .. _`gnu constructor`: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html .. _symbol: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/symversion.html diff --git a/docs/conf.py b/docs/conf.py index fe8ce87..57a02b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = u'1.0' # The full version, including alpha/beta/rc tags -release = u'1.0.6' +release = u'1.0.7' # -- General configuration --------------------------------------------------- diff --git a/include/gotcha/gotcha.h b/include/gotcha/gotcha.h index f46fec0..1180245 100644 --- a/include/gotcha/gotcha.h +++ b/include/gotcha/gotcha.h @@ -30,10 +30,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #ifndef GOTCHA_H #define GOTCHA_H +#include +#include #include -#include "gotcha/gotcha_types.h" - #if defined(__cplusplus) extern "C" { #endif diff --git a/src/gotcha.c b/src/gotcha.c index 480c181..3d9db45 100644 --- a/src/gotcha.c +++ b/src/gotcha.c @@ -40,7 +40,7 @@ static void setBindingAddressPointer(struct gotcha_binding_t *in, void *value) { writeAddress(target, value); } -static void **getInternalBindingAddressPointer(struct internal_binding_t **in) { +void **getInternalBindingAddressPointer(struct internal_binding_t **in) { return (void **)&((*in)->wrappee_pointer); } diff --git a/src/gotcha_dl.c b/src/gotcha_dl.c index 16e55b5..7ee28a8 100644 --- a/src/gotcha_dl.c +++ b/src/gotcha_dl.c @@ -113,6 +113,7 @@ static int per_binding(hash_key_t key, hash_data_t data, binding->user_binding->name, binding->associated_binding_table->tool->tool_name); + if (!binding->user_binding->name) return 0; while (binding->next_binding) { binding = binding->next_binding; // GCOVR_EXCL_START debug_printf(3, @@ -157,7 +158,16 @@ static void *dlsym_wrapper(void *handle, const char *symbol_name) { debug_printf(1, "User called dlsym(%p, %s)\n", handle, symbol_name); int result = lookup_hashtable(&function_hash_table, (hash_key_t)symbol_name, (hash_data_t *)&binding); - if (result != -1) return binding->user_binding->wrapper_pointer; + void *val = orig_dlsym(handle, symbol_name); + void **wrappee_ptr = getInternalBindingAddressPointer( + (struct internal_binding_t **)binding->user_binding->function_handle); + if (result != -1 && (val == NULL || *wrappee_ptr == val)) { + // if the wrapper is found and the wrappee is the function requested. + // This is needed in cases where we wrap a function F1 from library A and + // we dynamically load function F1 from library B. As name is same, we need + // to make sure the wrappee are the same as well + return binding->user_binding->wrapper_pointer; + } if (handle == RTLD_NEXT) { struct link_map *lib = gotchas_dlsym_rtld_next_lookup( symbol_name, __builtin_return_address(0)); @@ -168,7 +178,7 @@ static void *dlsym_wrapper(void *handle, const char *symbol_name) { } return NULL; } else { - return orig_dlsym(handle, symbol_name); + return val; } } diff --git a/src/gotcha_dl.h b/src/gotcha_dl.h index f968e92..252f5ae 100644 --- a/src/gotcha_dl.h +++ b/src/gotcha_dl.h @@ -9,6 +9,7 @@ extern void update_all_library_gots(hash_table_t *bindings); extern long lookup_exported_symbol(const char *name, const struct link_map *lib, void **symbol); extern int prepare_symbol(struct internal_binding_t *binding); +extern void **getInternalBindingAddressPointer(struct internal_binding_t **in); extern gotcha_wrappee_handle_t orig_dlopen_handle; extern gotcha_wrappee_handle_t orig_dlsym_handle; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 70ff615..80bb07f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(filter) add_subdirectory(wrap_main) #add_subdirectory(multi_agent_dlopen) add_subdirectory(symver) -add_subdirectory(function_ptr) \ No newline at end of file +add_subdirectory(function_ptr) +add_subdirectory(dispatcher) diff --git a/test/dispatcher/CMakeLists.txt b/test/dispatcher/CMakeLists.txt new file mode 100644 index 0000000..17fa6ae --- /dev/null +++ b/test/dispatcher/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(impl SHARED libimpl.c) +add_library(dispatcher SHARED libdispatcher.c) +target_link_libraries(dispatcher -ldl -lpthread) +add_executable(test_dispatcher main.c) +target_link_libraries(test_dispatcher gotcha dispatcher) +gotcha_add_test(dispatcher_test test_dispatcher) +set_property(TEST dispatcher_test APPEND PROPERTY ENVIRONMENT "GOTCHA_DEBUG=3") +set_property(TEST dispatcher_test APPEND PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}") diff --git a/test/dispatcher/libdispatcher.c b/test/dispatcher/libdispatcher.c new file mode 100644 index 0000000..9c16ddb --- /dev/null +++ b/test/dispatcher/libdispatcher.c @@ -0,0 +1,67 @@ +/* +This file is part of GOTCHA. For copyright information see the COPYRIGHT +file in the top level directory, or at +https://github.com/LLNL/gotcha/blob/master/COPYRIGHT +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License (as published by the Free +Software Foundation) version 2.1 dated February 1999. 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 terms and conditions of the GNU Lesser General Public License +for more details. You should have received a copy of the GNU Lesser 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 +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include + +int foo(void); +int bar(void); + +static void* impl_lib; +static int (*impl_foo)(void); +static int (*impl_bar)(void); + +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +void dispatch_init(void) { + fprintf(stderr, "Ed dispatch_init()\n"); + + impl_lib = dlopen("libimpl.so", RTLD_NOW); + assert(impl_lib); + impl_foo = dlsym(impl_lib, "foo"); + assert(impl_foo); + impl_bar = dlsym(impl_lib, "bar"); + assert(impl_bar); + + int ret = impl_bar(); + + fprintf(stderr, "Ld dispatch_init() = %d\n", ret); +} + +int foo(void) { + fprintf(stderr, "Ed foo()\n"); + + pthread_once(&init_once, dispatch_init); + + int ret = impl_bar() + impl_foo(); + + fprintf(stderr, "Ld foo()\n"); + + return ret; +} + +int bar(void) { + fprintf(stderr, "Ed bar()\n"); + + pthread_once(&init_once, dispatch_init); + + int ret = impl_bar(); + + fprintf(stderr, "Ld bar()\n"); + + return ret; +} diff --git a/test/dispatcher/libimpl.c b/test/dispatcher/libimpl.c new file mode 100644 index 0000000..5318df7 --- /dev/null +++ b/test/dispatcher/libimpl.c @@ -0,0 +1,31 @@ +/* +This file is part of GOTCHA. For copyright information see the COPYRIGHT +file in the top level directory, or at +https://github.com/LLNL/gotcha/blob/master/COPYRIGHT +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License (as published by the Free +Software Foundation) version 2.1 dated February 1999. 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 terms and conditions of the GNU Lesser General Public License +for more details. You should have received a copy of the GNU Lesser 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 +*/ + +#define _GNU_SOURCE +#include + +int foo(void) { + fprintf(stderr, "Ei foo()\n"); + fprintf(stderr, "Li foo()\n"); + + return 42; +} + +int bar(void) { + fprintf(stderr, "Ei bar()\n"); + fprintf(stderr, "Li bar()\n"); + + return 23; +} diff --git a/test/dispatcher/main.c b/test/dispatcher/main.c new file mode 100644 index 0000000..cc44ddb --- /dev/null +++ b/test/dispatcher/main.c @@ -0,0 +1,58 @@ +/* +This file is part of GOTCHA. For copyright information see the COPYRIGHT +file in the top level directory, or at +https://github.com/LLNL/gotcha/blob/master/COPYRIGHT +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License (as published by the Free +Software Foundation) version 2.1 dated February 1999. 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 terms and conditions of the GNU Lesser General Public License +for more details. You should have received a copy of the GNU Lesser 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 +*/ + +#define _GNU_SOURCE +#include +#include + +int foo(void); +int bar(void); + +static gotcha_wrappee_handle_t handle_foo; +static gotcha_wrappee_handle_t handle_bar; + +static int do_foo(void) { + fprintf(stderr, "Ew foo()\n"); + + typeof(&do_foo) orig_foo = gotcha_get_wrappee(handle_foo); + int ret = orig_foo(); + + fprintf(stderr, "Lw foo() = %d\n", ret); + + return ret; +} + +static int do_bar(void) { + fprintf(stderr, "Ew bar()\n"); + + typeof(&do_bar) orig_bar = gotcha_get_wrappee(handle_bar); + int ret = orig_bar(); + + fprintf(stderr, "Lw bar() = %d\n", ret); + + return ret; +} + +static struct gotcha_binding_t bindings[] = { + {"foo", do_foo, &handle_foo}, + {"bar", do_bar, &handle_bar}, +}; + +int main(int ac, char *av[]) { + gotcha_wrap(bindings, 2, "test"); + printf("%d\n", foo()); + + return 0; +} diff --git a/test/dlopen/num.c b/test/dlopen/num.c index b7a6be5..171619c 100644 --- a/test/dlopen/num.c +++ b/test/dlopen/num.c @@ -13,7 +13,6 @@ 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 */ -extern void mark_had_error(); extern int return_five(); int return_four() { diff --git a/test/dlopen/num2.c b/test/dlopen/num2.c index 7375182..4c5e567 100644 --- a/test/dlopen/num2.c +++ b/test/dlopen/num2.c @@ -13,9 +13,9 @@ 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 */ -extern void mark_had_error(); -extern int return_five(); +int return_four() { return 6; } -int return_four() { return 4; } - -int test_return_five() { return return_five(); } +int return_six() { + /* Intentional bug, gotcha wrapping will correct this to return 6 */ + return 7; +} diff --git a/test/dlopen/test_dlopen.c b/test/dlopen/test_dlopen.c index 88b8c35..e47a583 100644 --- a/test/dlopen/test_dlopen.c +++ b/test/dlopen/test_dlopen.c @@ -32,51 +32,52 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define LIB_NAME QUOTE(LIB_NAME_RAW) #define LIB2_NAME QUOTE(LIB2_NAME_RAW) int correct_return_four() { return 4; } +int correct_return_five() { return 5; } +int correct_return_six() { return 6; } int return_five() { /* Intentional bug, gotcha will correct this to return 5*/ return 3; } -int correct_return_five() { return 5; } - static gotcha_wrappee_handle_t buggy_return_four; static gotcha_wrappee_handle_t buggy_return_five; +static gotcha_wrappee_handle_t buggy_return_six; + struct gotcha_binding_t funcs[] = { {"return_four", correct_return_four, &buggy_return_four}, - {"return_five", correct_return_five, &buggy_return_five}}; + {"return_five", correct_return_five, &buggy_return_five}, + {"return_six", correct_return_six, &buggy_return_six}}; int main() { void *libnum; + void *libnum2; int (*retfour)(void); + int (*retsix)(void); int (*test_retfive)(void); int (*retdummy)(void); int had_error = 0; int result; - - result = gotcha_wrap(funcs, 2, "dlopen_test"); + /* We wrap the functions before they are loaded */ + result = gotcha_wrap(funcs, 3, "dlopen_test"); if (result != GOTCHA_FUNCTION_NOT_FOUND) { fprintf(stderr, "GOTCHA should have failed to find a function, but found it\n"); return -1; } - + /* Load the first libnum.so */ libnum = dlopen(LIB_NAME, RTLD_NOW); if (!libnum) { fprintf(stderr, "ERROR: Test failed to dlopen libnum.so\n"); return -1; } - libnum = dlopen(LIB2_NAME, RTLD_NOW); - if (!libnum) { - fprintf(stderr, "ERROR: Test failed to dlopen libnum2.so\n"); - return -1; - } - /* Test 1: Check if a dlsym generated indirect call gets re-routed by gotcha - */ + /* Check if return_four is wrapped from libnum.so */ retfour = (int (*)(void))dlsym(libnum, "return_four"); - if (retfour() != 4) { - fprintf(stderr, "ERROR: dlsym returned original function, not wrapped\n"); + if (retfour == NULL || retfour() != 4) { + fprintf(stderr, + "ERROR: dlsym returned original function, not wrapped from " + "libnum.so\n"); had_error = -1; } @@ -88,16 +89,25 @@ int main() { "by correct_return_five\n"); had_error = -1; } - /* Test 3: Does the dlsym implementation find the second occurrence of the - * symbol */ - test_retfive = (int (*)(void))dlsym(RTLD_NEXT, "test_return_five"); - if (test_retfive == NULL || test_retfive() != 5) { + + /* Load libnum2.so */ + libnum2 = dlopen(LIB2_NAME, RTLD_NOW); + if (!libnum) { + fprintf(stderr, "ERROR: Test failed to dlopen libnum2.so\n"); + return -1; + } + + /* Check if return_six is wrapped from libnum2.so */ + retsix = (int (*)(void))dlsym(libnum2, "return_six"); + if (retsix == NULL || retsix() != 6) { fprintf(stderr, - "ERROR: call to return_four should not be found in " - "RTLD_NEXT from libnum2.so and return 4\n"); + "ERROR: dlsym returned original function, not wrapped from " + "libnum2.so\n"); had_error = -1; } - /* Test 4: Does the dlsym implementation find the first occurrence of the + + /* Check RTLD_DEFAULT */ + /* Does the dlsym implementation find the first occurrence of the * symbol */ retfour = (int (*)(void))dlsym(RTLD_DEFAULT, "return_four"); if (retfour == NULL || retfour() != 4) { @@ -113,6 +123,15 @@ int main() { "by correct_return_five\n"); had_error = -1; } + + retsix = (int (*)(void))dlsym(RTLD_DEFAULT, "return_six"); + if (retsix == NULL || retsix() != 6) { + fprintf(stderr, + "ERROR: call to return_five in libnum2.so was not wrapped " + "by correct_return_five\n"); + had_error = -1; + } + retdummy = (int (*)(void))dlsym(RTLD_DEFAULT, "return_dummy"); if (retdummy != NULL) { fprintf( @@ -120,6 +139,17 @@ int main() { "ERROR: call to return_dummy should not be found in RTLD_DEFAULT\n"); had_error = -1; } + + /* Test RTLD_NEXT */ + /* Does the dlsym implementation find the second occurrence of the + * symbol */ + test_retfive = (int (*)(void))dlsym(RTLD_NEXT, "test_return_five"); + if (test_retfive == NULL || test_retfive() != 5) { + fprintf(stderr, + "ERROR: call to return_four should not be found in " + "RTLD_NEXT from libnum2.so and return 4\n"); + had_error = -1; + } retdummy = (int (*)(void))dlsym(RTLD_NEXT, "return_dummy"); if (retdummy != NULL) { fprintf(stderr, diff --git a/test/unit/gotcha_unit_tests.c b/test/unit/gotcha_unit_tests.c index 3a0c36e..e963955 100644 --- a/test/unit/gotcha_unit_tests.c +++ b/test/unit/gotcha_unit_tests.c @@ -558,6 +558,22 @@ Suite *gotcha_hash_suite() { return s; } +START_TEST(gotcha_version_check) { + ck_assert_msg(GOTCHA_GET_VERSION(1, 0, 3) > GOTCHA_GET_VERSION(1, 0, 2), + "Check GOTCHA_GET_VERSION failed"); + ck_assert_msg(GOTCHA_VERSION >= GOTCHA_GET_VERSION(1, 0, 6), + "Check GOTCHA_VERSION failed"); +} +END_TEST + +Suite *gotcha_version_suite() { + Suite *s = suite_create("Gotcha Version"); + TCase *version_case = configured_case_create("Basic tests"); + tcase_add_test(version_case, gotcha_version_check); + suite_add_tcase(s, version_case); + return s; +} + ////////////Launch///Tests//////////// int main() { @@ -578,9 +594,14 @@ int main() { SRunner *hash_runner = srunner_create(hash_suite); srunner_run_all(hash_runner, CK_NORMAL); num_fails += srunner_ntests_failed(hash_runner); + Suite *version_suite = gotcha_version_suite(); + SRunner *version_runner = srunner_create(version_suite); + srunner_run_all(version_runner, CK_NORMAL); + num_fails += srunner_ntests_failed(version_runner); srunner_free(core_runner); srunner_free(libc_runner); srunner_free(auxv_runner); srunner_free(hash_runner); + srunner_free(version_runner); return num_fails; }