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

Исключения через двоичную границу

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

У меня следующая ситуация:

   A
  / \
 /   \
B <-- C
  • A - это общая библиотека, которая содержит класс EException
  • Ссылка B и C на A
  • C также является общей библиотекой
  • B динамически загружает C во время выполнения

В какой-то момент C выдает экземпляр EException:

void doSometing() {
    throw EException("test-message");
}

in B Я хотел бы поймать это исключение:

try {
    doSomething();
} catch (const EException& ex) {
    // Not reached
} catch (...) {
    // Not reached
}

но, как указано в коде, ни один из предложений catch не вызван. Вместо этого поток, этот код выполняется, прерывается.

Я пробовал следующие вещи:

  • При компиляции A атрибут видимости
EException устанавливается как "по умолчанию", Заголовочный файл EException содержит только декларации Я использую опцию компоновщика -fvisibility=hidden в A, B и C Я использую опцию компоновщика -E в C

Используя nm, я получаю для A:

0000000000066260 T EException::EException(QString const&)
0000000000066306 T EException::EException(EException const&)
00000000000661d0 T EException::EException() 
0000000000066260 T EException::EException(QString const&) 
0000000000066306 T EException::EException(EException const&) 
00000000000661d0 T EException::EException() 
00000000000664de T EException::~EException()
000000000006641e T EException::~EException() 
000000000006641e T EException::~EException() 
00000000000663b6 T EException::operator=(EException const&)
<...>
000000000028de40 V typeinfo for EException
000000000028dd80 V typeinfo for EException*
000000000007342b V typeinfo name for EException
0000000000072ab7 V typeinfo name for EException*
000000000028de00 V vtable for EException

для B:

U EException::EException(QString const&)
U EException::~EException()
<...>
0000000000726f60 V typeinfo for EException

и для C:

U EException::EException(QString const&)
U EException::~EException()
<...>
U typeinfo for EException

Может ли проблема быть, что B использует свой собственный типinfo EException, а C использует тот, который предоставляется A? Как я могу это исправить?

Моя среда:

  • gcc 4.6.3 на x86_64-linux-gnu
  • с помощью Qt

Благодарим за помощь!

4b9b3361

Ответ 1

У меня были аналогичные проблемы с gcc < 4.5 с символами RTTI, используемыми на границах разделяемых библиотек, но не с gcc 4.6. Однако вы все равно можете найти следующую информацию.

Как уже упоминалось, vtable (содержащая запись для объекта typeinfo) для EException, по-видимому, дублируется в некоторых единицах перевода, что определенно является проблемой с gcc < 4.5 (хорошо, это проблема libsupС++, насколько я знаю, не слияние объектов type_info). Закрепление vtable EException путем определения виртуального деструктора вне строки (оно должно быть первым объявлением виртуальной функции в заголовке) в A помогло.

Также может быть полезно опубликовать полный файл заголовка для EException.

Ответ 2

Проверить

-fvisibility = скрытый

в настройках компоновщика. Если он установлен, измените его на

-fvisibility = по умолчанию

Ответ 3

IMHO, это как ничего общего с флагов компилятора.

Объявите свой объект исключения как extern и не предоставляйте никакой реализации нигде, кроме основного файла.

Это заставит компоновщик (динамический компоновщик BTW) использовать единственную возможную реализацию.

Нет типаinfo генерируется только для определения extern.

Ответ 4

B использует его собственное определение класса EException V typeinfo for EException, тогда как C, похоже, использует неразрешенный (U означает, что тип undefined в текущем блоке перевода и должен быть разрешен загрузчиком и динамическим компоновщиком).

Убедитесь, что B все еще является общей библиотекой, и она не связана статически с A, но динамически, не позволяя C находить. Продукты, вызываемые, поскольку я не могу видеть, что B не будет связывать тот же тип, что и A. Позаботьтесь о своем заголовке ^^.

Ответ 5

Вы можете попытаться скомпилировать A, B и C с помощью -rdynamic (на этапе соединения)

На странице GCC рассказывается о -rdynamic:

Передайте флаг -export-dynamic в листинг-лист ELF, на целевые объекты, которые его поддерживают. Это дает ссылку компоновщику добавлять все символы, а не только используемые, в таблицу динамических символов. Этот параметр необходим для некоторых видов использования "dlopen" или для получения обратных трасс из программы.