Создание исключения C ++ после перехода inline-asm - PullRequest
5 голосов
/ 15 апреля 2010

У меня есть какой-то странный самоизменяющийся код, но в основе его лежит довольно простая проблема: я хочу иметь возможность выполнить jmp (или call) и затем из этой произвольной точки выдать исключение и захватить его блоком try / catch, содержащим jmp / call.

Но когда я делаю это (в gcc 4.4.1 x86_64), исключение приводит к terminate(), как если бы исключение было выброшено извне try / catch. Я действительно не понимаю, чем это отличается от создания исключения внутри какой-то обширной библиотеки, но, очевидно, это потому, что она просто не работает.

Как я могу выполнить jmp или call, но все равно выбросить исключение обратно в исходный try / catch? Почему этот try / catch не обрабатывает эти исключения так, как если бы функция вызывалась нормально?

код:

#include <iostream>
#include <stdexcept>

using namespace std;

void thrower()
{
    cout << "Inside thrower" << endl;
    throw runtime_error("some exception");
}

int main()
{
    cout << "Top of main" << endl;  

    try {
        asm volatile (
            "jmp *%0" // same thing happens with a call instead of a jmp
            :
            : "r"((long)thrower)
            :
        );
    } catch (exception &e) {
        cout << "Caught : " << e.what() << endl;
    }
    cout << "Bottom of main" << endl << endl;
}

Ожидаемый результат:

Top of main 
Inside thrower 
Caught : some exception
Bottom of main

Фактический объем производства:

Top of main
Inside thrower
terminate called after throwing an instance of 'std::runtime_error'
  what():  some exception
Aborted

Ответы [ 4 ]

3 голосов
/ 15 апреля 2010

Вы смотрели, как ваша реализация обрабатывает исключения? Он включает в себя поиск адресов ПК в таблицах, чтобы выяснить, что программа делала в конкретном месте, которое бросило, и что делали все вызывающие абоненты. По крайней мере, в Mac OS X GCC.

Единственная другая система, на которую я смотрел, Metrowerks Codewarrior для Mac (в далеком прошлом) использовал аналогичную систему, хотя и несколько более прозрачную. Компилятор может прозрачно вставлять функции для любого изменения контекста исключений.

У вас нет молитвы сделать этот портативный.

1 голос
/ 23 декабря 2014

Если вы используете gcc 4.4.7 (и выше) на x86-64 linux , с dwarf механизмом обработки исключений (который может быть по умолчанию) один), у меня есть способ решить это.

Предположим, что ваш встроенный код сборки является функцией inline_add. Он вызовет другую функцию add, которая может вызвать исключение. Вот код:

extern "C" int add(int a, int b) {
    throw "in add";
}

int inline_add(int a, int b) {
    int r = 0;
    __asm__ __volatile__ (
        "movl %1, %%edi\n\t"
        "movl %2, %%esi\n\t"
        "call add\n\t"
        "movl %%eax, %0\n\t"
        :"=r"(r)
        :"r"(a), "r"(b)
        :"%eax"
    );
    return r;
}

Если вы позвоните inline_add, как это:

try {
    inline_add(1, 1);
} catch (...) {
    std::cout << "in catch" << std::endl;
}

произойдет сбой, потому что gcc не предоставляет рамку исключения для inline_add. Когда дело доходит до исключения, оно должно взломать. (см. здесь для «Совместимости с C»)

Так что нам нужно подделать для него фрейм исключения, но было бы трудно взломать сборку gcc, мы просто используем функции с соответствующим фреймом исключения, чтобы окружить его

мы определяем функцию следующим образом:

void build_exception_frame(bool b) {
    if (b) {
        throw 0;
    }
}

и позвоните inline_add вот так:

try {
    inline_add(1, 1);
    build_exception_frame(false);
} catch (...) {
    std::cout << "in catch" << std::endl;
}

и это просто работает.

build_exception_frame должен прийти после звонка, иначе он не будет работать

Более того, чтобы оптимизация gcc не приняла build_exception_frame, нам нужно добавить следующее:

void build_exception_frame(bool b) __attribute__((optimize("O0")));

Вы можете проверить код сборки, сгенерированный gcc, для проверки кода.

Кажется, что gcc предоставляет фрейм исключений для всего try / catch, если есть одна функция, которая может выдать, и положение имеет значение.

Нужно посмотреть, как gcc работает над этим позже.

Если кто-нибудь знает об этом, пожалуйста, будьте любезны, дайте мне знать. Спасибо.

1 голос
/ 15 апреля 2010

После выполнения встроенной сборки у вас есть поведение, определяемое реализацией (7.4 / 1).

Вы должны попытаться установить фрейм стека; детали зависят от платформы, и я не знаю, как это сделать на x86_64.

1 голос
/ 15 апреля 2010

Вы смотрели код ассемблера, сгенерированный gcc, если ваш блок try {} просто содержал вызов функции? Я почти уверен, что компилятор C ++ делает больше, чем просто переход в этот момент, поскольку он должен иметь возможность отслеживать трассировку в случае возникновения исключения.

Ваш код может быть работоспособным, если вы смогли имитировать шаги, которые gcc выполняет при структурировании вызова функции.

Обновление: Этот вопрос может предоставить более подробную информацию. В частности, может быть полезна метающая и захватывающая часть Itanium ABI: link

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...