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

时间:2010-04-15 02:30:29

标签: c++ assembly exception

我有一些奇怪的自我修改代码,但它的根本是一个非常简单的问题:我希望能够执行jmp(或call)然后从那个任意point抛出异常并将其包含在包含jmp / call的try / catch块中。

但是当我这样做时(在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;
}

预期产出:

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 个答案:

答案 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的工作原理。

如果有人知道,请善意地告诉我。感谢。