using std::thread in shared library causes SIGSEGV - c++

I just came to Linux c++ programming from Windows. Trying to make a shared library libso.so, which uses std::thread. The shared library will be loaded by other people and call the export function. The test code:
// so.cpp, the .so library
#include <iostream>
#include <thread>
using namespace std;
extern "C"
void run() {
cout << "run() begin" << endl;
std::thread t([] {
});
t.join();
cout << "run() end" << endl;
}
// test.cpp, the test loader
#include <dlfcn.h>
int main() {
typedef void (*run_t)();
auto dll = dlopen("libso.so", RTLD_LAZY);
run_t run = (run_t) dlsym(dll, "run");
run();
}
// The CMakeLists.txt file
cmake_minimum_required(VERSION 3.0)
PROJECT (test)
Include_Directories(${PROJECT_SOURCE_DIR})
Link_Directories(${PROJECT_BINARY_DIR})
add_library(so SHARED so.cpp )
target_link_libraries(so pthread)
add_executable( test test.cpp )
target_link_libraries(test pthread dl)
It crashes in the run() function, the output is:
run() begin
“./test” terminated by signal SIGSEGV (Address boundary error)
The std::thread seems work fine in executable, but not in shared library. What do I miss?
The environment: g++ 9.3.0, cmake 3.16.3
Edited:
Try ldd.
ldd ./test shows no pthread, but ldd ./libso.so has libpthread.so.0.
The difference of linking param with SET(CMAKE_VERBOSE_MAKEFILE TRUE)
// linking executable 'test'
/usr/bin/c++ -rdynamic CMakeFiles/test.dir/test.cpp.o -o test -L/e/c/1/kali -Wl,-rpath,/e/c/1/kali -ldl -lpthread
// linking library 'libso.so'
/usr/bin/c++ -fPIC -shared -Wl,-soname,libso.so -o libso.so CMakeFiles/so.dir/so.cpp.o -L/e/c/2/kali -Wl,-rpath,/e/c/1/kali -lpthread
The only difference is -fPIC, I googled and add set_property(TARGET test PROPERTY POSITION_INDEPENDENT_CODE ON) to the executable, but nothing changed.
Workaround 1
Since the .so has libpthread.so.0, I tried to add the code in .so to the executable:
int main() {
std::thread t([]{}); // to make executable linking to `pthread`
t.join();
// ... load dll and call run function
}
And it works, now the ldd ./test shows libpthread.so.0 and no crash. Which means: if a shared library uses std::thread and an executable wants to load it, the executable itself must also use std::thread.
Workaround 2:
The std::thread works fine in executable, but crashes in shared library. Found some related discuss, the walkaround is using boost::thread instead of std::thread and linking to boost_thread library, no crash .

I guess the problem is more related to dynamic linking
than threads.
The call dlopen("libso.so", RTLD_LAZY) will try to
find the library in a standard location.
Except if you set the LD_LIBRARY_PATH environment
variable to something that includes . (the current
directory) this library won't be found.
For a simple test you can either:
use export LD_LIBRARY_PATH=. in the terminal before
launching your program,
use dlopen("./libso.so", RTLD_LAZY) in your source code.
After using dlopen() or dlsym() if you obtain a null
pointer, then dlerror() can help displaying the reason
of the failure.
Note that on Windows the current directory and the executable
path are standard search paths for dynamic libraries; on UNIX
this is not the case, which could be surprising when changing
the target platform.
edit
cmake uses the -Wl,-rpath option to hardcode a library search
path in the executable, so all of what I explained above becomes
useless for this problem.
Assuming the dynamic library is found, the only way I can reproduce
the crash is to forget pthread in target_link_libraries for
test.
second edit
I finally managed to reproduce the crash with Ubuntu (in WSL).
Apparently your linker decides to ignore the libraries that are
not directly used by the executable.
This behavior suggests that the linker option --as-needed is
switched on by default.
To contradict this default behaviour, you need to pass the linker
option --no-as-needed before -lpthread.
This way, you don't have to insert a dummy thread in your
executable.
Using set(CMAKE_CXX_FLAGS -Wl,--no-as-needed) in the CMakeLists.txt
file you provide did the trick for me.

Related

How to link to shared object library in msys2?

I have some c++ code in msys2 that I am trying to link dynamically to show how a dynamic link library works.
In linux, showing the call is no problem. stepping in gdb, we can watch the call go through the jump vector, eventually landing in the desired function.
But in msys2, they wanted to eliminated dlls and all the libraries I can find are .dll.a, I think they are really static libraries.
I build a trivial little function like this:
#include <cstdint>
extern "C" {
uint64_t f(uint64_t a, uint64_t b) {
return a + b;
}
}
compiling in the makefile with:
g++ -g -fPIC -c lib1.cc
g++ -g -shared lib1.o -o libtest1.so
When I run the file utility, it says that:
libtest1.so: PE32+ executable (DLL) (console) x86-64, for MS Windows
When I compile the code using it:
g++ -g main.cc -ltest1 -o prog
The error is -ltest1 no such file or directory.
MinGW uses the .dll extension for shared libraries, not .so.
lib??.dll.a is an import library, a shim that will load the corresponding lib??.dll at runtime.
At some point in time, you couldn't link .dlls directly, and had to link .dll.as instead. The modern MinGW can link .dlls directly, so you shouldn't need to use import libraries anymore.
-ltest1 no such file or directory
Wouldn't you get the same error on Linux? You have to specify the library search path with -L. -ltest1 needs either libtest1.a or libtest1.dll.a or libtest1.dll (perhaps some other variants are checked too).
The reason your linker cannot find the library is because your current working directory is not in the search path for libraries. Add -L. to your linking command.
It is untrue that MSYS2 "wanted to eliminate DLLs". Just run ls /mingw64/bin/*.dll and you will see plenty of DLLs (assuming you have some MINGW64 packages installed). The .dll.a files used for linking are called import libraries.

Interaction of fno-exceptions and POSITION INDEPENDENT CODE

I ran into a rather bizarre issue while building a dynamic library. Here are details with a small example:
A simple file called static.h whose contents are:
#pragma once
#include <string>
std::string static_speak();
static.cpp looks like this:
#include "static.h"
std::string static_speak() {
return "I am static";
}
one can build a static library with these two files (using cmake) as:
add_library(static
static.cpp
)
Now, consider another file called shared.cpp whose contents are:
#include "static.h"
std::string dynamic_speak() {
return static_speak() + " I am dynamic";
}
One can try to build a dynamic library (again using cmake) as:
add_library(shared SHARED
shared.cpp
)
target_link_libraries(shared PRIVATE
static
)
When one tries to build the above, one will run into the following error:
[4/4] Linking CXX shared library libshared.so
FAILED: libshared.so
: && /opt/vatic/bin/clang++ -fPIC -g -shared -Wl,-soname,libshared.so -o libshared.so CMakeFiles/shared.dir/shared.cpp.o libstatic.a && :
/usr/bin/ld: libstatic.a(static.cpp.o): relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
This makes sense. We didn't compile static with POSITION_INDEPENDENT_CODE. That is easily fixable via:
add_library(static
static.cpp
)
set_target_properties(static
PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
Everything works perfectly now when one compiles shared library.
Now here is the problem. Let's say I didn't enable POSITION_INDEPENDENT_CODE but instead disabled exceptions (!) in my code as:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
Now when I try to compile shared, everything still works!!
How are exceptions and fPIC related to each other?
Here is a repo to reproduce the issue:
https://github.com/skgbanga/shared
How are exceptions and fPIC related to each other?
They aren't (mostly).
What happens is that compiling with -fno-exceptions changes the relocations (certain exception-related data is no longer referenced), and because of that, there is no relocation which causes the error at link time.
You can confirm this by building with and without -fno-exceptions, and comparing output from objdump -dr CMakeFiles/static.dir/static.cpp.o.
P.S. I could not reproduce your problem using g++ (Debian 9.3.0-8), because it defaults to building with -fPIE, and both links succeed.
And if I add -fno-pie, then both links fail the same way.

cmake, can't link static library into shared one

I'm writing adapter (shared library) to some fpga API. I've got libsomelib.a and its API - somelibAPI.h. Here is a minimal example of my adapter:
somelib_adapter.h:
#include <string>
namespace details {
#include "somelibAPI.h"
}
class somelib_adapter {
public:
std::string foo();
};
somelib_adapter.cpp:
#include "somelib_adapter.h"
using namespace details;
std::string somelib_adapter::foo() {
char result[64];
somelibAPI_call(result);
return std::string(result);
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
project(untitled1)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES somelib_adapter.cpp somelib_adapter.h)
FIND_LIBRARY(SOMELIB_LIBRARIES
NAMES libsomelib.a
PATHS "${SRC_CPP_DIRECTORY}")
add_library(untitled1 SHARED ${SOURCE_FILES})
set(untitled1 -Wl,--whole-archive ${SOMELIB_LIBRARIES} -Wl,--no-whole-archive)
target_link_libraries(untitled1 some_other_shared_lib_used_by_somelib)
cmake finds libsomelib.a but when I'm trying to nm libuntitled1.so | c++filt it does not contain symbols location of somelibAPI_call. What's more, there is an undefined reference error with executable I made to test it. What may be wrong?
EDIT: libsomelib.a is compiled with -fPIC
EDIT2: I see I misunderstood some example. Now Ive got target_link_libraries(untitled1 -Wl,--whole-archive ${SOMELIB_LIBRARIES} -Wl,--no-whole-archive some_other_shared_lib_used_by_somelib) but there is another problem: /usr/bin/ld: ../libsomelib.a(somelibAPI.o): relocation R_X86_64_32 against .rodata.str1.1 can not be used when making a shared object; recompile with -fPIC
This is, in general, not possible, unless you recompile the static library.
The problem here is that dynamic libraries have to be generated with position independent code, so that they can be loaded dynamically into the address space of an existing process. That is, the compiler must generate the code in a specific way so that it is suitable for execution from a shared library.
Since we do not have this requirement for static libraries, a compiler is free to create position-dependent code there (which is exactly what happened in your case), which prohibits it from being linked into a shared library later. So for this to work, you need to change compilation of the static library to make it aware that it will be linked into a shared library later on. Only by recompiling the static library with the correct options can you make this work.
The exact way to set the correct build options of course depends on the build system used for building that static library. For example, for a static library built with CMake, you could change its build script so that it sets the POSITION_INDEPENDENT_CODE target property on the static library target.

undefined reference to glfwSetErrorCallback

I'm trying to use the GLFW library, but am having difficulty with compiling a simple program. I went to the GLFW website and download the latest release, then using "How to build & install GLFW 3 and use it in a Linux project" I built and installed it.
Here's my code:
#include <GLFW/glfw3.h>
#include <iostream>
using std::cout;
using std::endl;
void GLFW_error(int error, const char* description)
{
fputs(description, stderr);
}
void run()
{
cout << "pooch" << endl;
}
int main()
{
glfwSetErrorCallback(GLFW_error);
if (!glfwInit()) exit(EXIT_FAILURE);
run();
glfwTerminate();
return 0;
}
Using the command line:
bulletbill22#ROBOTRON ~/Desktop $ g++ -std=c++11 -lglfw source.cpp
yields
source.cpp:function main: error: undefined reference to 'glfwSetErrorCallback'
glfwSetErrorCallback is taken from their tutorial for "Setting an error callback".
Inclusion of -glfw3 results in /usr/bin/ld: error: cannot find -lglfw3
Even though everything seemed to be installed correctly, I suspect the problem may lie somewhere with the installation of the GLFW library because I'm not used to CMake and don't entirely understand how it works. I'm frustrated because the answer must be simple, but I'm not sure which keywords are really relevant when googling the problem; mostly the results are people who were incorrectly compiling with CMake, which I'm not compiling with in this case.
It seems that the directories for the glfw3.h header and libglfw3.so (and/or libglfw3.a) library are not in the default path.
You can check with by adding the -v option to the g++ options. Locate the directory where the glfw3.h header is found - call this $GLFW_INCDIR - it typically ends with .../GLFW. Locate the directory where the library is found - call this $GLFW_LIBDIR. Try:
g++ -std=c++11 -I$GLFW_INCDIR source.cpp -o pooch -L$GLFW_LIBDIR -lglfw3
If all the library dependencies are satisfied, this hopefully results in a program called pooch.
One other thing: GLFW3 is a C library, and the callback function arguments are expected to be C functions. So your callback should have 'C' linkage, i.e.,
extern "C" void GLFW_error(int error, const char* description) ...
Also, if you're having trouble with cmake, you may have ccmake installed. Try ccmake . in the top-level directory of the GLFW3 package for 'interactive' configuration.

xerces-c 2.8 : error while loading shared libraries

I'm trying to compile a program running on an HP UX server on a Red Hat Linux.
It uses xerces-c library to parse xml files. Compilation is ok, but when i try to run it, I get the following message
./a.out: error while loading shared
libraries: libxerces-c.so.28: cannot
open shared object file: No such file
or directory
I wrote a very simple program to try and understand whats going on:
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/parsers/SAXParser.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
int main(int argc, char* argv[])
{
return 0;
}
And compiled it like this:
g++ test.cpp
-L./xml/xerces-c_2_8_0/lib -lxerces-c -I./xml/xerces-c_2_8_0/include
Surprisingly the file is actually there:
lib]$ ls
libxerces-c.a libxerces-c.so.28 libxerces-depdom.a libxerces-depdom.so.28
libxerces-c.so libxerces-c.so.28.0 libxerces-depdom.so libxerces-depdom.so.28.0
Any thoughts ? I feel i'm missing something, but don't know what.
Thanks in advance.
run ldd a.out and see if the linker can resolve the right .so file
export LD_LIBRARY_PATH to include the current folder (in the same manner as the PATH variable) and check ldd again
the good way to do what you want is the following one:
g++ test.cpp -Xlinker -R ./xml/xerces-c_2_8_0/lib -lxerces-c -I./xml/xerces-c_2_8_0/include
or
g++ test.cpp -Wl,-rpath ./xml/xerces-c_2_8_0/lib -lxerces-c -I./xml/xerces-c_2_8_0/include
Xlinker or Wl options allow you to use specific linking options, you do not need to modifiy
LD_LIBRARY_PATH
You need to tell the runtime c library where to find the various symbols that arent compiled statically in your code and arent in the usualy /lib and /usr/lib locations.
You do this by adding the path to your shared library to LD_LIBRARY_PATH. In this case, this will be what you have been putting for the -L argument to the compiler.