当g ++静态链接pthread时,会导致Segmentation错误,为什么? [英] when g++ static link pthread, cause Segmentation fault, why?

查看:579
本文介绍了当g ++静态链接pthread时,会导致Segmentation错误,为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  #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<< 测试开始......;
std :: map< int,A *>容器;
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<< 测试版本\ n;
for(int i = 0; i< AMOUNT; i ++)
{
auto iter = container.find(i);
删除iter->秒;
container.erase(iter);
}
std :: cout<< 测试结束......;
}

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


{
x.join();
}

返回0;
}

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



用以下语句编译: g ++ -pthread -o one one.cpp -Wall -std = c ++ 11 -O3



ldd one ,gots:

  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)

运行 ./一个,一切正常。



然后我尝试一个静态链接: g ++ -pthread -o one one.cpp -Wall -std = c ++ 11 -O3 -static



ldd one ,gots:

 不是动态可执行文件

但是当我运行它时,出现了一些问题......

 测试开始
分段错误(核心转储)

-g 重新编译,gdb显示:

  wang [00:35] [〜/ test] $ gdb one 
GNU gdb(Ubuntu 7.10-1ubuntu2)7.10
版权所有(C)2015 Free Software Foundation,Inc.
许可证GPLv3 +:GNU GPL版本3或更高版本< http://gnu.org/licenses/gpl.html>
这是免费软件:您可以自由更改和重新分配它。
在法律允许的范围内,不存在任何担保。有关详细信息,请键入显示复制
和显示保修。
这个GDB被配置为x86_64-linux-gnu。
输入显示配置以获取配置详细信息。
有关错误报告的说明,请参阅:
< http://www.gnu.org/software/gdb/bugs/> ;.
在线查找GDB手册和其他文档资源:
< http://www.gnu.org/software/gdb/documentation/> ;.
如需帮助,请输入help。
键入apropos word以搜索与word相关的命令...
从一个...中读取符号...完成。
(gdb)运行
启动程序:/ home / wang / test / one
[使用libthread_db启用线程调试]
使用主机libthread_db库/ lib / x86_64-linux- GNU /的libthread_db.so.1\" 。
[新线程0x7ffff7ffa700(LWP 3623)]
测试开始
[新线程0x7ffff77f8700(LWP 3624)]
测试开始
[新线程0x7ffff6ff7700(LWP 3625) ]
测试开始
[新线程0x7ffff67f6700(LWP 3626)]
测试开始

编程接收到的信号SIGSEGV,分段错误。
0x0000000000000000在?? ()
(gdb)

为什么这么做?

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

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

使用

boost :: thread 替换 std :: 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



没有问题发生!



困惑...

解决方案

首先,解决方案。这里有效:

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

使用 -pthread 时,编译器已经链接到pthread(并且根据平台,它确实定义了额外的宏,如 -D_REENTRANT ,详情请参阅此问题)。



因此,如果 -pthread 隐含 -lpthread ,为什么需要指定 -lpthread 当您静态链接时? $ b

Wl, - whole-archive 是做什么的?$ b $理解弱符号



在Unix上,使用了 ELF 文件格式,它具有弱和强符号的概念。引用维基百科页面


默认情况下,没有任何注释,目标文件中的符号。在链接过程中,强符号可以覆盖同名的符号。相比之下,两个强名称共享一个名称会在链接时产生链接错误。


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



引用 man ld


--whole-archive:
对于在命令行中提到的--whole-archive选项后面提到的每个存档,请将存档中的每个目标文件包含在
链接中比在档案中搜索所需的目标文件。这通常用于将存档文件转换为
共享库,强制每个对象包含在生成的共享库中。这个选项可能会被多次使用。

继续解释gcc需要将选项作为 -Wl, - whole-archive
$ b


使用gcc中的这个选项时需要注意两点:首先,gcc不知道这个选项,所以你必须使用-Wl,-whole-archive。
其次,不要忘记在归档列表后面使用-Wl,-no-whole-archive,因为gcc会将自己的归档列表添加到
链接中,并且您可能不希望使用此标志也影响这些人。


并解释了如何关闭它:


- no-whole-archive:
关闭--whole-archive选项的作用后续的档案文件。




pthread和libstdc ++中的弱符号



其中一个用例弱符号可以用优化的符号替换实现。

例如, fputc printf 概念性地使用)是POSIX需要的线程安全和需求要同步,这是昂贵的。在单线程环境中,您不想支付成本。因此,实现可以将同步函数实现为空存根,并将函数声明为弱符号。后来,如果多线程库已链接(例如,pthread) ,很明显,单线程支持并不打算。链接多线程库时,链接器可以用真正的同步函数(定义为强符号并由线程库实现)替换存根。另一方面,如果没有链接多线程库,可执行文件将使用存根作为同步函数。



glibc(提供 fputc )和pthreads似乎完全使用这个技巧。有关详细信息,请参阅此关于在glibc中使用弱符号的问题。以上示例摘自此答案



nm 允许您详细查看它,这与上面引用的答案看起来一致:

  $ nm /usr/lib/libc.a 2> / dev / null | grep pthread_mutex_lock 
w __pthread_mutex_lock
...(重复)

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
pre>

返回示例程序

通过查看动态链接可执行文件的共享库依赖关系,我在我的机器上获得几乎相同的 ldd 输出:

  $ ldd一
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 :: _ St​​ate>> ;, void(*)())( 0x7ffdc483c990,0x7ffdc483c998,0x7fab2fa52320,0x7fab2fa43a80)= 0
operator new(unsigned long)(16,0x7fab2f6a1fb0,0,0x800000)= 0x563ab918bd70
std :: thread :: _ M_start_thread(std :: unique_ptr operator new(unsigned long)(16,0x7fab2eea0fb0,0,0x800000)= 0x563ab918bec0
std :: thread :: _ M_start_thread(std :: unique_ptr >,void(*)())(0x7ffdc483c990,0x7ffdc483c998,0x7fab2fa52320,0x7fab2fa43a80test start
)= 0
operator new(unsigned long)(16,0x7fab2e69ffb0,0,0x800000)= 0x563ab918c010
std :: thread :: _ M_start_thread(std :: unique_ptr test start
)= 0
std :: thread :: join()(0x7ffdc483c9a0,0x7fab2de9efb0,0,0x800000test start
测试版
测试版
测试版
测试版
测试结束
测试结束
测试结束
测试结束
)= 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 的原因。



找出它有点反复试验。至少,我没有找到有关该主题的明确文件。我认为这是因为 Linux上的静态链接已成为一种边缘案例,而动态链接通常是关于要使用库(有关比较,请参阅静态链接vs动态链接)。我见过的最极端的例子,并且一直在努力工作,以便静态链接TBB



附录:Autotools的解决方法



如果您使用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 @


#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;
}

Above is a simple c++ code.

compile with: g++ -pthread -o one one.cpp -Wall -std=c++11 -O3

ldd one, gots:

    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)

run ./one, every thing is ok.

Then I try a static link: g++ -pthread -o one one.cpp -Wall -std=c++11 -O3 -static

ldd one, gots:

    not a dynamic executable

But when I run it, some thing goes wrong...

test start
Segmentation fault (core dumped)

re-compile with -g, and the gdb shows:

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) 

Why this ?

UPDATE ==============================

using boost::thread library (boost version: 1.60),

replace std::thread with boost::thread , and make a static link,

g++ -pthread -o one1 one.cpp -Wall -std=c++11 -O3 -I /opt/boost/include/ -L /opt/boost/lib/ -lboost_system -lboost_thread -static

no problem occurred!

confused...

解决方案

First, the solution. This here will work:

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

When you use -pthread, the compiler will already link against pthread (and depending on the platform, it does define extra macros like -D_REENTRANT, see this question for more details).

So if -pthread implies -lpthread, why do you need you specify -lpthread when you are linking statically? And what does Wl,--whole-archive do?

Understanding weak symbols

On Unix, the ELF file format is used, which has the concept of weak and strong symbols. To quote from the 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.

There is a subtle difference when it comes to dynamic and static libraries. In static libraries, the linker will stop at the first symbol, even if it is a weak one, and stops looking for strong ones. To force it to look at all symbols (like it would have done for a dynamically linked library), ld supports the --whole-archive option.

To quote from 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.

It goes on by explaining that from gcc, you have to pass the option as -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.

And it explains how to turn it off, again:

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

Weak symbols in pthread and libstdc++

One of the use cases of weak symbols is to be able to swap out implementations with optimized ones. Another is to use stubs, which can later to replaced if necessary.

For example, fputc (conceptionally used by printf) is required by POSIX to be thread-safe and needs to be synchronized, which is costly. In a single-threaded environment, you do not want to pay the costs. An implementation could therefore implement the synchronization functions as empty stubs, and declare the functions as weak symbols.

Later, if a multi-threading library is linked (e.g., pthread), it becomes obvious that single-thread support is not intended. When linking the multi-threading library, the linker can then replace the stubs by the real synchronization functions (defined as strong symbols and implemented by the threading-library). On the other hand, if no multi-threading library is linked, the executable will use the stubs for the synchronization function.

glibc (providing fputc) and pthreads seem to use exactly this trick. For details, refer to this question about the usage of weak symbols in glibc. The example above is taken from this answer.

nm allows you to look at it in detail, which seems consistent with the cited answer above:

$ nm /usr/lib/libc.a 2>/dev/null | grep pthread_mutex_lock
w __pthread_mutex_lock
... (repeats)

"w" stands for "weak", so the statically linked libc library contains __pthread_mutex_lock as a weak symbol. The statically linked pthread library contains it as a strong symbol:

$ 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

Back to the example program

By looking at the shared library dependencies of the dynamically linked executable, I get almost the same output of ldd on my machine:

$ 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)

Printing out the library calls with ltrace, leads to the following output:

$ 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) +++

As an example, std::thread::join is called, which will most likely use pthread_join internally. That symbol can be found in the (dynamically linked) libraries listed in the ldd ouput, namely in libstdc++.so.6 and 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

In the dynamically linked executable, the linker will replace weak symbols by strong symbols. In this example, we have to enforce the same semantic for the statically linked libraries. That is why -Wl,--whole-archive -lpthread -Wl,--no-whole-archive is needed.

Finding it out is a bit trial-and-error. At least, I found no clear documentation on that subject. I assume it is because static linking on Linux has become rather an edge case, whereas dynamic linking is often the canonical approach on how to use the libraries (for a comparison, see Static linking vs dynamic linking). The most extreme example that I have seen and personally struggled with a while to get it working is to link TBB statically.

Appendix: Workaround for Autotools

If you are using autotools as a build system, you need a workaround, as automake does not let you set options in the in LDADD. Unfortunately, you cannot write:

(Makefile.am)
mytarget_LDADD = -Wl,--whole-archive -lpthread -Wl,--no-whole-archive

As a workaround, you can avoid the check by defining the flags in configure.ac, and using them like this:

(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@

这篇关于当g ++静态链接pthread时,会导致Segmentation错误,为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆