C++ 静态库与共享库中的多个符号定义

问题描述 投票:0回答:1

这里是一个简单的CMake项目:

cmake_minimum_required(VERSION 3.0)

project(Test)

add_library(Test STATIC test1.cpp test2.cpp)

test1
如下:

// test1.h
#pragma once

void test();

// test1.cpp
#include "test1.h"

void test() 
{

}

test2

// test2.h
#pragma once

void test();

// test2.cpp
#include "test2.h"

void test() 
{

}

这个项目编译得很好。但是,如果我将库更改为动态的:

add_library(Test SHARED test1.cpp test2.cpp)

项目编译失败并显示链接错误 (MSVC 17.5.1):

致命错误 LNK1169:找到一个或多个多重定义的符号

什么原因导致静态库编译通过,共享库编译失败?我希望静态库也会失败,但这并没有发生。

c++ linker linker-errors
1个回答
0
投票

以下部分编辑的编译输出显示了问题:

user@host>tree
.
├── CMakeLists.txt
├── main.cpp
├── test1.cpp
├── test1.h
├── test2.cpp
└── test2.h

0 directories, 6 files
user@host>cat ./test1.h
// test1.h
#pragma once

int test();
user@host>cat ./test1.cpp
// test1.cpp
#include "test1.h"

int myvar1 = 12345;

int test() 
{
   return myvar1; 
}
user@host>cat ./test2.h
// test2.h
#pragma once

int test();
user@host>cat ./test2.cpp
// test2.cpp
#include "test2.h"

int test() 
{
    return 1;
}
user@host>cat ./main.cpp
// test2.cpp
#include "test1.h"
#include "test2.h"

#include <iostream>

int main() 
{
    std::cout << test() << '\n';
    return 0;
}
user@host>cat ./CMakeLists.txt
cmake_minimum_required(VERSION 3.0)

project(Test)

#add_library(Test SHARED test1.cpp test2.cpp)

add_library(test STATIC test1.cpp test2.cpp)

user@host>cmake .
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
[...]
user@host>#Build the library
user@host>make VERBOSE=1
[...]
[100%] Linking CXX static library libtest.a
/usr/bin/cmake -P CMakeFiles/test.dir/cmake_clean_target.cmake
/usr/bin/cmake -E cmake_link_script CMakeFiles/test.dir/link.txt --verbose=1
/usr/bin/ar qc libtest.a CMakeFiles/test.dir/test1.cpp.o CMakeFiles/test.dir/test2.cpp.o
/usr/bin/ranlib libtest.a
[...]
user@host>g++ -c ./main.cpp -o main.o
user@host>nm ./libtest.a | c++filt

test1.cpp.o:
0000000000000000 D myvar1
0000000000000000 T test()

test2.cpp.o:
0000000000000000 T test()
user@host>g++ --verbose -Wl,--warn-common -Wl,--verbose ./main.o ./libtest.a -o testexec
Using built-in specs.
[...]
attempt to open /usr/lib64/gcc/x86_64-suse-linux/7/../../../../lib64/crt1.o succeeded
[...]
attempt to open ./main.o succeeded
./main.o
attempt to open ./libtest.a succeeded
./libtest.a
(./libtest.a)test1.cpp.o
attempt to open /usr/lib64/gcc/x86_64-suse-linux/7/libstdc++.so succeeded
/usr/lib64/gcc/x86_64-suse-linux/7/libstdc++.so
[...]
user@host>./testexec
12345
user@host>

cmake 调用 ar 来创建静态库(这是标准方法)。但是ar不知道程序符号,盲目打包.o文件。

虽然我不是 ld 专家,但这个 this answer 表明 ld 在加载外部库时具有“智能”符号选择。 在这种情况下,我假设当使用静态库时,ld 足够聪明,可以立即丢弃重复的符号。

制作共享库时,ld直接链接.o文件(没有库预处理)并检测符号冲突。

© www.soinside.com 2019 - 2024. All rights reserved.