gcc — Бросать исключения C ++ из аппаратного обработчика исключений. Почему -fnon-call-исключения не ведут себя так, как ожидалось?

Прошлой ночью мне пришла в голову эта забавная идея — ловить аппаратные исключения и бросать C++ exception вместо. Мысль, которая может быть полезна для таких вещей, как FPU exceptions, который обычно либо вылетает, либо молча возвращается NaN а затем вызвать неожиданное поведение. C++ exception было бы гораздо более желательно здесь.

Так что я взломал все утро и наконец получил его на работу. Ну, почти. Компилятор до сих пор не понимает, что арифметические операции теперь могут C++ exceptionsи молча отбросит try/catch вокруг него Это работает, когда исключение происходит в функции.

void throw_exception()
{
throw std::runtime_error("Division by zero!");
}

__attribute__((noinline))
void try_div0()
{
cout << 1 / 0 << endl;
}

int main()
{
// this class traps a hardware exception (division by zero, in this case) and calls the supplied lambda function.
// uh, no, you probably don't want to see the assembly code behind this...
exception_wrapper div0_exc { 0, [] (exception_frame* frame, bool)
{
if (frame->address.segment != get_cs()) return false;           // only handle exceptions that occured in our own code
frame->stack.offset -= 4;                                       // sub <fault esp>, 4;
auto* stack = reinterpret_cast<std::uintptr_t *>(frame->stack.offset); // get <fault esp>
*stack = frame->address.offset;                                 // mov [<fault esp>], <fault address>;
frame->address.offset = reinterpret_cast<std::uintptr_t>(throw_exception);  // set return address to throw_exception()
return true;    // exception handled!
} };

try
{
// cout << 1 / 0 << endl;   // this throws, as expected, but calls std::terminate().
try_div0();                 // this exception is caught.
}
catch (std::exception& e)
{
cout << "oops: " << e.what() << endl;
}
}

Я понимаю, что это необычный вопрос … но есть ли способ сделать эту работу? Какой-то способ сказать gcc что исключения могут встречаться где угодно?

Я собираю с djgpp который (я считаю) использует DWARF Обработка исключений.


редактировать: Я только что нашел gcc флаги -fnon-call-exceptions а также -fasynchronous-unwind-tablesПохоже, что я ищу. Но это все еще не работает …


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

inline void nop() { asm(""); }
// or { cout << flush; } or something. empty function does not work.

int main()
{
/* ... */
try
{
nop();
cout << 1 / 0 << endl;
nop();
}
/* ... */
}

редактировать: Вложенные try/catch блоки имеют одинаковый эффект, ни одно исключение не перехватывается, если только перед захваченной инструкцией не предшествует вызов функции.

inline void nop() { asm(""); }

void try_div(int i)
{
try
{
// this works, catches exception in try_div(0).
nop();
cout << 1 / i << endl;
try_div(i - 1);

// without the first nop(), calls std::terminate()
//cout << 1 / i << endl;
//try_div(i - 1);

// reverse order, also terminates.
//if (i != 0) try_div(i - 1);
//cout << 1 / i << endl;
//nop();
}
catch (std::exception& e)
{
cout << "caught in try_div(" << i << "): " << e.what() << endl;
}
}

int main()
{
/* ... */

try
{
try_div(4);
}
catch (std::exception& e)
{
cout << "caught in main(): " << e.what() << endl;
}
}

редактировать: Я представил это как возможное ошибка в gcc, и сократил мой код до простого прецедент.

4

Решение

Это было какое-то время, но я наконец понял это … Функция метания должна быть помечена как наличие сигнального кадра.

[[gnu::no_caller_saved_registers]]
void throw_exception()
{
asm(".cfi_signal_frame");
throw std::runtime_error("Division by zero!");
}
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]