| Issue |
173997
|
| Summary |
[clang 21.1.8] [windows] [modules] undefined symbol when exporting variable from shared library
|
| Labels |
clang
|
| Assignees |
|
| Reporter |
Silverlan
|
Clang version: 21.1.8 (Downloaded from this repository)
Full source code for the minimal reproduction code, including GitHub workflows:
https://github.com/Silverlan/tmp_clang_errors/tree/undefined-symbol
Full build log: https://github.com/Silverlan/tmp_clang_errors/actions/runs/20599656929/job/59161977160
## Description
This bug happens when trying to access a variable exported from a c++20 module from a dynamic library when using clang (I have not tested clang-cl) on Windows. On Linux it compiles and links without any issues.
I've narrowed the problem down as much as I can. The reproduction project consists of a main application ("test_program"), as well as a dynamically linked library ("test_library"), which exports a c++20 module ("test_module"). Here is a short description of the code:
### Code
`test_library`
This is a shared library, which exports a c++20 module with some simple objects.
```cpp
export module test_module;
#ifdef _WIN32
#define API __declspec(dllexport)
#else
#define API __attribute__((visibility("default")))
#endif
export {
struct API TestVector {
float x;
float y;
float z;
};
}
export namespace test {
// These are causing "undefined symbol" errors in certain circumstances (see test cases in main program)
constexpr TestVector cexpr_test_vector = TestVector{1.f,2.f,3.f};
API TestVector dllexp_test_vector = TestVector{1.f,2.f,3.f};
//
API TestVector dllexp_get_test_vector() {return TestVector{1.f,2.f,3.f};}
API void test_function(const TestVector &v)
{
}
}
```
`test_program`
The program links against `test_library` and contains several test cases which can be switched between when configuring the project with CMake (using the `-DTEST_CASE=X` option). Some cases trigger the linking error, others do not (see comments below for each test case).
```cpp
#include <iostream>
import test_module;
// Test Case 0: Access member of constexpr vector from module test_module
// Result: Build successful
#if TEST_CASE == 0
int main() {
std::cout<<test::cexpr_test_vector.x<<std::endl;
return 0;
}
#endif
// Test Case 1: Call test_function from module test_module with constexpr vector
// Result: lld-link: error: undefined symbol: struct TestVector const test::cexpr_test_vector
#if TEST_CASE == 1
int main() {
test::test_function(test::cexpr_test_vector);
return 0;
}
#endif
// Test Case 2: Same as Test Case 1, but with locally created vector
// Result: Build successful
#if TEST_CASE == 2
int main() {
test::test_function(TestVector {1.f, 2.f, 3.4});
return 0;
}
#endif
// Test Case 3: Calling a locally defined function with constexpr vector as argument
// Result: Build successful
#if TEST_CASE == 3
static void print_vec(const TestVector v)
{
std::cout<<v.x<<","<<v.y<<","<<v.z<<std::endl;
}
int main() {
print_vec(test::cexpr_test_vector);
return 0;
}
#endif
// Test Case 4: Same as Test Case 3, but with optimization turned off
// Result: lld-link: error: undefined symbol: struct TestVector const test::cexpr_test_vector
#if TEST_CASE == 4
#pragma clang optimize off
static void print_vec(const TestVector v)
{
std::cout<<v.x<<","<<v.y<<","<<v.z<<std::endl;
}
int main() {
print_vec(test::cexpr_test_vector);
return 0;
}
#pragma clang optimize on
#endif
// Test Case 5: Same as Test Case 0, but using dllexported vector instead of constexpr
// Result: lld-link: error: undefined symbol: struct TestVector test::dllexp_test_vector
#if TEST_CASE == 5
int main() {
std::cout<<test::dllexp_test_vector.x<<std::endl;
return 0;
}
#endif
// Test Case 6: Same as Test Case 0, but using dllexported getter-function for vector instead of constexpr
// Result: Build successful
#if TEST_CASE == 6
int main() {
std::cout<<test::dllexp_get_test_vector().x<<std::endl;
return 0;
}
#endif
```
All test cases build and link successfully with [clang on Linux](https://github.com/Silverlan/tmp_clang_errors/actions/runs/20599656928/job/59161977155).
Interestingly, when building with [MSVC on Windows](https://github.com/Silverlan/tmp_clang_errors/actions/runs/20599656933/job/59161977122), Case 0 and 1 also fail with a similar error as the one clang reports:
```
main.cpp.obj : error LNK2019: unresolved external symbol "struct TestVector const test::cexpr_test_vector" (?cexpr_test_vector@test@@3UTestVector@@B::<!test_module>) referenced in function main
```
## Build instructions:
Run from visual studio 2022 command prompt (replace clang executable paths `CMAKE_C_COMPILER` and `CMAKE_CXX_COMPILER`):
```bash
git clone --single-branch --branch "undefined-symbol" https://github.com/Silverlan/tmp_clang_errors.git
cd tmp_clang_errors
set root=%cd%
cd test_program
mkdir build
cd build
cmake -S .. -B . -G "Ninja" -DCMAKE_C_COMPILER="%root%/clang/bin/clang.exe" -DCMAKE_CXX_COMPILER="%root%/clang/bin/clang++.exe" -DCMAKE_INSTALL_PREFIX:PATH="%root%/install" -DCMAKE_CXX_SCAN_FOR_MODULES=1 -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DTEST_CASE=0
cmake --build .
cmake --install .
%root%/install/test_program.exe
```
(Alternatively fork the test project repository and run the [GitHub workflow](https://github.com/Silverlan/tmp_clang_errors/blob/undefined-symbol/.github/workflows/windows-clang.yml).)
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs