在内联asm跳转后抛出C++异常

Soa*_*Box 5 c++ assembly exception

我有一些奇怪的自修改代码,但它的根本是一个非常简单的问题:我希望能够执行一个jmp(或一个call),然后从该任意点抛出异常并让它被try/catch捕获包含jmp/的块call.

但是当我这样做时(在gcc 4.4.1 x86_64中),terminate()如果从try/catch外部抛出异常,异常会导致异常.我真的没有看到这与从一些偏远的库中抛出异常有什么不同,但显然是因为它不起作用.

如何执行jmpcall仍然将异常抛回原始的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;
}
Run Code Online (Sandbox Code Playgroud)

预期产量:

Top of main 
Inside thrower 
Caught : some exception
Bottom of main
Run Code Online (Sandbox Code Playgroud)

实际输出:

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

小智 1

如果您在x86-64 linux上使用gcc 4.4.7(及更高版本),并且具有异常处理机制(可能是默认的),我有一种方法可以解决这个问题。

假设您的内联汇编代码是一个函数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;
}
Run Code Online (Sandbox Code Playgroud)

如果你inline_add这样调用:

try {
    inline_add(1, 1);
} catch (...) {
    std::cout << "in catch" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

它会崩溃,因为 gcc 不提供inline_add. 当遇到异常时,就必须抓紧。(请参阅此处的“与 C 的兼容性”)

所以我们需要为它伪造一个异常框架,但是用 gcc 程序集很难破解,我们只需使用具有适当异常框架的函数来包围它

我们定义一个这样的函数:

void build_exception_frame(bool b) {
    if (b) {
        throw 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

inline_add并像这样调用:

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

它确实有效。

build_exception_frame应该在通话后出现,否则不起作用

此外,为了防止 gcc 可能进行优化build_exception_frame,我们需要添加以下内容:

void build_exception_frame(bool b) __attribute__((optimize("O0")));
Run Code Online (Sandbox Code Playgroud)

您可以检查 gcc 生成的汇编代码来验证代码。

看来 gcc 为整个try/提供了异常框架catch,只要有一个函数可能会抛出,并且位置很重要。

稍后需要看看 gcc 是如何工作的。

如果有人知道这一点,请好心告诉我。谢谢。