【问题标题】:when does a function will include stack _Unwind_Resume call in c++?什么时候函数会在 C++ 中包含堆栈 _Unwind_Resume 调用?
【发布时间】:2020-07-12 10:29:07
【问题描述】:

我现在正在研究c++异常,遇到了麻烦,程序如下所示

#include<iostream>
#include<unistd.h>
#include<string>
#include<thread>
#include<vector>
#include<exception>
using namespace std;


vector<int> vec(20);


void fn()throw() {
        vec.at(10);
}

int main(){
        fn();

return 0;
}

我用gdb反汇编fn(),我们可以看到callq 0x4008d0 _Unwind_Resume@plt,是调用stack unwind操作,因为vector::at可能抛出超出范围的异常

Dump of assembler code for function fn():
   0x00000000004009e6 <+0>:     push   %rbp
   0x00000000004009e7 <+1>:     mov    %rsp,%rbp
   0x00000000004009ea <+4>:     mov    $0xa,%esi
   0x00000000004009ef <+9>:     mov    $0x6020a0,%edi
   0x00000000004009f4 <+14>:    callq  0x400ba6 <std::vector<int, std::allocator<int> >::at(unsigned long)>
   0x00000000004009f9 <+19>:    jmp    0x400a11 <fn()+43>
   0x00000000004009fb <+21>:    cmp    $0xffffffffffffffff,%rdx
   0x00000000004009ff <+25>:    je     0x400a09 <fn()+35>
   0x0000000000400a01 <+27>:    mov    %rax,%rdi
   0x0000000000400a04 <+30>:    callq  0x4008d0 <_Unwind_Resume@plt>
   0x0000000000400a09 <+35>:    mov    %rax,%rdi
   0x0000000000400a0c <+38>:    callq  0x400880 <__cxa_call_unexpected@plt>
   0x0000000000400a11 <+43>:    pop    %rbp
   0x0000000000400a12 <+44>:    retq   
End of assembler dump.

但是,当我尝试通过调用函数来模仿这个进度时,会抛出异常,汇编代码 call <_unwind_resume> 不存在,为什么?

#include<iostream>
#include<unistd.h>
#include<string>
#include<thread>
#include<vector>
#include<exception>
using namespace std;


class myException:public exception  
{  
        public:  
                myException(){ } 
};


void fn()throw() {
        throw myException();
}

void fn2()throw(){
        fn();
}


int main(){
        fn2();

        return 0;
}


函数fn2()的汇编代码,它不包含调用Unwind_Resume@plt,为什么?

(gdb) disassemble fn2
Dump of assembler code for function fn2():
   0x0000000000400aec <+0>:     push   %rbp
   0x0000000000400aed <+1>:     mov    %rsp,%rbp
   0x0000000000400af0 <+4>:     callq  0x400aa6 <fn()>
   0x0000000000400af5 <+9>:     nop
   0x0000000000400af6 <+10>:    pop    %rbp
   0x0000000000400af7 <+11>:    retq   
End of assembler dump.

【问题讨论】:

    标签: c++ exception gcc


    【解决方案1】:

    由于fn2 仅调用声明为throw()fn,因此不会有异常传播到fn 的活动堆栈帧。 GCC 识别出这种情况并优化异常处理程序。

    在原来的情况下,这是不可能的,因为std::vector::at(size_type) 可以抛出。仅由于 throw() 声明才需要异常处理程序,以便在发生异常时调用 std::unexpected()

    _Unwind_Resume 仅在堆栈帧在展开时需要一些特殊操作(例如调用析构函数或std::unexpected())时才会显示。没有它,Itanium C++ ABI(GCC 使用)根本不需要任何每帧操作,这就是为什么这种实现有时被称为零成本异常处理

    【讨论】:

    • 非常感谢,我误以为throw() 会将所有异常抛到外面,但这实际上取决于括号中的类型,throw() 表示什么也不抛,替换为throw(myException) 后一切正常.但是,另一个问题来了,好像throw()已经被弃用了,推荐noexcept(),所以我用noexcept(false)代替throw(myException),然后fn()的汇编代码的call Unwind_Resume@plt就消失了,所以我很想要弄清楚call Unwind_Resume@plt 是否由调用者执行以恢复那些被调用函数的堆栈,对吗?
    猜你喜欢
    • 1970-01-01
    • 2014-11-24
    • 2012-08-13
    • 1970-01-01
    • 1970-01-01
    • 2016-02-24
    • 2014-05-28
    • 1970-01-01
    • 2011-01-19
    相关资源
    最近更新 更多