When I try to compile the following program
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
// This function will be called from a thread
void call_from_thread()
{
std::cout << "Threaded: Hello, World" << std::endl;
}
int main()
{
std::cout << "Hello World" << std::endl;
std::thread t1(call_from_thread);
t1.join();
std::cout << "Goodbye World" << std::endl;
}
with this call
pi@raspberrypi:/tmp $ arm-linux-gnueabihf-g++ -g -Wall -O0 -ggdb -Wl,-z,nocopyreloc segfault_check.cpp -o segv_test -lpthread
It will compile without a warning. In this case the target machine is a raspberry compute module 3 running rapbian stretch and gcc 6.3.0. Trying to run it will result in an immediate segfault before even reaching the main entry point.
GNU gdb (Raspbian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 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 "arm-linux-gnueabihf".
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 segv_test...done.
(gdb) run
Starting program: /tmp/segv_test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0 0x00000000 in ?? ()
#1 0x76e99148 in __dynamic_cast () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#2 0x76f105f4 in bool std::has_facet<std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > > >(std::locale const&) () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#3 0x76f02f94 in std::basic_ios<char, std::char_traits<char> >::_M_cache_locale(std::locale const&) () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#4 0x76f030c0 in std::basic_ios<char, std::char_traits<char> >::init(std::basic_streambuf<char, std::char_traits<char> >*) () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#5 0x76eab684 in std::ios_base::Init::Init() () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#6 0x00010de8 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at /usr/include/c++/6/iostream:74
#7 0x00010e28 in _GLOBAL__sub_I__Z16call_from_threadv () at segfault_check.cpp:44
#8 0x00011db4 in __libc_csu_init ()
#9 0x76c5460c in __libc_start_main (main=0x7efff214, argc=1993838592, argv=0x76c5460c <__libc_start_main+168>, init=0x11d68 <__libc_csu_init>, fini=0x11dc8 <__libc_csu_fini>, rtld_fini=0x76fdf9b8 <_dl_fini>, stack_end=0x7efff214)
at libc-start.c:247
#10 0x00010a78 in _start ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)
However, if I drop the nocopyreloc
directive everything seems to work:
pi@raspberrypi:/tmp $ arm-linux-gnueabihf-g++ -g -Wall -O0 -ggdb segfault_check.cpp -o segv_test -lpthread
pi@raspberrypi:/tmp $ ./segv_test
Hello World
Threaded: Hello, World
Goodbye World
Until today I didn't know about the nocopyreloc
option. It slipped in via the CMake default CMAKE_EXE_LINKER_FLAGS setting Toolchain file which we use for cross compilation. The binutils documentation states:
nocopyreloc disables production of copy relocs.
which doesn't explain much.
When I compile the program on my dev machine (linux x86_64) with g++ 7.2.0, I get the following error message
./segv_test: Symbol `_ZSt4cout' causes overflow in R_X86_64_PC32 relocation
./segv_test: Symbol `_ZSt4cout' causes overflow in R_X86_64_PC32 relocation
./segv_test: Symbol `_ZSt4cout' causes overflow in R_X86_64_PC32 relocation
./segv_test: Symbol `_ZTVNSt6thread6_StateE' causes overflow in R_X86_64_PC32 relocation
Segmentation fault (core dumped)
That seems to hint at the cause of the problem. Apparently the loader can't perform the necessary relocations, but I don't really understand the details of the process well enough.
I wonder wether I can safely drop the nocopyreloc
option, or if I need to build my libs differently or both? or what?
nocopyreloc was designed for the i386 abi where relocs from a dynamic library were also copied as relocs in the main executable. At the time, it was the only abi doing this. Copying the relocs was not useful at all and was causing problem in corner cases, see here for test cases: https://marc.info/?l=binutils&m=149887877340522&w=2 . The flag nocopyrelocs suppresses this behavior. I am not sure what it is doing on non i386 abis, but it seems safe to drop this flag.
User contributions licensed under CC BY-SA 3.0