gpt4 book ai didi

c++ - 当g++静态链接pthread时,导致段错误,为什么?

转载 作者:IT老高 更新时间:2023-10-28 22:00:24 28 4
gpt4 key购买 nike

#include <iostream>
#include <map>
#include <thread>

#define SIZE 1024
#define AMOUNT 100000
#define THREADS 4

class A
{
private:
char a[SIZE];
};

void test()
{
std::cout << "test start\n";
std::map<int, A*> container;
for(int i=0; i<AMOUNT; i++)
{
A* a = new A();
std::pair<int, A*>p = std::make_pair(i, a);
container.insert(p);
}

std::cout << "test release\n";
for(int i=0; i<AMOUNT; i++)
{
auto iter = container.find(i);
delete iter->second;
container.erase(iter);
}
std::cout << "test end\n";
}

int main()
{
std::thread ts[THREADS];
for(int i=0; i<THREADS; i++)
{
ts[i] = std::thread(test);
}

for(std::thread& x: ts)
{
x.join();
}

return 0;
}

上面是一个简单的c++代码。

编译: g++ -pthread -o one one.cpp -Wall -std=c++11 -O3 ldd one,得到:
    linux-vdso.so.1 =>  (0x00007ffebafce000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fb47352a000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb473313000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb4730f4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb472d2a000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb472a22000)
/lib64/ld-linux-x86-64.so.2 (0x00005654c5112000)

运行 ./one,一切正常。

然后,我尝试一个静态链接: g++ -pthread -o one one.cpp -Wall -std=c++11 -O3 -static ldd one,得到:
    not a dynamic executable

但是当我运行它时,出现了一些问题...
test start
Segmentation fault (core dumped)

-g重新编译,gdb显示:
wang[00:35][~/test]$ gdb one
GNU gdb (Ubuntu 7.10-1ubuntu2) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from one...done.
(gdb) run
Starting program: /home/wang/test/one
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7ffa700 (LWP 3623)]
test start
[New Thread 0x7ffff77f8700 (LWP 3624)]
test start
[New Thread 0x7ffff6ff7700 (LWP 3625)]
test start
[New Thread 0x7ffff67f6700 (LWP 3626)]
test start

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb)

为什么这个 ?

更新
=============================

使用 boost::thread库( boost 版:1.60),

std::thread替换为 boost::thread,并建立静态链接,
g++ -pthread -o one1 one.cpp -Wall -std=c++11 -O3 -I /opt/boost/include/ -L /opt/boost/lib/ -lboost_system -lboost_thread -static
没问题!

困惑...

最佳答案

首先,解决方案。这将在这里工作:

更新: Since Ubuntu 18.04,您还需要链接到librt(添加-lrt):

g++ -o one one.cpp -Wall -std=c++11 -O3 -static -lrt -pthread \
-Wl,--whole-archive -lpthread -Wl,--no-whole-archive

(继续原始答案)
g++ -o one one.cpp -Wall -std=c++11 -O3 -static -pthread \
-Wl,--whole-archive -lpthread -Wl,--no-whole-archive

当您使用 -pthread时,编译器将已经针对pthread进行链接(并且取决于平台,它确实定义了额外的宏,例如 -D_REENTRANT,有关更多详细信息,请参见 this question)。

因此,如果 -pthread暗示 -lpthread,为什么在静态链接时需要指定 -lpthreadWl,--whole-archive有什么作用?

了解弱符号

在Unix上,使用 ELF文件格式,其概念为 weak and strong symbols。引用 Wikipedia page:

By default, without any annotation, a symbol in an object file is strong. During linking, a strong symbol can override a weak symbol of the same name. In contrast, two strong symbols that share a name yield a link error during link-time.



在动态库和静态库方面存在细微的差异。在静态库中,链接器将在第一个符号处停止,即使它是一个弱符号也将停止寻找强符号。为了迫使它查看所有符号(就像对动态链接库所做的那样), ld支持 --whole-archive选项。

引用 man ld :

--whole-archive: For each archive mentioned on the command line after the --whole-archive option, include every object file in the archive in the link, rather than searching the archive for the required object files. This is normally used to turn an archive file into a shared library, forcing every object to be included in the resulting shared library. This option may be used more than once.



继续说明,从gcc,您必须将选项传递为 -Wl,--whole-archive:

Two notes when using this option from gcc: First, gcc doesn't know about this option, so you have to use -Wl,-whole-archive. Second, don't forget to use -Wl,-no-whole-archive after your list of archives, because gcc will add its own list of archives to your link and you may not want this flag to affect those as well.



并再次说明了如何将其关闭:

--no-whole-archive: Turn off the effect of the --whole-archive option for subsequent archive files.



pthread和libstdc++中的符号较弱

弱符号的用例之一是能够将实现与优化的实现交换出去。另一个方法是使用存根,如有必要,可以稍后将其替换。

例如,POSIX要求 fputc( conceptionally used by printf )是线程安全的,并且需要进行同步,这非常昂贵。在单线程环境中,您不想支付这些费用。因此,实现可以将同步功能实现为空存根,并将功能声明为弱符号。

稍后,如果链接了多线程库(例如pthread),则很明显,不需要单线程支持。链接多线程库时,链接器然后可以用实际的同步功能(定义为强符号并由线程库实现)替换存根。另一方面,如果未链接任何多线程库,则可执行文件将使用存根用于同步功能。

glibc(提供 fputc)和pthreads似乎恰好使用了这一技巧。有关详细信息,请引用此 question about the usage of weak symbols in glibc。上面的示例取自 this answer

nm允许您详细查看它,这似乎与上面引用的答案一致:
$ nm /usr/lib/libc.a 2>/dev/null | grep pthread_mutex_lock
w __pthread_mutex_lock
... (repeats)

“w”代表“弱”,因此静态链接的libc库包含 __pthread_mutex_lock作为弱符号。静态链接的pthread库包含它作为强符号:
$ nm /usr/lib/libpthread.a 2>/dev/null | grep pthread_mutex_lock
U pthread_mutex_lock
pthread_mutex_lock.o:
00000000000006a0 T __pthread_mutex_lock
00000000000006a0 T pthread_mutex_lock
0000000000000000 t __pthread_mutex_lock_full

返回示例程序

通过查看动态链接的可执行文件的共享库依赖关系,我在我的机器上得到了几乎相同的 ldd输出:
$ ldd one
linux-vdso.so.1 (0x00007fff79d6d000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007fcaaeeb3000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007fcaaeb9b000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007fcaae983000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fcaae763000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fcaae3bb000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcaaf23b000)

使用 ltrace打印出库调用会导致以下输出:
$ ltrace -C ./one 
std::ios_base::Init::Init()(0x563ab8df71b1, 0x7ffdc483cae8, 0x7ffdc483caf8, 160) = 0
__cxa_atexit(0x7fab3023bc20, 0x563ab8df71b1, 0x563ab8df7090, 6) = 0
operator new(unsigned long)(16, 0x7ffdc483cae8, 0x7ffdc483caf8, 192) = 0x563ab918bc20
std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)())(0x7ffdc483c990, 0x7ffdc483c998, 0x7fab2fa52320, 0x7fab2fa43a80) = 0
operator new(unsigned long)(16, 0x7fab2f6a1fb0, 0, 0x800000) = 0x563ab918bd70
std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)())(0x7ffdc483c990, 0x7ffdc483c998, 0x7fab2fa52320, 0x7fab2fa43a80) = 0
operator new(unsigned long)(16, 0x7fab2eea0fb0, 0, 0x800000) = 0x563ab918bec0
std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)())(0x7ffdc483c990, 0x7ffdc483c998, 0x7fab2fa52320, 0x7fab2fa43a80test start
) = 0
operator new(unsigned long)(16, 0x7fab2e69ffb0, 0, 0x800000) = 0x563ab918c010
std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)())(0x7ffdc483c990, 0x7ffdc483c998, 0x7fab2fa52320, 0x7fab2fa43a80test start
test start
) = 0
std::thread::join()(0x7ffdc483c9a0, 0x7fab2de9efb0, 0, 0x800000test start
test release
test release
test release
test release
test end
test end
test end
test end
) = 0
std::thread::join()(0x7ffdc483c9a8, 0x7fab2eea19c0, 0x7fab2f6a2700, 0) = 0
std::thread::join()(0x7ffdc483c9b0, 0x7fab2e6a09c0, 0x7fab2eea1700, 0) = 0
std::thread::join()(0x7ffdc483c9b8, 0x7fab2de9f9c0, 0x7fab2e6a0700, 0) = 0
+++ exited (status 0) +++

例如,调用 std::thread::join,很可能会在内部使用 pthread_join。可以在 ldd输出中列出的(动态链接的)库中找到该符号,即 libstdc++.so.6libpthread.so.0:
$ nm /usr/lib/libstdc++.so.6 | grep pthread_join
w pthread_join

$ nm /usr/lib/libpthread.so.0 | grep pthread_join
0000000000008280 T pthread_join

在动态链接的可执行文件中,链接器将用强符号代替弱符号。在此示例中,我们必须对静态链接库强制执行相同的语义。这就是为什么需要 -Wl,--whole-archive -lpthread -Wl,--no-whole-archive的原因。

找出来有点试错。至少,我没有找到关于该主题的清晰文档。我认为这是因为 static linking on Linux has become rather an edge case,而动态链接通常是有关如何使用库的规范方法(有关比较,请参见 Static linking vs dynamic linking)。为了使它正常工作,我看到并亲自奋斗了一段时间的最极端例子是 link TBB statically

附录:自动工具的解决方法

如果将自动工具用作构建系统,则需要一种解决方法,因为自动制作不允许您在LDADD中设置选项。不幸的是,您不能写:
(Makefile.am)
mytarget_LDADD = -Wl,--whole-archive -lpthread -Wl,--no-whole-archive

解决方法是,可以通过在configure.ac中定义标志并像这样使用它们来避免检查:
(configure.ac)
WL_WHOLE_ARCHIVE_HACK="-Wl,--whole-archive"
WL_NO_WHOLE_ARCHIVE_HACK="-Wl,--no-whole-archive"
AC_SUBST(WL_WHOLE_ARCHIVE_HACK)
AC_SUBST(WL_NO_WHOLE_ARCHIVE_HACK)

(Makefile.am)
mytarget_LDADD = @WL_WHOLE_ARCHIVE_HACK@ -lpthread @WL_NO_WHOLE_ARCHIVE_HACK@

关于c++ - 当g++静态链接pthread时,导致段错误,为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35116327/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com