C++ 抛出一个C++;内联asm跳转后出现异常
我有一些奇怪的自修改代码,但其根源是一个非常简单的问题:我希望能够执行C++ 抛出一个C++;内联asm跳转后出现异常,c++,assembly,exception,C++,Assembly,Exception,我有一些奇怪的自修改代码,但其根源是一个非常简单的问题:我希望能够执行jmp(或调用),然后从任意点抛出一个异常,并让包含jmp/调用的try/catch块捕获它 但是当我这样做时(在gcc 4.4.1 x86_64中),异常会导致一个terminate(),就像从try/catch外部抛出异常一样。我真的不明白这和从某个遥远的库中抛出异常有什么不同,但很明显,这是因为它根本不起作用 如何执行jmp或调用,但仍然将异常抛出回原始的try/catch?为什么这个try/catch不能像正常调用函数
jmp
(或调用
),然后从任意点抛出一个异常,并让包含jmp
/调用的try/catch块捕获它
但是当我这样做时(在gcc 4.4.1 x86_64中),异常会导致一个terminate()
,就像从try/catch外部抛出异常一样。我真的不明白这和从某个遥远的库中抛出异常有什么不同,但很明显,这是因为它根本不起作用
如何执行jmp
或调用
,但仍然将异常抛出回原始的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
terminate called after throwing an instance of 'std::runtime_error'
what(): some exception
Aborted
如果try{}块只包含一个函数调用,那么您是否看过gcc生成的汇编代码?我确信C++编译器在这个点上不仅仅是跳转,因为它需要在异常发生时能够回溯堆栈。
如果您能够模仿gcc在构造函数调用时所采取的步骤,那么您的代码可能是可行的
更新:可能提供更多信息。具体而言,安腾ABI的抛出和捕获部分可能很有用:您是否了解了您的实现如何处理异常?它涉及到在表中查找PC地址,以了解程序在抛出的特定点上做了什么,以及所有呼叫者都在做什么。至少在Mac OS X GCC上
我看过的唯一的另一个系统是Metrowerks Codewarrior for Mac(很久以前),它使用了一个类似的系统,尽管有点透明。编译器可以透明地为任何类型的异常上下文更改插入函数
您不需要祈祷让它可移植。一旦您完成了内联汇编,您就有了实现定义的行为(7.4/1)
您应该尝试设置一个堆栈框架;详细信息是特定于平台的,我不知道如何在x86_64上实现它。如果您在x86-64 linux上使用GCC4.4.7(及以上)以及dwarf异常处理机制(可能是默认机制),我有办法解决这个问题
假设您的内联汇编代码是一个函数inline\u 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\u add
如下:
try {
inline_add(1, 1);
} catch (...) {
std::cout << "in catch" << std::endl;
}
void build_exception_frame(bool b) {
if (b) {
throw 0;
}
}
try {
inline_add(1, 1);
build_exception_frame(false);
} catch (...) {
std::cout << "in catch" << std::endl;
}
然后像这样调用inline\u add
:
try {
inline_add(1, 1);
} catch (...) {
std::cout << "in catch" << std::endl;
}
void build_exception_frame(bool b) {
if (b) {
throw 0;
}
}
try {
inline_add(1, 1);
build_exception_frame(false);
} catch (...) {
std::cout << "in catch" << std::endl;
}
您可以检查gcc生成的汇编代码以验证代码
似乎gcc为整个try
/catch
提供了异常框架,只要有一个函数可能抛出,并且定位问题
稍后需要了解gcc在这方面是如何工作的
如果有人知道这件事,请告诉我。谢谢。最初,我在linux中的信号处理程序上下文中问了一个类似的问题。这确实给问题蒙上了一层阴影,所以我删除了那个,并询问了这个更简化的版本。整个信号处理程序只是掩盖了我真正的问题。一个是修饰代码和调用站点,以便将上下文推送到任何地方,另一个是索引调用和返回点,以便异常处理程序可以查找到哪里。不管怎样,内联asm都缺少一些东西。这个问题并没有提供更多的信息。我试图理解异常处理(在Mac OS X上)的微弱尝试导致我进入了一些奇怪的libunwind
代码,我找不到开源代码。它是用C++编写的,并且有苹果的命名约定。重点是,它并不是严格意义上的GCC功能。查看GCC源代码,您会发现它取决于操作系统提供的libunwind。不,它看起来只是对函数的调用(我在调用周围放置了内联asm注释,除了调用本身之外,注释之间没有说明)@SoapBox:您需要比较异常分派表,以查看在try{
和throw
插入的函数的操作。很有趣……但是如果我嵌入了对“thrower()的合法的工作调用”在同一个try/catch中,内联asm版本仍然不起作用,这听起来像是你说的应该起作用。我猜异常处理程序可能正在查看调用本身的地址,它不知道这个地址。这是迄今为止最有希望的答案。。。。