c++ 异常处理(2)(五)
this指针
400d20: e8 fd 01 00 00 callq 400f22 <_ZN2csD1Ev>
400d25: eb 1e jmp 400d45 <_Z12test_func3_2v+0xa1>
400d27: 48 89 45 b8 mov %rax,0xffffffffffffffb8(%rbp)
400d2b: 48 8b 5d b8 mov 0xffffffffffffffb8(%rbp),%rbx
400d2f: 48 8d 7d e0 lea 0xffffffffffffffe0(%rbp),%rdi #c的this指针
400d33: e8 ea 01 00 00 callq 400f22 <_ZN2csD1Ev>
400d38: 48 89 5d b8 mov %rbx,0xffffffffffffffb8(%rbp)
400d3c: 48 8b 7d b8 mov 0xffffffffffffffb8(%rbp),%rdi
400d40: e8 b3 fc ff ff callq 4009f8 <_Unwind_Resume@plt> #c的this指针
400d45: 48 8d 7d e0 lea 0xffffffffffffffe0(%rbp),%rdi
400d49: e8 d4 01 00 00 callq 400f22 <_ZN2csD1Ev>
}
400d4e: 48 83 c4 48 add $0x48,%rsp
400d52: 5b pop %rbx
400d53: c9 leaveq
400d54: c3 retq
400d55: 90 nop
复制代码
注意其中标红色的代码,_ZN2csD1Ev即是类cs的析构函数,_Unwind_Resume()是当清理完成时,退出退出landing pad的代码。test_func3_2()中只有3个cs 对象,但调用析构函数的代码却出现了6次。这里其实就是多个出口函数,分别对应不同情况下,处理局部变量的析构,对于我们分布的代码来说,test_func3_2()函数中的landing pad就是从地址:400d09开始:
1)先析构c2,然后jump到400d2b析构c.
2)最后调用_Unwind_Resume()
由此可见当程序中有多个地方可能抛异常的话,该函数的出口将更复杂,这也算是异常处理的一个overhead了。
总结
至此,关于GCC处理异常的具体流程及方式,各个细节都已写完,涉及很多比较琐碎的东西,只有反复
阅读源码及相关文档才能搞明白,也不容易啊,只是古人说的好,纸上得来终觉浅,为了加深印象及验证所学的内容,我根据前面了解的这些知识,简单仿着GCC写了一个简化版的c++ ABI,代码放到了github上这里,有兴趣的读者们可以参考一下,原本是打算把unwinder也写一遍的,但DWARF的格式实在太过复杂,已经超出了异常处理这个范围,就作罢了。