#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:
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 doesWl,--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:
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
:It goes on by explaining that from gcc, you have to pass the option as
-Wl,--whole-archive
:And it explains how to turn it off, again:
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 byprintf
) 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:
"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: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:Printing out the library calls with ltrace, leads to the following output:
As an example,
std::thread::join
is called, which will most likely usepthread_join
internally. That symbol can be found in the (dynamically linked) libraries listed in theldd
ouput, namely inlibstdc++.so.6
andlibpthread.so.0
: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:
As a workaround, you can avoid the check by defining the flags in configure.ac, and using them like this: