Использование многопоточности С++ 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
))
В некоторых библиотеках есть больше зависимостей (которые не связаны с потоком), но, похоже, не существует каких-либо "конфликтных" зависимостей (нет зависимостей от разных версий/путей одной и той же библиотеки в любом из этих библиотеки).
Ответы
Ответ 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;