Подтвердить что ты не робот

Использование многопоточности С++ 11 в общей библиотеке, загруженной программой без поддержки потоков

В настоящее время я пытаюсь использовать многопоточность С++ 11 в общей библиотеке, которая загружается в основную программу (написанную на C) в Linux. Это часть большой симуляционной программы, и я ничего не могу изменить о загрузке библиотеки или изменении основной программы в целом.

Основная программа скомпилирована с gcc 4.1.2, и у меня нет источников для нее (я не могу перекомпилировать ее с помощью gcc 4.8.2).

Общая библиотека скомпилирована с помощью gcc 4.8.2, чтобы использовать многопоточность С++ 11. Я передаю команды компилятора

-pthread -lpthread -std=c++11

как описано в Каковы правильные параметры ссылок для использования std:: thread в GCC под Linux?

Компиляция автономной тестовой программы с этой конфигурацией ( "-pthread -std=c++11" и gcc 4.8) корректно работает в моей системе. Но когда я запускаю загрузку программы в общую библиотеку, я получаю исключение:

Caught std::exception!
Exception Message: Enable multithreading to use std::thread: Operation not permitted

Terminating...

Использование -pthread и -lpthread ( Изменить:, а также только -pthread без -lpthread) параметр компиляции не работает. Аргументы компилятора (я использую систему сборки повара):

-pthread -std=c++11 -fmessage-length=0 -fPIC -Wchar-subscripts ...(lots of -W* here)
... -Wunused-variable -m64 -D__64BIT__ -pthread -lpthread

и аргументы компоновщика (дублирующие параметры из-за системы сборки):

-pthread -lpthread -std=c++11 -pthread -lpthread -std=c++11 -shared -fPIC -Wl,-Bsymbolic -Wl,--allow-shlib-undefined -pthread -lpthread

вызов ldd в моей библиотеке дает следующий вывод

$ ldd calc3/build/amd64_linux26_RH5/library.so
    linux-vdso.so.1 =>  (0x00007fff4d1fd000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00002ae6ec124000)
    libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ae6ec340000)
    libm.so.6 => /lib64/libm.so.6 (0x00002ae6ec655000)
    libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ae6ec8d8000)
    libc.so.6 => /lib64/libc.so.6 (0x00002ae6ecaef000)
    /lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)

и в основной программе

$ ldd .../bin-64/main_program
    linux-vdso.so.1 =>  (0x00007fff64595000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00000032cc000000)
    libz.so.1 => /usr/lib64/libz.so.1 (0x00000032cc800000)
    libc.so.6 => /lib64/libc.so.6 (0x00000032cb800000)
    /lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)

Библиотека pthread связана с моей общей библиотекой, но не с основной программой. В этом ответе говорится, что вам нужно связать pthreads с основной программой, но второй комментарий к этому ответу (by @R..) говорит, что это необязательно (что звучит логически).

К сожалению, я ничего не знаю о механике загрузки всей системы, за исключением того, что моя библиотека использует другую библиотеку С++ как API.

Обратите внимание, что другие функции С++ 11 работают (и libstdС++, поэтому в зависимостях моей библиотеки), но многопоточность С++ 11 не является (хотя libpthread.so также находится в зависимостях моей библиотеки).

Использование класса потоковой передачи из библиотеки, содержащейся в самой программе, работает (и этот класс потоков также использует pthreads).

Я также пытался использовать -fabi-version=0 или -fabi-version=2, потому что основная программа скомпилирована с помощью gcc 4.1.2 с моей библиотекой, но ничего не изменила.

Есть ли что-нибудь, что я упустил или вариант компилятора, который я могу использовать, чтобы заставить его работать? Или это кажется проблемой моей программной среды? Любые идеи приветствуются.

Edit:

Я попытался использовать -Wl,-no-as-needed (как было предложено в комментариях), но, к сожалению, ничего не изменил.

Использование clang 3.5 вместо gcc 4.8 также не помогло.

Создание небольшого тестового приложения, которое загружает общую библиотеку (например, в ответе ниже @chill), работает (даже без флага компилятора), если я использую gcc 4.8 или clang 3.5 для основного приложения и общей библиотеки. При использовании gcc 4.1 для основной программы, однако, основная программа даже не загружает библиотеку (которая работает в моем "реальном" приложении). Я думаю, что может возникнуть проблема с отличительными ABI компиляторов.

Использование pthreads непосредственно из pthread.h, похоже, работает (хотя программа в настоящее время завершается на pthread_join без сообщения об ошибке, но я все еще там тестирую...)

Изменить 2:

Запуск "тестовой программы" с помощью LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH (потому что там тоже должны быть пути библиотеки gcc 4.8, спасибо @MvG) запустил программу, но снова с ошибкой Enable multithreading to use std::thread: Operation not permitted.

Я проверил все остальные загружаемые библиотеки (нашел их с strace ./main_program 2>&1 | grep '^open(".*\.so"' [см. здесь]) и проверил их все с помощью ldd. Все они зависят от тех же библиотек (с теми же путями). ldd выходы (для всех):

linux-vdso.so.1 =>  (0x00007fff4d3fd000)
libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ade28774000)
libm.so.6 => /lib64/libm.so.6 (0x00002ade28ab0000)
libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ade28d33000)
libc.so.6 => /lib64/libc.so.6 (0x00002ade28f49000)
/lib64/ld-linux-x86-64.so.2 (0x00000032ea200000)

(что все они не зависят от libpthread.so.0, кроме моей библиотеки и другого (но это то же самое /lib64/libpthread.so.0))

В некоторых библиотеках есть больше зависимостей (которые не связаны с потоком), но, похоже, не существует каких-либо "конфликтных" зависимостей (нет зависимостей от разных версий/путей одной и той же библиотеки в любом из этих библиотеки).

4b9b3361

Ответ 1

В thread.cc вы можете прочитать, что это исключение генерируется, если __gthread_active_p возвращает false. Этот вызов просто проверяет, доступен ли данный символ. Символ, о котором идет речь, является слабым символом: он не обязательно должен присутствовать, но его присутствие проверяется, чтобы определить, поддерживается ли потоковая передача.

Но что означает наличие символа? В этом случае это означает, что символ находится в списке таблиц символов, которые рассматриваемая библиотека (libgcc_s.so.1 в моем случае) ищет определения символов. Это включает в себя символы, экспортируемые самим приложением, а также символы, экспортируемые всеми загруженными перед ним библиотеками. Однако он не включает библиотеки, загруженные впоследствии. К сожалению, если libgcc загружается до libpthread, то этот символ недоступен в его поисковом домене. Поэтому он сообщает, что потоки не поддерживаются. Я думаю, у вас есть еще один модуль С++, загруженный до многопоточного, поэтому вы столкнулись с этой проблемой.

Одно из решений, которое работает в моем примере воспроизведения, устанавливает LD_PRELOAD=/lib64/libpthread.so.0 в среде, используемой для вызова двоичного файла. Это загружает libpthread спереди, поэтому его символы доступны для удовлетворения слабой связи символов. Это не будет работать для двоичных файлов setuid/setgid, и в других случаях их можно считать уродливым взломом, поэтому я заинтересован в более чистых решениях. Тем не менее, эта работа выполняется большую часть времени.

Ответ 2

Я смог воспроизвести что-то очень похожее на вашу ситуацию.

Сначала источник тестовой общей библиотеки:

#include <thread>

void f() {}

extern "C" int foo () {
        std::thread(f).join();
        return 0;
}

Скомпилировано/связано с c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so

Затем "основная программа":

#include <dlfcn.h>

int
main () {
    void *lib = dlopen ("libthrlib.so", RTLD_NOW);

    int (*foo)();
    foo = (int (*)()) dlsym (lib, "foo");
    foo ();
}

Скомпилировано/связано с gcc main.c -ldl

Результат выполнения:

[email protected]:~/tmp$ LD_LIBRARY_PATH=`pwd` ./a.out 
terminate called after throwing an instance of 'std::system_error'
  what():  Enable multithreading to use std::thread: Operation not permitted
Aborted (core dumped)

Теперь создайте общую библиотеку, добавив в параметры командной строки -Wl,-no-as-needed

c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so -Wl,-no-as-needed

И основная программа выполняется без ошибок.

Я запускаю это на Ubuntu 13.10 с gcc 4.8.1, все вышеперечисленное может фактически не применимо к вашей среде, но эффекты очень похожи на ваши, так что есть хороший шанс то же решение будет работать для вас, стоит попробовать в любом случае:)

PS. Еще одна вещь, которую стоит попробовать, добавить явную жесткую ссылку на pthread_cancel в вашей библиотеке:

#include <pthread.h>
void *zzz = (void *) pthread_cancel;