Issue
I am now studying c++ exception and a trouble comes to me, program show as below
#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;
}
I use gdb to disassemble fn(), and we can see callq 0x4008d0 _Unwind_Resume@plt, it is a call to stack unwind operation since vector::at might throw an out of range exception
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.
however, when I try to imitate this progress with calling a function will throw exception , assembler code call <_Unwind_Resume> doesn't exist,why?
#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;
}
assembler code for function fn2(), it doesn't include call Unwind_Resume@plt,why?
(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.
Solution
Since fn2
only calls fn
, which is declared as throw()
, no exception can propagate to an active stack frame for fn
. GCC recognizes this situation and optimizes the exception handler away.
In the original case, this is not possible because std::vector::at(size_type)
can throw. The exception handler is only needed because of the throw()
declaration, to call std::unexpected()
in case of an exception.
_Unwind_Resume
only shows up if the stack frame needs some special action when unwinding (such as calling destructors or std::unexpected()
). Without that, the Itanium C++ ABI (which is used by GCC) does not require any per-frame action at all, which is why this implementation is sometimes called zero-cost exception handling.
Answered By - Florian Weimer