#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 ++代码。
编译:{{1}}
g++ -pthread -o one one.cpp -Wall -std=c++11 -O3
,gots:
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
,gots:
ldd one
但是当我运行它时,有些事情出错......
not a dynamic executable
使用test start
Segmentation fault (core dumped)
重新编译,gdb显示:
-g
为什么会这样?
更新 的 ==============================
使用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)
库(提升版本:1.60),
将boost::thread
替换为std::thread
,并建立静态链接
boost::thread
没有问题!
...混淆
答案 0 :(得分:37)
首先,解决方案。这将是有效的:
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。) / p>
因此,如果-pthread
暗示-lpthread
,为什么在静态链接时需要指定-lpthread
? Wl,--whole-archive
做了什么?
在Unix上,使用ELF文件格式,其格式为weak and strong symbols。引用Wikipedia page:
默认情况下,如果没有任何注释,目标文件中的符号强。在链接期间,强符号可以覆盖同名的弱符号。相反,共享名称的两个强符号在链接时会产生链接错误。
动态和静态库存在细微差别。在静态库中,链接器将停在第一个符号,即使它是一个弱符号,并停止寻找强符号。要强制它查看所有符号(就像它对动态链接库所做的那样),ld
支持--whole-archive
选项。
引用man ld
:
--whole-archive:
对于--whole-archive选项后命令行中提到的每个存档,包括存档中的每个目标文件 链接,而不是在存档中搜索所需的目标文件。这通常用于将存档文件转换为 共享库,强制每个对象都包含在生成的共享库中。此选项可能会被多次使用。
继续从gcc解释,您必须将选项作为-Wl,--whole-archive
传递:
从gcc使用此选项时的两个注意事项:首先,gcc不了解此选项,因此您必须使用-Wl,-whole-archive。 其次,不要忘记在档案列表之后使用-Wl,-no-whole-archive,因为gcc会将自己的档案列表添加到 您的链接,您可能不希望此标志也影响这些。
它再次解释了如何关闭它:
--no-whole-archive:
关闭后续存档文件的--whole-archive选项的效果。
弱符号的一个用例是能够用优化的符号交换实现。另一种方法是使用存根,以后可以在必要时进行更换。
例如,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)
&#34; W&#34;代表&#34;弱&#34;,因此静态链接的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.6
和libpthread.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)。我见过的最极端的例子是个人努力工作一段时间才能使其发挥作用{/ 3}}。
如果您使用autotools作为构建系统,则需要一种解决方法,因为automake不允许您在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@
答案 1 :(得分:2)
在链接使用pthread的预构建C ++ .a
归档文件时,我遇到了类似的问题。就我而言,除了-Wl,--whole-archive -lpthread -Wl,--no-whole-archive
,我还需要为每个弱符号做-Wl,-u,...
。
我的症状是在运行时崩溃,当使用gdb进行反汇编时,我可以看到崩溃只是在callq 0x0
之后,这似乎很可疑。进行了一些搜索,发现其他人已经通过静态pthread链接看到了这一点。
我找出了要使用nm
强制解析的符号,并寻找w
符号。链接之后,我可以看到callq 0x0
指令已使用各种符号地址更新为pthread_mutex_lock
等。