использование std :: thread в разделяемой библиотеке вызывает SIGSEGV - PullRequest
2 голосов
/ 17 апреля 2020

Я только что пришел к Linux программированию на c ++ с Windows. Попытка сделать общую библиотеку libso.so , которая использует std::thread. Общая библиотека будет загружена другими людьми и вызовет функцию экспорта. Тестовый код:

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

Сбой в функции run(), вывод:

run() begin
“./test” terminated by signal SIGSEGV (Address boundary error)

Кажется, что std::thread отлично работает в исполняемом файле , но не в общей библиотеке. Что мне не хватает?

Среда: g ++ 9.3.0, cmake 3.16.3

Отредактировано:

Попробуйте ldd.

ldd ./test показывает нет pthread, но ldd ./libso.so имеет libpthread.so.0. Разница в связывании параметров с 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

Единственная разница - -fPIC, я погуглил и добавил set_property(TARGET test PROPERTY POSITION_INDEPENDENT_CODE ON) к исполняемому файлу, но ничего не изменилось.

Обходной путь 1

Так как .so имеет libpthread.so.0, я попытался добавить код в .so к исполняемому файлу:

int main() {
    std::thread t([]{}); // to make executable linking to `pthread`
    t.join();

    // ... load dll and call run function
}

И это работает, теперь ldd ./test показывает libpthread.so.0, а не cra * sh. Это означает: если разделяемая библиотека использует std::thread и исполняемый файл хочет ее загрузить, сам исполняемый файл также должен использовать std::thread.

Обходной путь 2 :

std::thread отлично работает в исполняемом файле, но вылетает в разделяемой библиотеке. Найдены некоторые связанные обсуждения , обход использует boost::thread вместо std::thread и связывается с библиотекой boost_thread, без cra * sh.

1 Ответ

3 голосов
/ 17 апреля 2020

Я полагаю, что проблема больше связана с динамическим связыванием c, чем с потоками.

Вызов dlopen("libso.so", RTLD_LAZY) попытается найти библиотеку в стандартном месте.
За исключением случаев, когда вы установите LD_LIBRARY_PATH переменная окружения, включающая . (текущий каталог), эта библиотека не найдена.

Для простого теста вы можете:

  • использовать export LD_LIBRARY_PATH=. в терминале перед запуском вашей программы,
  • используйте dlopen("./libso.so", RTLD_LAZY) в вашем исходном коде.

После использования dlopen() или dlsym(), если вы получите нулевой указатель, тогда dlerror() может помочь отобразить причину сбоя.

Обратите внимание, что в Windows текущий каталог и путь к исполняемому файлу являются стандартными путями поиска для динамических c библиотек; на UNIX это не тот случай, который может удивить при смене целевой платформы.


edit

cmake использует -Wl,-rpath возможность жестко закодировать путь поиска библиотеки в исполняемом файле, поэтому все, что я объяснил выше, становится бесполезным для этой проблемы.

Если предположить, что найдена библиотека dynamici c, единственный способ воспроизвести cra sh должен забыть pthread в target_link_libraries для test.


второе редактирование

Мне наконец удалось воспроизвести the cra sh с Ubuntu (в WSL).
Очевидно, ваш компоновщик решает игнорировать библиотеки, которые непосредственно не используются исполняемым файлом.
Такое поведение предполагает, что опция компоновщика --as-needed включена по умолчанию .
Чтобы противоречить этому поведению по умолчанию, вам нужно передать параметр компоновщика --no-as-needed перед -lpthread.
Таким образом, вам не нужно вставлять dummy поток в вашем исполняемом файле.
Использование set(CMAKE_CXX_FLAGS -Wl,--no-as-needed) в файле CMakeLists.txt. ты обеспечил мне трюк.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...