声明向量类型变量的段错误<shared_ptr<int>>

2022-01-12 00:00:00 gcc redhat segmentation-fault c++ ld

这是给出段错误的程序.

Here is the program that gives the segfault.

#include <iostream>
#include <vector>
#include <memory>

int main() 
{
    std::cout << "Hello World" << std::endl;

    std::vector<std::shared_ptr<int>> y {};  

    std::cout << "Hello World" << std::endl;
}

当然,程序本身绝对没有错.段错误的根本原因取决于其构建和运行的环境.

Of course, there is absolutely nothing wrong in the program itself. The root cause of the segfault depends on the environment in which its built and ran.

我们在 Amazon 使用构建系统,该系统以几乎机器独立的方式构建和部署二进制文件(libbin).对于我们的例子,这基本上意味着它将可执行文件(从上述程序构建)部署到 $project_dir/build/bin/ 和 几乎 它的所有依赖项(即共享库) 到 $project_dir/build/lib/ 中.为什么我使用短语 "almost" 是因为对于共享库,例如 libc.solibm.sold-linux-x86-64.so.2 和可能其他几个,可执行文件从系统中挑选(即来自 /lib64 ).请注意,应该从 $project_dir/build/lib 中选择 libstdc++.

We, at Amazon, use a build system which builds and deploys the binaries (lib and bin) in an almost machine independent way. For our case, that basically means it deploys the executable (built from the above program) into $project_dir/build/bin/ and almost all its dependencies (i.e the shared libraries) into $project_dir/build/lib/. Why I used the phrase "almost" is because for shared libraries such libc.so, libm.so, ld-linux-x86-64.so.2 and possibly few others, the executable picks from the system (i.e from /lib64 ). Note that it is supposed to pick libstdc++ from $project_dir/build/lib though.

现在我运行如下:

$ LD_LIBRARY_PATH=$project_dir/build/lib ./build/bin/run

segmentation fault

但是,如果我运行它,而不设置 LD_LIBRARY_PATH.它运行良好.

However if I run it, without setting the LD_LIBRARY_PATH. It runs fine.

这里是两种情况的 ldd 信息(请注意,我已经编辑了输出以提及库的 完整 版本只要有区别)

Here are ldd informations for both cases (please note that I've edited the output to mention the full version of the libraries wherever there is difference)

$ LD_LIBRARY_PATH=$project_dir/build/lib ldd ./build/bin/run

linux-vdso.so.1 =>  (0x00007ffce19ca000)
libstdc++.so.6 => $project_dir/build/lib/libstdc++.so.6.0.20 
libgcc_s.so.1 =>  $project_dir/build/lib/libgcc_s.so.1 
libc.so.6 => /lib64/libc.so.6 
libm.so.6 => /lib64/libm.so.6 
/lib64/ld-linux-x86-64.so.2 (0x0000562ec51bc000)

并且没有 LD_LIBRARY_PATH:

and without LD_LIBRARY_PATH:

$ ldd ./build/bin/run

linux-vdso.so.1 =>  (0x00007fffcedde000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6.0.16 
libgcc_s.so.1 => /lib64/libgcc_s-4.4.6-20110824.so.1
libc.so.6 => /lib64/libc.so.6 
libm.so.6 => /lib64/libm.so.6 
/lib64/ld-linux-x86-64.so.2 (0x0000560caff38000)

2.段错误时的 gdb

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7dea45c in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.62.al12.x86_64
(gdb) bt
#0  0x00007ffff7dea45c in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1  0x00007ffff7df0c55 in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
#2  0x00007ffff7b1dc41 in std::locale::_S_initialize() () from $project_dir/build/lib/libstdc++.so.6
#3  0x00007ffff7b1dc85 in std::locale::locale() () from $project_dir/build/lib/libstdc++.so.6
#4  0x00007ffff7b1a574 in std::ios_base::Init::Init() () from $project_dir/build/lib/libstdc++.so.6
#5  0x0000000000400fde in _GLOBAL__sub_I_main () at $project_dir/build/gcc-4.9.4/include/c++/4.9.4/iostream:74
#6  0x00000000004012ed in __libc_csu_init ()
#7  0x00007ffff7518cb0 in __libc_start_main () from /lib64/libc.so.6
#8  0x0000000000401021 in _start ()
(gdb)

3.LD_DEBUG=全部

我还尝试通过为 segfault 案例启用 LD_DEBUG=all 来查看链接器信息.当它搜索 pthread_once 符号时,我发现了一些可疑的东西,当它无法找到它时,它给出了段错误(这是我对以下输出片段 BTW 的解释):

3. LD_DEBUG=all

I also tried to see the linker information by enabling LD_DEBUG=all for the segfault case. I found something suspicious, as it searches for pthread_once symbol, and when it unable to find this, it gives segfault (that is my interpretation of the following output snippet BTW):

initialize program: $project_dir/build/bin/run

symbol=_ZNSt8ios_base4InitC1Ev;  lookup in file=$project_dir/build/bin/run [0]
symbol=_ZNSt8ios_base4InitC1Ev;  lookup in file=$project_dir/build/lib/libstdc++.so.6 [0]
binding file $project_dir/build/bin/run [0] to $project_dir/build/lib/libstdc++.so.6 [0]: normal symbol `_ZNSt8ios_base4InitC1Ev' [GLIBCXX_3.4]
symbol=_ZNSt6localeC1Ev;  lookup in file=$project_dir/build/bin/run [0]
symbol=_ZNSt6localeC1Ev;  lookup in file=$project_dir/build/lib/libstdc++.so.6 [0]
binding file $project_dir/build/lib/libstdc++.so.6 [0] to $project_dir/build/lib/libstdc++.so.6 [0]: normal symbol `_ZNSt6localeC1Ev' [GLIBCXX_3.4]
symbol=pthread_once;  lookup in file=$project_dir/build/bin/run [0]
symbol=pthread_once;  lookup in file=$project_dir/build/lib/libstdc++.so.6 [0]
symbol=pthread_once;  lookup in file=$project_dir/build/lib/libgcc_s.so.1 [0]
symbol=pthread_once;  lookup in file=/lib64/libc.so.6 [0]
symbol=pthread_once;  lookup in file=/lib64/libm.so.6 [0]
symbol=pthread_once;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]

但是当它成功运行时,我没有看到任何 pthread_once

But I dont see any pthread_once for the case when it runs successfully!

我知道这样调试非常困难,而且我可能没有提供很多关于环境和所有内容的信息.但是,我的问题仍然是:这个段错误的可能根本原因是什么?如何进一步调试并找到它?一旦我发现问题,修复就会很容易.

I know that its very difficult to debug like this and probably I've not given a lot of informations about the environments and all. But still, my question is: what could be the possible root-cause for this segfault? How to debug further and find that? Once I find the issue, fix would be easy.

我在 RHEL5 上使用 GCC 4.9.

I'm using GCC 4.9 on RHEL5.

如果我评论以下行:

std::vector<std::shared_ptr<int>> y {}; 

它编译并运行良好!

我刚刚在我的程序中包含了以下标题:

I just included the following header to my program:

#include <boost/filesystem.hpp>

并相应地链接.现在它可以在没有任何段错误的情况下工作.所以似乎通过对 libboost_system.so.1.53.0. 的依赖,满足了一些要求,或者规避了问题!

and linked accordingly. Now it works without any segfault. So it seems by having a dependency on libboost_system.so.1.53.0., some requirements are met, or the problem is circumvented!

由于我在将可执行文件链接到 libboost_system.so.1.53.0 时看到它工作正常,所以我一步一步地做了以下事情.

Since I saw it working when I made the executable to be linked against libboost_system.so.1.53.0, so I did the following things step by step.

我没有在代码本身中使用 #include <boost/filesystem.hpp>,而是使用原始代码并通过使用 libboost_system.so 预加载来运行它code>LD_PRELOAD如下:

Instead of using #include <boost/filesystem.hpp> in the code itself, I use the original code and ran it by preloading libboost_system.so using LD_PRELOAD as follows:

$ LD_PRELOAD=$project_dir/build/lib/libboost_system.so $project_dir/build/bin/run

它运行成功!

接下来我在 libboost_system.so 上做了 ldd,它给出了一个库列表,其中两个是:

Next I did ldd on the libboost_system.so which gave a list of libs, two of which were:

  /lib64/librt.so.1
  /lib64/libpthread.so.0

所以我没有预加载 libboost_system,而是分别预加载 librtlibpthread:

So instead of preloading libboost_system, I preload librt and libpthread separately:

$ LD_PRELOAD=/lib64/librt.so.1 $project_dir/build/bin/run

$ LD_PRELOAD=/lib64/libpthread.so.0 $project_dir/build/bin/run

在这两种情况下,它都运行成功.

In both cases, it ran successfully.

现在我的结论是,通过加载 librtlibpthread (或 both ),可以满足某些要求或规避问题!不过,我仍然不知道问题的根本原因.

Now my conclusion is that by loading either librt or libpthread (or both ), some requirements are met or the problem is circumvented! I still dont know the root cause of the issue, though.

由于构建系统很复杂,并且默认情况下有很多选项.所以我尝试使用 CMake 的 set 命令显式添加 -lpthread,然后它就起作用了,正如我们已经通过 preloading libpthread 看到的那样 成功了!

Since the build system is complex and there are lots of options which are there by default. So I tried to explicitly add -lpthread using CMake's set command, then it worked, as we have already seen that by preloading libpthread it works!

为了查看这两种情况的build区别(when-it-works和when-it-gives-segfault),我通过将 -v 传递给 GCC 以 verbose 模式构建它,以查看编译阶段和它实际传递给 cc1plus 的选项(编译器) 和 collect2 (链接器).

In order to see the build difference between these two cases (when-it-works and when-it-gives-segfault), I built it in verbose mode by passing -v to GCC, to see the compilation stages and the options it actually passes to cc1plus (compiler) and collect2 (linker).

(请注意,为简洁起见,已使用美元符号和虚拟路径对路径进行了编辑.)

$/gcc-4.9.4/cc1plus -quiet -v -I/a/include -I/b/include -iprefix$/gcc-4.9.4/-MMD main.cpp.d -MF main.cpp.o.d -MT main.cpp.o-D_GNU_SOURCE -D_REENTRANT -D __USE_XOPEN2K8 -D _LARGEFILE_SOURCE -D _FILE_OFFSET_BITS=64 -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D NDEBUG $/lab/main.cpp -quiet -dumpbase main.cpp -msse -mfpmath=sse -march=core2 -auxbase-strip main.cpp.o -g -O3 -Wall -Wextra -std=gnu++1y -version -fdiagnostics-color=auto -ftemplate-depth=128 -fno-operator-names -o/tmp/ccxfkRyd.s

$/gcc-4.9.4/cc1plus -quiet -v -I /a/include -I /b/include -iprefix $/gcc-4.9.4/ -MMD main.cpp.d -MF main.cpp.o.d -MT main.cpp.o -D_GNU_SOURCE -D_REENTRANT -D __USE_XOPEN2K8 -D _LARGEFILE_SOURCE -D _FILE_OFFSET_BITS=64 -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D NDEBUG $/lab/main.cpp -quiet -dumpbase main.cpp -msse -mfpmath=sse -march=core2 -auxbase-strip main.cpp.o -g -O3 -Wall -Wextra -std=gnu++1y -version -fdiagnostics-color=auto -ftemplate-depth=128 -fno-operator-names -o /tmp/ccxfkRyd.s

不管它是否有效,cc1plus 的命令行参数都是完全相同的.完全没有区别.这似乎不是很有帮助.

Irrespective of whether it works or not, the command-line arguments to cc1plus are exactly the same. No difference at all. That does not seem to be very helpful.

然而,区别在于链接时间.以下是我所看到的,适用的情况:

The difference, however, is at the linking time. Here is what I see, for the case when it works:

$/gcc-4.9.4/collect2 -plugin $/gcc-4.9.4/liblto_plugin.so
-plugin-opt=$/gcc-4.9.4/lto-wrapper -plugin-opt=-fresolution=/tmp/cchl8RtI.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker/lib64/ld-linux-x86-64.so.2 -o 运行/usr/lib/../lib64/crt1.o/usr/lib/../lib64/crti.o $/gcc-4.9.4/crtbegin.o -L/a/lib -L/b/lib-L/c/lib-lpthread --as-needed main.cpp.o -lboost_timer -lboost_wave -lboost_chrono -lboost_filesystem -lboost_graph -lboost_locale -lboost_thread -lboost_wserialization -lboost_atomic -lboost_context -lboost_date_time -lboost_iostreams -lboost_math_c99 -lboost_math_clboost_math_tr1 -lboost_math_tr1f -lboost_math_tr1l -lboost_mpi -lboost_prg_exec_monitor -lboost_program_options -lboost_random -lboost_regex -lboost_serialization -lboost_signals -lboost_system -lboost_unit_test_framework -lboost_exception -lboost_test_exec_monitor -lbz2 -licui18n -licuuc -licudata -lz -rpath/一个/lib中:/b/lib中:/c/lib: -lstdc++ -lm -lgcc_s -lgcc -lpthread -lc -lgcc_s -lgcc $/gcc-4.9.4/crtend.o/usr/lib/../lib64/crtn.o

$/gcc-4.9.4/collect2 -plugin $/gcc-4.9.4/liblto_plugin.so
-plugin-opt=$/gcc-4.9.4/lto-wrapper -plugin-opt=-fresolution=/tmp/cchl8RtI.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o run /usr/lib/../lib64/crt1.o /usr/lib/../lib64/crti.o $/gcc-4.9.4/crtbegin.o -L/a/lib -L/b/lib -L/c/lib -lpthread --as-needed main.cpp.o -lboost_timer -lboost_wave -lboost_chrono -lboost_filesystem -lboost_graph -lboost_locale -lboost_thread -lboost_wserialization -lboost_atomic -lboost_context -lboost_date_time -lboost_iostreams -lboost_math_c99 -lboost_math_c99f -lboost_math_c99l -lboost_math_tr1 -lboost_math_tr1f -lboost_math_tr1l -lboost_mpi -lboost_prg_exec_monitor -lboost_program_options -lboost_random -lboost_regex -lboost_serialization -lboost_signals -lboost_system -lboost_unit_test_framework -lboost_exception -lboost_test_exec_monitor -lbz2 -licui18n -licuuc -licudata -lz -rpath /a/lib:/b/lib:/c/lib: -lstdc++ -lm -lgcc_s -lgcc -lpthread -lc -lgcc_s -lgcc $/gcc-4.9.4/crtend.o /usr/lib/../lib64/crtn.o

如您所见,-lpthread 被提及两次!第一个 -lpthread(后面跟着 --as-needed)是 missing 当它给出 segfault 的情况下强>.这是这两种情况之间的唯一区别.

As you can see, -lpthread is mentioned twice! The first -lpthread (which is followed by --as-needed) is missing for the case when it gives segfault. That is the only difference between these two cases.

有趣的是,两种情况下 nm -C 的输出是相同的(如果忽略第一列中的整数值).

Interestingly, the output of nm -C in both cases is identical (if you ignore the integer values in the first columns).

0000000000402580 d _DYNAMIC
0000000000402798 d _GLOBAL_OFFSET_TABLE_
0000000000401000 t _GLOBAL__sub_I_main
0000000000401358 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U _Unwind_Resume
0000000000401150 W std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_destroy()
0000000000401170 W std::vector<std::shared_ptr<int>, std::allocator<std::shared_ptr<int> > >::~vector()
0000000000401170 W std::vector<std::shared_ptr<int>, std::allocator<std::shared_ptr<int> > >::~vector()
0000000000401250 W std::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >::~vector()
0000000000401250 W std::vector<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >::~vector()
                 U std::ios_base::Init::Init()
                 U std::ios_base::Init::~Init()
0000000000402880 B std::cout
                 U std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
0000000000402841 b std::__ioinit
                 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
                 U operator delete(void*)
                 U operator new(unsigned long)
0000000000401510 r __FRAME_END__
0000000000402818 d __JCR_END__
0000000000402818 d __JCR_LIST__
0000000000402820 d __TMC_END__
0000000000402820 d __TMC_LIST__
0000000000402838 A __bss_start
                 U __cxa_atexit
0000000000402808 D __data_start
0000000000401100 t __do_global_dtors_aux
0000000000402820 t __do_global_dtors_aux_fini_array_entry
0000000000402810 d __dso_handle
0000000000402828 t __frame_dummy_init_array_entry
                 w __gmon_start__
                 U __gxx_personality_v0
0000000000402838 t __init_array_end
0000000000402828 t __init_array_start
00000000004012b0 T __libc_csu_fini
00000000004012c0 T __libc_csu_init
                 U __libc_start_main
                 w __pthread_key_create
0000000000402838 A _edata
0000000000402990 A _end
000000000040134c T _fini
0000000000400e68 T _init
0000000000401028 T _start
0000000000401054 t call_gmon_start
0000000000402840 b completed.6661
0000000000402808 W data_start
0000000000401080 t deregister_tm_clones
0000000000401120 t frame_dummy
0000000000400f40 T main
00000000004010c0 t register_tm_clones

推荐答案

考虑到崩溃的点,而且预加载 libpthread 似乎可以修复它,我相信这两种情况的执行分歧于 locale_init.cc:315.这是代码的摘录:

Given the point of crash, and the fact that preloading libpthread seems to fix it, I believe that the execution of the two cases diverges at locale_init.cc:315. Here is an extract of the code:

  void
  locale::_S_initialize()
  {
#ifdef __GTHREADS
    if (__gthread_active_p())
      __gthread_once(&_S_once, _S_initialize_once);
#endif
    if (!_S_classic)
      _S_initialize_once();
  }

如果您的程序与 pthread 链接,

__gthread_active_p() 返回 true,特别是它检查 pthread_key_create 是否可用.在我的系统上,这个符号在/usr/include/c++/7.2.0/x86_64-pc-linux-gnu/bits/gthr-default.h"中定义为static inline,因此它是 ODR 违规的潜在来源.

__gthread_active_p() returns true if your program is linked against pthread, specifically it checks if pthread_key_create is available. On my system, this symbol is defined in "/usr/include/c++/7.2.0/x86_64-pc-linux-gnu/bits/gthr-default.h" as static inline, hence it is a potential source of ODR violation.

请注意,LD_PRELOAD=libpthread,so 将始终导致 __gthread_active_p() 返回 true.

Notice that LD_PRELOAD=libpthread,so will always cause __gthread_active_p() to return true.

__gthread_once 是另一个应始终转发到 pthread_once 的内联符号.

__gthread_once is another inlined symbol that should always forward to pthread_once.

如果不进行调试,很难猜出发生了什么,但我怀疑你正在触及 __gthread_active_p() 的真正分支,即使它不应该这样做,然后程序崩溃,因为没有pthread_once 调用.

It's hard to guess what's going on without debugging, but I suspect that you are hitting the true branch of __gthread_active_p() even when it shouldn't, and the program then crashes because there is no pthread_once to call.

编辑:所以我做了一些实验,我看到在 std::locale::_S_initialize 中崩溃的唯一方法是如果 __gthread_active_p 返回 true,但 pthread_oncecode> 没有链接进去.

EDIT: So I did some experiments, the only way I see to get a crash in std::locale::_S_initialize is if __gthread_active_p returns true, but pthread_once is not linked in.

libstdc++ 不直接链接到 pthread,但它会将 pthread_xx 的一半导入为弱对象,这意味着它们可以是未定义的并且不会导致链接器错误.

libstdc++ does not link directly against pthread, but it imports half of pthread_xx as weak objects, which means they can be undefined and not cause a linker error.

显然链接 pthread 会使崩溃消失,但如果我是对的,主要问题是您的 libstdc++ 认为它位于多线程可执行文件中,即使我们没有链接 pthread.

Obviously linking pthread will make the crash disappear, but if I am right, the main issue is that your libstdc++ thinks that it is inside a multi-threaded executable even if we did not link pthread in.

现在,__gthread_active_p 使用 __pthread_key_create 来决定我们是否有线程.这在您的可执行文件中定义为弱对象(可以是 nullptr 并且仍然可以).由于 shared_ptr (删除它并再次检查 nm 以确保),我 99% 确定该符号存在.所以,不知何故 __pthread_key_create 被绑定到一个有效的地址,可能是因为你的链接器标志中的最后一个 -lpthread .您可以通过在 locale_init.cc:315 处设置断点并检查您采用的分支来验证这一理论.

Now, __gthread_active_p uses __pthread_key_create to decide if we have threads or no. This is defined in your executable as a weak object (can be nullptr and still be fine). I am 99% sure that the symbol is there because of shared_ptr (remove it and check nm again to be sure). So, somehow __pthread_key_create gets bound to a valid address, maybe because of that last -lpthread in your linker flags. You can verify this theory by putting a breakpoint at locale_init.cc:315 and checking which branch you take.

EDIT2:

评论摘要,只有当我们具备以下所有条件时,该问题才能重现:

Summary of the comments, the issue is only reproducible if we have all of the following:

  1. 使用 ld.gold 代替 ld.bfd
  2. 使用--根据需要
  3. 强制 __pthread_key_create 的弱定义,在本例中通过 std::shared_ptr 的实例化.
  4. 未链接到 pthread,或链接 pthread after --as-needed.
  1. Use ld.gold instead of ld.bfd
  2. Use --as-needed
  3. Forcing a weak definition of __pthread_key_create, in this case via instantiation of std::shared_ptr.
  4. Not linking to pthread, or linking pthread after --as-needed.

回答评论中的问题:

为什么默认使用黄金?

默认情况下,它使用 /usr/bin/ld,在大多数发行版中,它是指向 /usr/bin/ld.bfd/的符号链接usr/bin/ld.gold.这样的默认值可以使用 update-alternatives 进行操作.我不确定为什么在你的情况下它是 ld.gold,据我了解 RHEL5 默认附带 ld.bfd.

By default it uses /usr/bin/ld, which on most distro is a symlink to either /usr/bin/ld.bfd or /usr/bin/ld.gold. Such default can be manipulated using update-alternatives. I am not sure why in your case it is ld.gold, as far as I understand RHEL5 ships with ld.bfd as default.

如果需要,gold 为什么不向二进制文件添加 pthread.so 依赖项?

And why does gold not add pthread.so dependency to the binary if it is needed?

因为对所需内容的定义有些模糊.man ld 说(强调我的):

Because the definition of what is needed is somehow shady. man ld says (emphasis mine):

--根据需要

--不需要的

此选项会影响命令行中提到的动态库的 ELF DT_NEEDED 标记在 --as-needed 选项之后.通常链接器会添加一个 DT_NEEDED命令行中提到的每个动态库的标记,无论该库是否实际需要.--as-needed 导致 DT_NEEDED 标签仅针对链接中该点满足来自常规的 非弱未定义符号 引用的库发出目标文件,或者,如果库不是在其他所需库的 DT_NEEDED 列表中找到,来自另一个所需动态的非弱未定义符号引用图书馆.目标文件或库出现在相关库之后的命令行上不会影响是否根据需要查看该库.这是相似的提取规则档案中的目标文件.--no-as-needed 恢复默认行为.

This option affects ELF DT_NEEDED tags for dynamic libraries mentioned on the command line after the --as-needed option. Normally the linker will add a DT_NEEDED tag for each dynamic library mentioned on the command line, regardless of whether the library is actually needed or not. --as-needed causes a DT_NEEDED tag to only be emitted for a library that at that point in the link satisfies a non-weak undefined symbol reference from a regular object file or, if the library is not found in the DT_NEEDED lists of other needed libraries, a non-weak undefined symbol reference from another needed dynamic library. Object files or libraries appearing on the command line after the library in question do not affect whether the library is seen as needed. This is similar to the rules for extraction of object files from archives. --no-as-needed restores the default behaviour.

现在,根据这个错误报告,gold 尊重非弱未定义符号"部分,而 ld.bfd 根据需要看到弱符号.TBH 我对此没有完全理解,关于该链接是否应被视为 ld.gold 错误或 libstdc++ 的一些讨论错误.

Now, according to this bug report, gold is honoring the "non weak undefined symbol" part, while ld.bfd sees weak symbols as needed. TBH I do not have a full understanding on this, and there is some discussion on that link as to whether this is to be considered a ld.gold bug, or a libstdc++ bug.

为什么我需要同时提到 -pthread 和 -lpthread ?(-pthread 是我们的构建系统默认通过,我已经通过 -lpthread 来制作它与黄金一起使用).

Why do I need to mention -pthread and -lpthread both? (-pthread is passed by default by our build system, and I've pass -lpthread to make it work with gold is used).

-pthread-lpthread 做不同的事情(参见 pthread vs lpthread).我的理解是前者应该暗示后者.

-pthread and -lpthread do different things (see pthread vs lpthread). It is my understanding that the former should imply the latter.

无论如何,你可能只能通过一次 -lpthread,但你需要在 before --as-needed 之前这样做,或者使用--no-as-needed 在最后一个库之后和 -lpthread 之前.

Regardless, you can probably pass -lpthread only once, but you need to do it before --as-needed, or use --no-as-needed after the last library and before -lpthread.

还值得一提的是,即使使用黄金链接器,我也无法在我的系统 (GCC 7.2) 上重现此问题.所以我怀疑它已经在更新版本的 libstdc++ 中得到修复,这也可以解释为什么如果你使用系统标准库它不会出现段错误.

It is also worth mentioning that I was not able to reproduce this issue on my system (GCC 7.2), even using the gold linker. So I suspect that it has been fixed in a more recent version libstdc++, which might also explain why it does not segfault if you use the system standard library.

相关文章