Skip to content

Commit 051fd44

Browse files
Extend test dynamic link (#1724)
1 parent d304712 commit 051fd44

File tree

5 files changed

+197
-24
lines changed

5 files changed

+197
-24
lines changed

include/oneapi/tbb/detail/_assert.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2005-2022 Intel Corporation
2+
Copyright (c) 2005-2025 Intel Corporation
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -30,7 +30,8 @@ namespace r1 {
3030
/** Normally called from __TBB_ASSERT macro.
3131
If assertion handler is null, print message for assertion failure and abort.
3232
Otherwise call the assertion handler. */
33-
TBB_EXPORT void __TBB_EXPORTED_FUNC assertion_failure(const char* location, int line, const char* expression, const char* comment);
33+
TBB_EXPORT void __TBB_EXPORTED_FUNC assertion_failure(const char *location, int line,
34+
const char *expression, const char *comment);
3435
#if __TBBMALLOC_BUILD
3536
}} // namespaces rml::internal
3637
#else

src/tbb/dynamic_link.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,17 @@ namespace r1 {
136136
const char* str = nullptr;
137137
// Note: dlerr_t depends on OS: it is char const * on Linux* and macOS*, int on Windows*.
138138
#if _WIN32
139-
#define DLERROR_SPECIFIER "%d"
139+
#if __INTEL_LLVM_COMPILER
140+
// Suppress the incorrect warning about the format specifier for the unsigned long long type.
141+
#pragma GCC diagnostic push
142+
#pragma GCC diagnostic ignored "-Wformat"
143+
#endif
144+
#define DLERROR_SPECIFIER "%ul"
140145
typedef DWORD dlerr_t;
141146
#else
142147
#define DLERROR_SPECIFIER "%s"
143148
typedef const char* dlerr_t;
144-
#endif
149+
#endif // _WIN32
145150
dlerr_t error = 0;
146151

147152
std::va_list args;
@@ -155,7 +160,10 @@ namespace r1 {
155160
DLERROR_SPECIFIER "\n", prefix, str, error);
156161
break;
157162
case dl_sym_not_found: // char const * sym, dlerr_t err:
158-
// TODO: Print not found symbol once it is used by the implementation
163+
str = va_arg(args, const char*);
164+
error = va_arg(args, dlerr_t);
165+
TBB_FPRINTF(stderr, "%s The symbol \"%s\" was not resolved. System error: "
166+
DLERROR_SPECIFIER "\n", prefix, str, error);
159167
break;
160168
case dl_sys_fail:
161169
str = va_arg(args, const char*);
@@ -216,6 +224,9 @@ namespace r1 {
216224
va_end(args);
217225
} // library_warning
218226
#undef DLERROR_SPECIFIER
227+
#if _WIN32 && __INTEL_LLVM_COMPILER
228+
#pragma GCC diagnostic pop
229+
#endif
219230
#else
220231
static void dynamic_link_warning( int code, ... ) {
221232
suppress_unused_warning(code);
@@ -224,6 +235,8 @@ namespace r1 {
224235

225236
#elif defined(DYNAMIC_LINK_WARNING)
226237
#define __DYNAMIC_LINK_REPORT_SIGNATURE_ERRORS 1
238+
#else
239+
#define DYNAMIC_LINK_WARNING(...) (void)0
227240
#endif /* !defined(DYNAMIC_LINK_WARNING) && !__TBB_WIN8UI_SUPPORT && __TBB_DYNAMIC_LOAD_ENABLED */
228241

229242
static bool resolve_symbols( dynamic_link_handle module, const dynamic_link_descriptor descriptors[], std::size_t required )
@@ -244,6 +257,7 @@ namespace r1 {
244257
dynamic_link_descriptor const & desc = descriptors[k];
245258
pointer_to_handler addr = (pointer_to_handler)dlsym( module, desc.name );
246259
if ( !addr ) {
260+
DYNAMIC_LINK_WARNING( dl_sym_not_found, desc.name, dlerror() );
247261
return false;
248262
}
249263
h[k] = addr;
@@ -426,7 +440,7 @@ namespace r1 {
426440
otherwise -- Ok, number of characters (incl. terminating null) written to buffer.
427441
*/
428442
static std::size_t abs_path( char const * name, char * path, std::size_t len ) {
429-
if ( ap_data._len == 0 )
443+
if ( !name || ap_data._len == 0 )
430444
return 0;
431445

432446
std::size_t name_len = std::strlen( name );

test/CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,18 @@ if (TARGET TBB::tbb)
420420
if (NOT TBB_TCM_TESTING)
421421
tbb_add_test(SUBDIR tbb NAME test_arena_priorities DEPENDENCIES TBB::tbb)
422422
endif()
423-
tbb_add_test(SUBDIR tbb NAME test_dynamic_link DEPENDENCIES TBB::tbb)
423+
424+
tbb_add_test(SUBDIR tbb NAME test_dynamic_link)
425+
add_dependencies(test_dynamic_link TBB::tbb)
426+
target_include_directories(test_dynamic_link PRIVATE
427+
$<TARGET_PROPERTY:TBB::tbb,INTERFACE_INCLUDE_DIRECTORIES>)
428+
set_property(TARGET test_dynamic_link PROPERTY BUILD_RPATH_USE_ORIGIN TRUE)
429+
set_property(TARGET test_dynamic_link PROPERTY BUILD_RPATH "$ORIGIN")
430+
target_compile_definitions(test_dynamic_link PRIVATE "TBBLIB_NAME=\"$<TARGET_FILE_NAME:TBB::tbb>\"")
431+
# Making unsigned library which the test fails loading
432+
add_library(stub_unsigned SHARED ${CMAKE_CURRENT_SOURCE_DIR}/tbb/stub_unsigned_lib.cpp)
433+
add_dependencies(test_dynamic_link stub_unsigned)
434+
424435
if (LINKER_HAS_NO_AS_NEEDED)
425436
# The linker may not detect a dependency on pthread in static variable constructors.
426437
target_link_libraries(test_dynamic_link PRIVATE "-Wl,--no-as-needed")

test/tbb/stub_unsigned_lib.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
Copyright (c) 2025 Intel Corporation
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
//! \file stub_unsigned_lib.cpp
18+
//! \brief Test for [internal] functionality
19+
20+
#if _WIN32 || _WIN64
21+
// Make sure the symbol is visible
22+
#define STUB_EXPORT __declspec(dllexport)
23+
#else
24+
// On other platforms check the branch when symbol cannot be resolved by hiding it
25+
#define STUB_EXPORT __attribute__ ((visibility ("hidden")))
26+
#endif
27+
28+
#include <cstdlib>
29+
30+
extern "C" {
31+
32+
static int dummy = []() {
33+
#if _WIN32
34+
// Make sure the library is not loaded on Windows in test dynamic link
35+
std::abort();
36+
#endif
37+
return 1;
38+
}();
39+
40+
STUB_EXPORT void foo() {
41+
std::abort(); // Test dynamic link should never call this function
42+
}
43+
}

test/tbb/test_dynamic_link.cpp

Lines changed: 121 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2005-2024 Intel Corporation
2+
Copyright (c) 2005-2025 Intel Corporation
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -21,8 +21,13 @@
2121
#define _CRT_SECURE_NO_WARNINGS
2222
#endif
2323

24+
#define __TBB_NO_IMPLICIT_LINKAGE 1
25+
2426
#include "common/test.h"
2527

28+
// Out-of-line TBB assertion handling routines are instantiated here
29+
#include "../../src/tbb/assert_impl.h"
30+
2631
enum FOO_TYPE {
2732
FOO_DUMMY,
2833
FOO_IMPLEMENTATION
@@ -42,31 +47,43 @@ FOO_TYPE dummy_foo1() { return FOO_DUMMY; }
4247
FOO_TYPE dummy_foo2() { return FOO_DUMMY; }
4348

4449
#include "oneapi/tbb/detail/_config.h"
50+
#include "oneapi/tbb/version.h"
51+
4552
// Suppress the weak symbol mechanism to avoid surplus compiler warnings.
4653
#ifdef __TBB_WEAK_SYMBOLS_PRESENT
4754
#undef __TBB_WEAK_SYMBOLS_PRESENT
4855
#endif
49-
#include "src/tbb/dynamic_link.h"
5056

51-
#if __TBB_DYNAMIC_LOAD_ENABLED
52-
// Handlers.
53-
static FOO_TYPE (*foo1_handler)() = &dummy_foo1;
54-
static FOO_TYPE (*foo2_handler)() = &dummy_foo2;
55-
56-
// Table describing how to link the handlers.
57-
static const tbb::detail::r1::dynamic_link_descriptor LinkTable[] = {
58-
{ "foo1", (tbb::detail::r1::pointer_to_handler*)(void*)(&foo1_handler) },
59-
{ "foo2", (tbb::detail::r1::pointer_to_handler*)(void*)(&foo2_handler) }
60-
};
61-
#endif
6257

63-
// The direct include since we want to test internal functionality.
58+
#ifndef TBB_DYNAMIC_LINK_WARNING
59+
#define TBB_DYNAMIC_LINK_WARNING 1
60+
#endif
61+
#ifdef __TBB_SKIP_DEPENDENCY_SIGNATURE_VERIFICATION
62+
#warning "Signature verification must not be turned off to fully test dynamic link functionality"
63+
#endif
64+
// The direct include since we want to test internal functionality
6465
#include "src/tbb/dynamic_link.cpp"
66+
67+
6568
#include "common/utils.h"
6669
#include "common/utils_dynamic_libs.h"
6770

71+
#include <cstdio> // for std::snprintf
72+
#include <cstring> // for std::strcmp
73+
6874
void test_dynamic_link(const char* lib_name) {
6975
#if __TBB_DYNAMIC_LOAD_ENABLED
76+
// Handlers.
77+
static FOO_TYPE (*foo1_handler)() = nullptr;
78+
static FOO_TYPE (*foo2_handler)() = nullptr;
79+
foo1_handler = &dummy_foo1;
80+
foo2_handler = &dummy_foo2;
81+
82+
// Table describing how to link the handlers.
83+
static const tbb::detail::r1::dynamic_link_descriptor LinkTable[] = {
84+
{ "foo1", (tbb::detail::r1::pointer_to_handler*)(void*)(&foo1_handler) },
85+
{ "foo2", (tbb::detail::r1::pointer_to_handler*)(void*)(&foo2_handler) }
86+
};
7087
#if !_WIN32
7188
// Check if the executable exports its symbols.
7289
REQUIRE_MESSAGE((utils::GetAddress(utils::OpenLibrary(nullptr), "foo1") && utils::GetAddress(utils::OpenLibrary(nullptr), "foo2")),
@@ -80,9 +97,10 @@ void test_dynamic_link(const char* lib_name) {
8097
if (tbb::detail::r1::dynamic_link(lib_name, LinkTable, sizeof(LinkTable) / sizeof(LinkTable[0]))) {
8198
REQUIRE_MESSAGE((foo1_handler && foo2_handler), "The symbols are corrupted by dynamic_link");
8299
REQUIRE_MESSAGE((foo1_handler() == FOO_IMPLEMENTATION && foo2_handler() == FOO_IMPLEMENTATION),
83-
"dynamic_link returned the successful code but symbol(s) are wrong");
100+
"dynamic_link returned the successful code but symbol(s) are wrong");
84101
} else {
85-
REQUIRE_MESSAGE((foo1_handler == dummy_foo1 && foo2_handler == dummy_foo2), "The symbols are corrupted by dynamic_link");
102+
REQUIRE_MESSAGE((foo1_handler == &dummy_foo1 && foo2_handler == &dummy_foo2),
103+
"The symbols are corrupted by dynamic_link");
86104
}
87105
#else
88106
utils::suppress_unused_warning(lib_name);
@@ -97,6 +115,92 @@ TEST_CASE("Test dynamic_link with non-existing library") {
97115

98116
//! Testing dynamic_link
99117
//! \brief \ref error_guessing
100-
TEST_CASE("Test dynamic_link") {
118+
TEST_CASE("Test dynamic_link corner cases") {
119+
test_dynamic_link(nullptr);
101120
test_dynamic_link("");
102121
}
122+
123+
124+
#if __TBB_DYNAMIC_LOAD_ENABLED
125+
126+
#if !__TBB_WIN8UI_SUPPORT
127+
//! Testing dynamic_link with existing library
128+
//! \brief \ref requirement
129+
TEST_CASE("Test dynamic_link with existing library") {
130+
// Check that not-yet-linked library is found and loaded even without specifying absolute path to
131+
// it. On Windows, signature validation is performed in addition.
132+
#if _WIN32
133+
// Well known signed Windows library that exist in every distribution
134+
const char* lib_name = "user32.dll";
135+
const char* symbol = "MessageBoxA";
136+
int (*handler)(void * /*hWnd*/, const char * /*lpText*/, const char * /*lpCaption*/,
137+
unsigned int /*uType*/) = nullptr;
138+
#else
139+
const char* lib_name = TBBLIB_NAME; // On Linux it is the name of the library itself
140+
const char* symbol = "TBB_runtime_version";
141+
static const char* (*handler)() = nullptr;
142+
#endif
143+
static const tbb::detail::r1::dynamic_link_descriptor table[] = {
144+
{ symbol, (tbb::detail::r1::pointer_to_handler*)(void*)(&handler) },
145+
};
146+
147+
constexpr int load_flags =
148+
tbb::detail::r1::DYNAMIC_LINK_DEFAULT & ~tbb::detail::r1::DYNAMIC_LINK_BUILD_ABSOLUTE_PATH;
149+
const bool link_result = tbb::detail::r1::dynamic_link(lib_name, table,
150+
sizeof(table) / sizeof(table[0]),
151+
/*handle*/nullptr, load_flags);
152+
char msg[128] = {0};
153+
std::snprintf(msg, sizeof(msg) / sizeof(msg[0]), "The library \"%s\" was not loaded", lib_name);
154+
REQUIRE_MESSAGE(link_result, msg);
155+
REQUIRE_MESSAGE(handler, "The symbol was not found.");
156+
#if !_WIN32
157+
REQUIRE_MESSAGE(0 == std::strcmp(handler(), TBB_VERSION_STRING),
158+
"dynamic_link returned successful code but symbol returned incorrect result");
159+
#endif
160+
}
161+
#endif // !__TBB_WIN8UI_SUPPORT
162+
163+
//! Testing dynamic_link with stub library known to be unsigned (on Windows) and having no exported
164+
//! symbols (on Linux)
165+
// \brief \ref requirement
166+
TEST_CASE("Test dynamic_link with bad library") {
167+
const int size = PATH_MAX + 1;
168+
const char* lib_name = TEST_LIBRARY_NAME("stub_unsigned");
169+
char path[size] = {0};
170+
const int msg_size = size + 128; // Path to the file + message
171+
char msg[msg_size] = {0};
172+
173+
#if !__TBB_WIN8UI_SUPPORT
174+
const std::size_t len = tbb::detail::r1::abs_path(lib_name, path, sizeof(path));
175+
REQUIRE_MESSAGE((0 < len && len <= PATH_MAX), "The path to the library is not built");
176+
std::snprintf(msg, msg_size, "Test prerequisite is not held - the path \"%s\" must exist", path);
177+
REQUIRE_MESSAGE(tbb::detail::r1::file_exists(path), msg);
178+
#endif
179+
180+
// The library exists, check that it will not be loaded.
181+
void (*handler)() = nullptr;
182+
static const tbb::detail::r1::dynamic_link_descriptor table[] = {
183+
{ "foo", (tbb::detail::r1::pointer_to_handler*)(void*)(&handler) },
184+
};
185+
186+
// Specify library name without absolute path to test that it still can be found because it
187+
// resides in the application (i.e. test) directory
188+
constexpr int load_flags =
189+
tbb::detail::r1::DYNAMIC_LINK_DEFAULT & ~tbb::detail::r1::DYNAMIC_LINK_BUILD_ABSOLUTE_PATH;
190+
const bool link_result = tbb::detail::r1::dynamic_link(lib_name, table,
191+
sizeof(table) / sizeof(table[0]),
192+
/*handle*/nullptr, load_flags);
193+
std::snprintf(msg, msg_size, "The library \"%s\" was loaded but should not have been.", path);
194+
195+
// Expectation is that the library will not be loaded because:
196+
// a) On Windows the library is unsigned
197+
// b) On Linux the library does not have exported symbols
198+
const bool expected_link_result = false;
199+
200+
REQUIRE_MESSAGE(expected_link_result == link_result, msg);
201+
REQUIRE_MESSAGE(nullptr == handler, "The symbol should not be changed.");
202+
// TODO: Verify the warning message contains "TBB dynamic link warning: The module
203+
// \".*stub_unsigned.*.dll\" is unsigned or has invalid signature."
204+
}
205+
206+
#endif // __TBB_DYNAMIC_LOAD_ENABLED

0 commit comments

Comments
 (0)