typeinfo, общие библиотеки и dlopen () без RTLD_GLOBAL - PullRequest
10 голосов
/ 18 февраля 2011

У меня возникли проблемы с неправильным функционированием исключений (или, по крайней мере, как я надеюсь, я знаю, что есть проблемы с этим) в разделяемых библиотеках при загрузке с использованием dlopen .Я включил здесь несколько упрощенных примеров кода.Фактическая ситуация: myapp = Matlab, myext1 = расширение mexglx matlab, mylib - это общая библиотека моего кода между двумя расширениями ( myext1 , myext2 )

mylib.h

struct Foo { Foo(int a); m_a; }
void throwFoo();

mylib.cpp

#include "mylib.h"
Foo::Foo(int a): m_a(a) {}
void throwFoo() { throw Foo(123); }

myext1.cpp

#include "mylib.h" 
#include <iostream>
extern "C" void entrypoint()    
{ 
   try { throwFoo(); } 
   catch (Foo &e) { std::cout << "Caught foo\n"; }
}

myext2.cpp Идентичен myext1.cpp

myapp.cpp

#include <dlfcn.h>
int main()
{
  void *fh1 = dlopen("./myext1.so",RTLD_LAZY);
  void *fh2 = dlopen("./myext2.so",RTLD_LAZY);
  void *f1  = dlsym(fh1,"entrypoint");
  void *f2  = dlsym(fh2,"entrypoint");
  ((void (*)())func1)();  // call myext1 (A)
  ((void (*)())func2)();  // call myext2 (B)
}

Компиляция этого кода:

g++ mylib.cpp -fPIC  -o libmylib.so -shared
g++ myext1.cpp -fPIC -o myext1.so -shared -L. -lmylib -Wl,-rpath=.
g++ myext2.cpp -fPIC -o myext2.so -shared -L. -lmylib -Wl,-rpath=. 
g++ myapp.cpp -fPIC -o myapp -ldl

Вызов точка входа () в A работает как положено, с throwFoo () выбрасывает исключение и entrypoint () перехватывает его.Однако вызов на B не может перехватить исключение.Добавление дополнительного диагностического кода показывает, что typeinfo для класса Foo отличается в двух расширениях.Изменение порядка двух вызовов dlopen не имеет значения, второе загруженное расширение завершается неудачно.

Я знаю, что могу это исправить, используя RTLD_GLOBAL в качестве дополнительного флага для dlopen , но приложение (Matlab), использующее dlopen, находится вне моего контроля.Могу ли я что-нибудь сделать с mylib или myext1 , myext2 , чтобы решить эту проблему?

Я должен избегать использования флагов LD для времени выполнения (поскольку я не могу контролировать пользователей, использующих двоичный файл Matlab).Любые другие предложения?

Ответы [ 3 ]

3 голосов
/ 27 июня 2011

Простой обходной путь - иметь библиотеку dlopen с флагом RTLD_GLOBAL при первом использовании.Это заменит предыдущее открытие с RTLD_LOCAL и поместит все в глобальное пространство имен символов.

3 голосов
/ 27 февраля 2012

Могу ли я что-нибудь сделать с mylib или myext1, myext2, чтобы это исправить проблема

В качестве альтернативы использованию RTLD_GLOBAL вы можете просто использовать переменную окружения LD_PRELOAD при запуске приложения, чтобы решить вашу проблему. Вам не нужно ничего перекомпилировать:

LD_PRELOAD=libmylib.so ./myapp
3 голосов
/ 18 февраля 2011

Правило 62 в "Стандартах кодирования C ++" Александреску и Саттера:

"62. Не допускайте распространения исключений через границы модуля."

Хотя это может работать, когда вы делаете это осторожно, для действительно переносимого и многократно используемого кода это не может быть сделано. Я бы сказал, что это довольно распространенное общее правило при программировании общих библиотек или библиотек DLL, не распространяйте исключения через границы модуля. Просто используйте интерфейс в стиле C, возвращайте коды ошибок и делайте все внутри экспортируемой функции внутри блока try { } catch(...) { };. Кроме того, RTTI не распределяется между модулями, поэтому не ожидайте, что Foo будет иметь одинаковую информацию типа в разных модулях.

...