gcc 11 未链接库构造函数

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

如何找出在 Ubuntu 22.04 上使用 GCC 11 构建时 gcc 无法链接我的库(仅使用 lib 构造函数)的原因?

我在这里设置了一个小例子来尝试复制问题:https://godbolt.org/z/PT4jETToj,问题是我不能 - 在 godbolt 上它可以工作。 该问题仅发生在本地。

库构造函数应该打印

Library constructor called!
,注意它在容器中构建时的表现,但在我的主机上构建时则不然 (
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
)

❯ rm -rf main.gcc; docker run -it --rm -v $(pwd):/workspace gcc:11.4 bash -c "cd /workspace; ./build.sh" && ./main.gcc
Building GCC variant
Library constructor called!
Main program started!

❯ rm -rf main.gcc; ./build.sh && ./main.gcc
Building GCC variant
Main program started! 

build.sh
运行的地方:

g++ -fPIC -shared -o libexample-gcc.so example.cpp \
  && g++ -std=c++17 -o main.gcc main.cpp -L. -lexample-gcc -Wl,-rpath,.

如果我在 clang 中本地构建它,这也有效。 我开始认为我的系统只是坏了,但我同事的系统(20.04,gcc 9.4)也没有运行库构造函数。

问题似乎出在链接和运行时之间。 当我添加

-Wl,--verbose
标志时,我可以看到链接器对于 gcc 和 clang
 都看到 
libexample

# verbose linker log
attempt to open ./libexample-gcc.so succeeded
./libexample-gcc.so

但是我的主机上生成的 gcc 二进制文件在

libexample
输出中没有显示
ldd
,而当使用 clang 构建时却显示了。 由于它不在链接器列表中,因此在运行时不存在以运行。

该符号在库中定义良好

❯ nm -C libexample-gcc.so | rg library_constructor
0000000000001179 T library_constructor()

但是库在

ldd
中完全缺失:

❯ ( LD_LIBRARY_PATH=$(pwd) ldd main.gcc | rg example ) || echo "not found"
not found

❯ LD_LIBRARY_PATH=$(pwd) ldd main.clang | rg example
        libexample-clang.so => /home/matt/workspace/lib_constructor_example/libexample-clang.so (0x00007d0188775000)

即使我明确添加

--no-as-needed
i.e.

,这也是如此
g++ -std=c++17 -o main.gcc main.cpp -L. -lexample-gcc -Wl,-rpath,. -Wl,--no-as-needed

我的环境或系统设置中是否有某些东西正在控制这一点?

linux gcc linker ld linker-flags
1个回答
0
投票

链接器不需要您的共享库,因为当链接器在输入序列中考虑它时, 它已累积 0 个对您的库定义的一个符号 library_constructor() 的未解析引用(自

main.o
当然,没有引用它)。您的库在输入时不会解析任何内容,因此不需要。
库构造函数通常是一个 

static

函数,以免被其他模块引用。我是 确保您计划向库中添加一些函数或数据对象的外部定义以使其有用。 如果您这样做,并且这些符号中的任何一个都在

main.o
或任何其他文件中引用 在您的库之前输入到链接,然后将需要您的库并将其链接 无论您在任何系统上使用什么前端。
直到它提供链接所需的任何符号,无论您的库是否链接
取决于是否在幕后生成的样板链接选项
您调用的特定前端在命令行库之前包含 

-Wl,-as-needed

是插值的。

--as-needed

生效时,链接器将认为需要共享库当且仅当 它确实需要解决链接中先前累积的引用问题。当

--as-needed
无效时,链接器将考虑所需的库并链接它,即使它实际上不是 需要解决参考文献。 在调用
library_constructor()

ldd

报告动态链接库的情况下,前端 使用中配置不默认

--as-needed
。在不调用
library_constructor()
而调用
ldd
的场景中 不报告您的库,正在使用的前端
确实
默认
--as-needed
你努力强迫
--no-as-needed

g++ -std=c++17 -o main.gcc main.cpp -L. -lexample-gcc -Wl,-rpath,. -Wl,--no-as-needed

 不成功,因为每个相反的链接器选项 

--as-needed
/

--no-as-needed

 都是
对共享库进行操作
随后
输入,直到出现相反的选项。所以
你的 
-Wl,--no-as-needed
来得太晚了,因为 -lexample-gcc 已经被输入了。这将是 仅适用于在
g++
的样板添加到命令行中输入的任何共享库,除非 该样板文件会在之后附加
--as-needed
,但在附加任何标准共享库之前(实际上它无法管理)。
您成功地将您的图书馆与 
clang++
链接到本地是因为您本地的

clang(++)

与 您本地的

gcc\g++
,不默认
--as-needed
。我也是这样:
$ g++ --version
g++ (Ubuntu 13.2.0-23ubuntu4) 13.2.0
...
$ clang++ --version
Ubuntu clang version 18.1.6 (++20240518023429+1118c2e05e67-1~exp1~20240518143527.144)

$ tail -n +1 main.cpp example.cpp CMakeLists.txt 
==> main.cpp <==
#include <iostream>

extern int foo;

auto main() -> int {
    std::cout << "Main program started!" << std::endl;
    return 0;
}

==> example.cpp <==
#include <iostream>

// Constructor function
static __attribute__((constructor)) void library_constructor() {
    std::cout << "Library constructor called!" << std::endl;
}

==> CMakeLists.txt <==
cmake_minimum_required(VERSION 3.21)

project(main-test VERSION 1.0 LANGUAGES CXX)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
set(CXX_STANDARD 17)

add_library(example SHARED)
target_sources(example PRIVATE example.cpp)

add_executable(main)
target_sources(main PRIVATE main.cpp)
target_link_libraries(main PRIVATE example)

$ mkdir build
$ cd build/
$ cmake ..
...
$ make VERBOSE=1 | grep '\-o main'
/usr/bin/c++ CMakeFiles/main.dir/main.cpp.o -o main  -Wl,-rpath,/home/imk/develop/so/scrap2/build libexample.so
最后一件事是 

g++
 链接命令行。让我们重做一下 
--verbose

看看

--as-needed
是否在样板文件中:
$ /usr/bin/c++ CMakeFiles/main.dir/main.cpp.o -o main --verbose -Wl,-rpath,/home/imk/develop/so/scrap2/build libexample.so 2>&1 | grep -o as-needed; echo Done
as-needed
Done
是的。还有:

$ readelf --dynamic --wide main | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 

libexample.so
 不是 
NEEDED

:

$ ./main
Main program started!
使用 

clang++
重做:

$ /usr/bin/clang++ CMakeFiles/main.dir/main.cpp.o -o main --verbose -Wl,-rpath,/home/imk/develop/so/scrap2/build libexample.so 2>&1 | grep -o as-needed; echo Done Done

--as-needed
。还有:

$ readelf --dynamic --wide main | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libexample.so] 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6] 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]

链接了三个共享的不必要的共享库,包括

libexample.so

$ ./main Library constructor called! Main program started!

    

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