Add live tests for OS dynamic loader behavior
authorCharles Giessen <charles@lunarg.com>
Fri, 4 Mar 2022 17:21:10 +0000 (10:21 -0700)
committerCharles Giessen <46324611+charles-lunarg@users.noreply.github.com>
Fri, 4 Mar 2022 22:40:01 +0000 (15:40 -0700)
Executes various combinations of loading and linking to libraries which
export the same symbols.

tests/live_verification/CMakeLists.txt
tests/live_verification/dynamic_loader_behavior/CMakeLists.txt [new file with mode: 0644]
tests/live_verification/dynamic_loader_behavior/dynamic_library.cpp [new file with mode: 0644]
tests/live_verification/dynamic_loader_behavior/dynamic_library.h [new file with mode: 0644]
tests/live_verification/dynamic_loader_behavior/dynamic_loading_behavior.md [new file with mode: 0644]
tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp [new file with mode: 0644]
tests/live_verification/dynamic_loader_behavior/test_dynamic_loading.cpp [new file with mode: 0644]
tests/live_verification/dynamic_loader_behavior/test_dynamic_loading_and_linking.cpp [new file with mode: 0644]

index d58f748..e23643c 100644 (file)
@@ -16,4 +16,6 @@
 # ~~~
 
 add_executable(dynamic_rendering_get_proc_addr dynamic_rendering_get_proc_addr.cpp)
-target_link_libraries(dynamic_rendering_get_proc_addr testing_dependencies)
\ No newline at end of file
+target_link_libraries(dynamic_rendering_get_proc_addr testing_dependencies)
+
+add_subdirectory(dynamic_loader_behavior)
\ No newline at end of file
diff --git a/tests/live_verification/dynamic_loader_behavior/CMakeLists.txt b/tests/live_verification/dynamic_loader_behavior/CMakeLists.txt
new file mode 100644 (file)
index 0000000..29c773a
--- /dev/null
@@ -0,0 +1,48 @@
+# ~~~
+# Copyright (c) 2022 Valve Corporation
+# Copyright (c) 2022 LunarG, 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.
+# ~~~
+
+if (WIN32)
+    #tests use dlsym and linux specific library names
+else()
+add_library(dynamic_library_a dynamic_library.cpp)
+target_link_libraries(dynamic_library_a PUBLIC testing_framework_util)
+target_compile_definitions(dynamic_library_a PRIVATE PRINT_OUTPUT_A)
+
+add_library(dynamic_library_b dynamic_library.cpp)
+target_link_libraries(dynamic_library_b PUBLIC testing_framework_util)
+target_compile_definitions(dynamic_library_b PRIVATE PRINT_OUTPUT_B)
+
+add_library(dynamic_library_c dynamic_library.cpp)
+target_link_libraries(dynamic_library_c PUBLIC testing_framework_util)
+target_compile_definitions(dynamic_library_c PRIVATE PRINT_OUTPUT_C)
+
+add_executable(test_dynamic_linking_a_first test_dynamic_linking.cpp)
+add_executable(test_dynamic_linking_b_first test_dynamic_linking.cpp)
+add_executable(test_dynamic_linking_c_then_load test_dynamic_linking.cpp)
+target_link_libraries(test_dynamic_linking_a_first PUBLIC dynamic_library_a dynamic_library_b)
+target_link_libraries(test_dynamic_linking_b_first PUBLIC dynamic_library_b dynamic_library_a)
+target_link_libraries(test_dynamic_linking_c_then_load PUBLIC dynamic_library_c)
+target_compile_definitions(test_dynamic_linking_c_then_load PRIVATE PRINT_OUTPUT_C)
+
+
+add_executable(test_dynamic_loading test_dynamic_loading.cpp)
+target_link_libraries(test_dynamic_loading PUBLIC testing_framework_util)
+
+add_executable(test_dynamic_loading_and_linking test_dynamic_loading_and_linking.cpp)
+target_link_libraries(test_dynamic_loading_and_linking PUBLIC testing_framework_util)
+target_link_libraries(test_dynamic_loading_and_linking PUBLIC dynamic_library_a)
+endif()
\ No newline at end of file
diff --git a/tests/live_verification/dynamic_loader_behavior/dynamic_library.cpp b/tests/live_verification/dynamic_loader_behavior/dynamic_library.cpp
new file mode 100644 (file)
index 0000000..3126cad
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 The Khronos Group Inc.
+ * Copyright (c) 2022 Valve Corporation
+ * Copyright (c) 2022 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ */
+
+#include "dynamic_library.h"
+
+FRAMEWORK_EXPORT char do_logic() {
+#if defined(PRINT_OUTPUT_A)
+    return 'A';
+#elif defined(PRINT_OUTPUT_B)
+    return 'B';
+#elif defined(PRINT_OUTPUT_C)
+    return 'C';
+#endif
+}
+
+FRAMEWORK_EXPORT void init() {
+#if defined(PRINT_OUTPUT_A)
+    static LibraryWrapper b{};
+    static LibraryWrapper a{};
+    a = LibraryWrapper{"./libdynamic_library_a.dylib"};
+    b = LibraryWrapper{"./libdynamic_library_b.dylib"};
+#endif
+}
\ No newline at end of file
diff --git a/tests/live_verification/dynamic_loader_behavior/dynamic_library.h b/tests/live_verification/dynamic_loader_behavior/dynamic_library.h
new file mode 100644 (file)
index 0000000..8b6e29a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022 The Khronos Group Inc.
+ * Copyright (c) 2022 Valve Corporation
+ * Copyright (c) 2022 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ */
+
+#include "test_util.h"
+
+extern "C" {
+
+using DoLogicFunction = char (*)();
+#define DO_LOGIC_FUNCTION_NAME "do_logic"
+FRAMEWORK_EXPORT char do_logic();
+
+using InitFunction = void (*)();
+#define INIT_FUNCTION_NAME "init"
+FRAMEWORK_EXPORT void init();
+};
+
+#if defined(WIN32)
+#ifndef LIB_EXT
+#define LIB_EXT "dll"
+#endif
+#elif defined(__APPLE__)
+#ifndef LIB_EXT
+#define LIB_EXT "dylib"
+#endif
+#else
+#ifndef LIB_EXT
+#define LIB_EXT "so"
+#endif
+#endif
\ No newline at end of file
diff --git a/tests/live_verification/dynamic_loader_behavior/dynamic_loading_behavior.md b/tests/live_verification/dynamic_loader_behavior/dynamic_loading_behavior.md
new file mode 100644 (file)
index 0000000..3f7f508
--- /dev/null
@@ -0,0 +1,31 @@
+# General behavior of dynamic linkers on linux and macOS
+
+## Linking only
+
+Symbols are found based on link order. If two libraries export the same symbol,
+the first library in the link list is the library which is called.
+
+## Loading
+
+Applications cannot load symbols without specifying a library.
+Putting RTLD_NEXT in dlsym will not find any symbols even if a loaded library
+should contain them.
+An app _must_ reference a library that was loaded with `dlopen` to be able to
+get at its exported symbols through `dlsym`.
+If a loaded library loads subsequent libraries, and they all export the same
+symbols, the library which the application explicitly loaded is the one whose
+symbols are used.
+
+## Linking and Loading
+
+Applications can now use RTLD_NEXT to query symbols.
+They will always be the first linked library is that exported the symbol.
+In other words, loading a library explicitly doesn't change the behavior.
+
+Similarly, explicitly loaded libraries behave the same.
+Symbols from loaded libraries must be referenced to load the symbol, RTLD_NEXT
+doesn't work.
+If a loaded library subsequently loads another library which exports the same
+symbols, then the library the application explicitly loaded is used.
+If one of the subsequently loaded libraries happened to be linked, this doesn't
+affect the behavior when querying functions from the application loaded library.
\ No newline at end of file
diff --git a/tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp b/tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp
new file mode 100644 (file)
index 0000000..2d2d6b3
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2022 The Khronos Group Inc.
+ * Copyright (c) 2022 Valve Corporation
+ * Copyright (c) 2022 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ */
+
+#include "dynamic_library.h"
+
+int main() {
+    std::cout << do_logic() << "\n";
+#if defined(PRINT_OUTPUT_C)
+    LibraryWrapper dynamic_library_c{std::string("./libdynamic_library_c.") + LIB_EXT};
+    InitFunction init = dynamic_library_c.get_symbol(INIT_FUNCTION_NAME);
+    if (init == nullptr) return 1;
+    init();
+    DoLogicFunction do_logic = dynamic_library_c.get_symbol(DO_LOGIC_FUNCTION_NAME);
+    if (do_logic == nullptr || do_logic() != 'C') return 2;
+
+    do_logic = reinterpret_cast<DoLogicFunction>(dlsym(RTLD_NEXT, DO_LOGIC_FUNCTION_NAME));
+    if (do_logic == nullptr || do_logic() != 'A') return 3;
+    std::cout << "Success\n";
+#endif
+    return 0;
+}
\ No newline at end of file
diff --git a/tests/live_verification/dynamic_loader_behavior/test_dynamic_loading.cpp b/tests/live_verification/dynamic_loader_behavior/test_dynamic_loading.cpp
new file mode 100644 (file)
index 0000000..118d005
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022 The Khronos Group Inc.
+ * Copyright (c) 2022 Valve Corporation
+ * Copyright (c) 2022 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ */
+
+#include "dynamic_library.h"
+
+int main() {
+    {
+        LibraryWrapper dynamic_library_a{std::string("./libdynamic_library_a.") + LIB_EXT};
+        LibraryWrapper dynamic_library_b{std::string("./libdynamic_library_b.") + LIB_EXT};
+
+        DoLogicFunction do_logic = nullptr;
+        do_logic = dynamic_library_a.get_symbol(DO_LOGIC_FUNCTION_NAME);
+        if (do_logic == nullptr || do_logic() != 'A') return 1;
+        do_logic = dynamic_library_b.get_symbol(DO_LOGIC_FUNCTION_NAME);
+        if (do_logic == nullptr || do_logic() != 'B') return 2;
+    }
+    {
+        LibraryWrapper dynamic_library_c{std::string("./libdynamic_library_c.") + LIB_EXT};
+        InitFunction init = dynamic_library_c.get_symbol(INIT_FUNCTION_NAME);
+        if (init == nullptr) return 3;
+        init();
+        DoLogicFunction do_logic = dynamic_library_c.get_symbol(DO_LOGIC_FUNCTION_NAME);
+        if (do_logic == nullptr || do_logic() != 'C') return 4;
+        // should fail because RTLD_NEXT on linux only is for dynamically *linked* libraries
+        do_logic = reinterpret_cast<DoLogicFunction>(dlsym(RTLD_NEXT, DO_LOGIC_FUNCTION_NAME));
+        if (do_logic != nullptr) return 5;
+    }
+    std::cout << "Success\n";
+    return 0;
+}
diff --git a/tests/live_verification/dynamic_loader_behavior/test_dynamic_loading_and_linking.cpp b/tests/live_verification/dynamic_loader_behavior/test_dynamic_loading_and_linking.cpp
new file mode 100644 (file)
index 0000000..97b7bd8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022 The Khronos Group Inc.
+ * Copyright (c) 2022 Valve Corporation
+ * Copyright (c) 2022 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ */
+
+#include "dynamic_library.h"
+
+int main() {
+    DoLogicFunction do_logic = nullptr;
+    do_logic = reinterpret_cast<DoLogicFunction>(dlsym(RTLD_NEXT, DO_LOGIC_FUNCTION_NAME));
+    if (do_logic == nullptr || do_logic() != 'A') return 1;
+
+    LibraryWrapper dynamic_library_c{std::string("./libdynamic_library_c.") + LIB_EXT};
+    InitFunction init = dynamic_library_c.get_symbol(INIT_FUNCTION_NAME);
+    if (init == nullptr) return 2;
+    init();
+
+    do_logic = dynamic_library_c.get_symbol(DO_LOGIC_FUNCTION_NAME);
+    if (do_logic == nullptr || do_logic() != 'C') return 3;
+
+    do_logic = reinterpret_cast<DoLogicFunction>(dlsym(RTLD_NEXT, DO_LOGIC_FUNCTION_NAME));
+    if (do_logic == nullptr || do_logic() != 'A') return 4;
+
+    std::cout << "Success\n";
+    return 0;
+}
\ No newline at end of file