Sun Studio, связывающая библиотеки gcc: исключения не работают

Мне нужно построить приложение с Sun Studio. Это приложение использует общую библиотеку, которую можно собрать только с помощью Gnu C++. Разделяемая библиотека имеет интерфейс C, так что код может вызываться компилятором Sun (чтобы избежать проблем с искажением имен, см. Также этот вопрос).

Все, кроме обработки исключений работает нормально. Когда исключение выдается в общей библиотеке, программа segfaults. Это происходит только тогда, когда основная программа компилируется с использованием Sun Studio Compiler. Скомпилировав минимальный приведенный ниже пример с помощью компилятора Gnu C++, программа работает нормально, и разделяемая библиотека обнаруживает исключение.

План A: ссылка динамически Вот иллюстрация установки:

GCC                       SOLARIS STUDIO
                shared
c_layer.so      <-----    application
(no exceptions)           (uses exceptions sol studio)
   |
   | use flag -static -static-libstdc++ -static-lib-gcc
   v
gcc_only_lib.so
libstdc++.so
(uses gcc exceptions)

Результат: нарушение сегментации при возникновении исключения (см. Код ниже).

План Б: ссылка статически

как указано выше, но сборка c_layer.a

Результат:

Неопределенный первый ссылочный символ
в файле __cxa_allocate_exception libs/cInterface/libcInterface.a(c_layer.cpp.o) std::string::~std::basic_string () libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_end_cface libcIn.a(c_layer.cpp.o) __cxa_free_exception libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_begin_catch libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_inter. cpp.o)

Вопрос: Почему обработка исключений не работает с Sun Studio?


Если я приведу в исполнение gcc, как это:

LD_PRELOAD=/usr/sfw/lib/amd64/libgcc_s.so ./example

он падает по-разному:

$> terminate вызывается после создания экземпляра 'std::runtime_error' $> terminate вызывается рекурсивно

(DBX), где 1 __lwp_sigqueue (0x1, 0x6, 0xffffc1000bae5060, 0xffffffff, 0x0, 0xffff80ffbffff810), в 0xffff80ffbf51e70a [2] thr_kill(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbf512ec8 [3] повышение (0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbf4c291d [4] прервать (0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbf497ff2 [5] __gnu_cxx::___ verbose_000x0x0_0 0 0 0 0 0x0), в 0xffff80ffbd9de911 [6] __cxxabiv1::__ прекращается (0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbd9dbd5b [7] std::terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) 0xffff80ffbd9dbda3 [8] __cxa_rethrow(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbd9dc02d [9] __gnu_cxx::__verbose_terminate_handler(0x0, 0x0x0x0x0x0x0x0x0: 00 0x0: 00 0x0: 00 0x0: 0x0 0x0: 00 __terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbd9dbd5b [11] std::terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), в 0xffff80ffbd9dbda3 0x0 00 0 0 0 0 0 0 0 (0) 0 [0] 0 [0] 0 [0] 0 [0] 0 [000] 0 [0] 0 [0] 0 [0] 0, 0x0, 0x0, 0x0), в 0xffff80ffbd9dbfd6 [13] clayerCall(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffb9991116 =>[14] main(argc = 1, argv = 0xffff80ffbffffa78), строка 6 в "exampleMain.cpp"


Вот минимальный пример для воспроизведения проблемы:

exampleMain.cpp:

#include <clayer.h> 
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!clayerCall())
        printf("got exception\n");
    else
        printf("OK\n");
}

заголовок разделяемой библиотеки:

extern "C" {

bool clayerCall();

} // end extern "C" 

общий источник lib:

#include "clayer.h"

#include <exception>
#include <stdexcept>
#include <stdio.h>

extern "C" {

bool clayerCall()
{
    try
    {
        throw std::runtime_error("hhh");
        return true;
    }
    catch (std::exception &ex)
    {
        return false;
    }
}

} // end extern c

Файлы cmake выглядят так:

для исполняемого файла

project(exampleMain)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)
add_definitions(-m64 -fPIC)

include_directories(../stackru)

link_directories (
   ../stackru
)

add_executable(example exampleMain.cpp)
target_link_libraries(
    example
    stdc++
    clayer
)

для библиотеки

project(clayer)
cmake_minimum_required(VERSION 2.8)
cmake_policy(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)

add_library(
    clayer SHARED
    clayer.cpp
)

2 ответа

Решение

Для обработки исключений требуется поддержка библиотек и компоновщиков, которые различаются в цепочке инструментов Sun Studio C++ и в Gnu C++ (таким образом, это похоже на искажение имен, которое, как вы уже заметили, отличается в двух цепочках инструментов). Использование связи "C" здесь вам не поможет, потому что реализации функций, которые вы связываете, зависят от этого средства обработки исключений. В общем случае вы не можете использовать код C++, построенный с двумя различными цепочками инструментов в одном исполняемом файле.

Если вам нужно использовать Sun Studio, потому что вы используете библиотеки с закрытым исходным кодом, которые совместимы только с Sun Studio, ваш самый простой путь вперед - это, вероятно, получить библиотеку, которая "собирается только с GNU C++", для сборки с помощью компилятора Sun C++, предполагая что эта библиотека с открытым исходным кодом. Это может быть не тривиально, и вам может потребоваться поддержка авторов библиотеки. Я сделал это, когда мне пришлось написать небольшие скрипты, похожие на команды GNU C++, которые вызывают компилятор Sun с правильными флагами.

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

Изменить: Поскольку библиотека, на которую ссылаются, является специально продвинутой, этот вопрос может быть полезным. Подводя итог, можно сказать, что некоторые компоненты boost могут быть собраны с вашей версией компилятора Sun.

Невозможно загрузить разделяемую библиотеку, созданную с помощью gcc, в исполняемый файл, созданный с помощью Solaris Studio 12.3., Если исключения не отключены.

Проблема в том, что исключения не являются частью C ABI. Среда выполнения Solaris Studio и среда gcc используют разные реализации вызовов _Unwind:

В gcc (в частности, в libstdC++) используются дополнительные нестандартные вызовы _Unwind, которые, естественно, отсутствуют в Solaris libc, детали реализации реализации Unwind в libc отличаются от деталей libgccs, поэтому, когда все стандартные процедуры _Unwind разрешаются в версии Solaris и одна нестандартная процедура _Unwind преобразуется в версию gcc, и вы получаете проблему (скорее всего, именно это и происходит с вами) ( дополнительную информацию см. здесь)

Невозможно выполнить один процесс с разными средами выполнения C++ для частей, созданных с помощью Solaris Studio и части gcc. Поэтому загрузка совместно используемой библиотеки, созданной с помощью gcc, в исполняемый файл, созданный с помощью Solaris Studio, невозможна.


Хорошей новостью является то, что начиная с версии Solaris Studio 12.4, если вы включите поддержку C++11, она может действительно работать:

В Oracle Solaris Studio 12.4 компилятор C++ поддерживает новый язык C++ 11 и ABI (двоичный интерфейс приложения). В режиме C++ 11 компилятор CC использует ABI g ++ и версию библиотеки времени выполнения g ++, которая поставляется вместе с Oracle Solaris Studio. В этом выпуске используется версия 4.8.2 библиотеки времени выполнения g ++. ( ссылка)

Другие вопросы по тегам