Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 抛出一个C++;内联asm跳转后出现异常_C++_Assembly_Exception - Fatal编程技术网

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版本仍然不起作用,这听起来像是你说的应该起作用。我猜异常处理程序可能正在查看调用本身的地址,它不知道这个地址。这是迄今为止最有希望的答案。。。。