我有一些奇怪的自我修改代码,但它的根本是一个非常简单的问题:我希望能够执行jmp
(或call
)然后从那个任意point抛出异常并将其包含在包含jmp
/ call
的try / catch块中。
但是当我这样做时(在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
答案 0 :(得分:3)
您是否了解过您的实现如何处理异常?它涉及在表格中查找PC地址,以弄清楚程序在投掷的特定位置做了什么,以及所有调用者正在做什么。至少在Mac OS X GCC上。
我看过的唯一一个其他系统,Metrowerks Codewarrior for Mac(回来的时候)使用了类似的系统,尽管有点透明。编译器可以透明地插入任何类型的异常上下文更改函数。
你没有祈祷让这个便携。
答案 1 :(得分:1)
如果你的try {}块只包含一个函数调用,你看过gcc生成的汇编代码吗?我很确定C ++编译器在这一点上做的不仅仅是跳转,因为它需要能够在发生异常时对堆栈进行反向跟踪。
如果您能够模仿gcc在构造函数调用时所采取的步骤,那么您的代码可能是可行的。
更新: this question可能会提供更多信息。特别是,Itanium ABI的投掷和捕获部分可能是有用的:link
答案 2 :(得分:1)
执行内联汇编后,您将拥有实现定义的行为(7.4 / 1)。
您应该尝试设置堆栈框架;详细信息是特定于平台的,我不知道如何在x86_64上执行此操作。
答案 3 :(得分:1)
如果您在 x86-64 linux 上使用 gcc 4.4.7 (及以上),则使用 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
提供异常框架。当遇到异常时,它必须要解决问题。 (请参阅here了解“与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的工作原理。
如果有人知道,请善意地告诉我。感谢。