函数何时会在c++中包含stack_Unwind_Resume调用

when does a function will include stack _Unwind_Resume call in c++?

本文关键字:Unwind Resume 调用 stack 包含 何时会 c++ 函数      更新时间:2023-10-16

我现在正在研究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((,我们可以看到调用0x4008d0_Unwind_Resume@plt,这是对堆栈展开操作的调用,因为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.

然而,当我试图通过调用函数来模仿这种进展时,会抛出异常,汇编程序代码调用&lt_展开资源&gt不存在,为什么?

#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.

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

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

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