阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。
阿里妹导读
作者在调查某个 bug 时涉及到 C++ 异常,借此机会以本文把 C++ 异常机制梳理清楚供大家参考。
测试程序
struct A {
A() { printf("A\n"); }
~A() { printf("~A\n"); }
};
struct E {
E() { printf("E\n"); }
~E() { printf("~E\n"); }
};
void f()
{
throw E();
}
void g()
{
A a;
f();
}
int main()
{
try {
g();
} catch (int n) {
printf("catch int %d\n", n);
} catch (const E& e) {
printf("catch E %p\n", &e);
}
return 0;
}
抛出异常
void f()
{
// throw E();
E* e = __cxa_allocate_exception(sizeof(struct E)); // 从堆上分配异常对象
e->E(); // 构造异常对象
__cxa_throw( // 抛异常
e, // 异常对象
&typeid(struct E), // 异常对象的类型,这是编译时生成的静态对象
&E::~E); // 异常对象的析构函数
}
这些 __cxa 开头的函数是由 C++ 运行时库提供的。
传播异常
void g()
{
// A a;
A a; // 在栈上分配 a 对象
a.A(); // 构造 a 对象
// f();
f(); // 调用 f()
a.~A(); // f() 正常返回走到这里
goto end_of_catch;
// f() 抛异常跳转到这里。
// 尽管 g() 没有 catch,但是 a 需要析构,因此也有着落场。
// 此时 rax 指向异常对象头,rdx 表示匹配的动作。
a.~A(); // 析构 a 对象
_Unwind_Resume(e); // 没有匹配的 catch,继续回溯栈帧
end_of_catch:
return;
}
捕获异常
int main()
{
// try {
// g();
// }
g(); // 调用 g()
// 如果 try { ... } 在 g() 后面还有其它代码,会放在这里
goto end_of_catch; // g() 正常返回走到这里
// 这里是 throw 的着陆场。
// $rax 指向异常对象。
// $rdx 表示动作:
// 0 表示不 catch,继续向上回溯栈帧;
// 1 表示匹配第一个 catch;
// 2 表示匹配第二个 catch。
void *p = rax;
int action = rdx;
// 如果 try { ... } 有对象需要析构,在这里析构。
// 下面我们开始匹配 catch 了。
// catch (int n) {
// printf("catch int %d\n", n);
// }
if (action == 1) {
n = *(int *) e;
printf("catch int %d\n", n);
goto end_of_catch;
}
// catch (const E& e) {
// printf("catch E\n");
// }
if (action == 2) {
E *e = __cxa_begin_catch(p);
printf("catch E %p\n", e);
__cxa_end_catch(); // 内部析构 e 对象
goto end_of_catch;
}
_Unwind_Resume(p); // 如果没有匹配的 catch,继续回溯栈帧。
end_of_catch:
return 0;
}
其它细节
1、Itanium C++ ABI: Exception Handling:https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
2、Exception Handling ABI for the Arm Architecture:https://github.com/ARM-software/abi-aa/blob/844a79fd4c77252a11342709e3b27b2c9f590cf1/ehabi32/ehabi32.rst
3、libunwind LLVM Unwinder:https://github.com/llvm/llvm-project/blob/main/libunwind/docs/index.rst
4、Linux 栈回溯(x86_64):https://zhuanlan.zhihu.com/p/302726082
5、.eh_frame:https://www.airs.com/blog/archives/460
阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。
文章引用微信公众号"阿里开发者",如有侵权,请联系管理员删除!