内容简介:作者:在我们理解异常的路程上,我们发现重担在libstdc++里完成,如C++ ABI说明的那样。阅读了一些链接器错误,我们上次推断要处理异常我们需要C++ ABI的辅助;我们创建了一个抛出异常的C++程序,把它与一个C程序链接,发现编译器有时把我们的throw指令翻译为某些现在调用几个libstdc++函数的对象来实际抛出异常。已经迷失了?你可以在我的无论如何,我们希望确切理解异常是如何抛出的,因此我们将尝试实现我们自己的小ABI,能够抛出异常。要做到这,需要许多RTFM,不过
作者: nicolasbrailo
在我们理解异常的路程上,我们发现重担在libstdc++里完成,如C++ ABI说明的那样。阅读了一些链接器错误,我们上次推断要处理异常我们需要C++ ABI的辅助;我们创建了一个抛出异常的C++程序,把它与一个C程序链接,发现编译器有时把我们的throw指令翻译为某些现在调用几个libstdc++函数的对象来实际抛出异常。已经迷失了?你可以在我的 github repo 里检查这个项目的源代码。
无论如何,我们希望确切理解异常是如何抛出的,因此我们将尝试实现我们自己的小ABI,能够抛出异常。要做到这,需要许多RTFM,不过 在这里 可以找到一个用于LLVM的完整ABI接口。让我们先回忆一下这些缺少的函数是什么开始:
1. > gcc main.o throw.o -o app
2. throw.o: In function `foo()':
3. throw.cpp:4: undefined reference to `__cxa_allocate_exception'
4. throw.cpp:4: undefined reference to `__cxa_throw'
5. throw.o:(.rodata._ZTI9Exception[typeinfo for Exception]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
6. collect2: ld returned 1 exit status
__cxa_allocate_exception
我觉得名字不言自明。__cxa_allocate_exception接受一个size_t,分配足够的内存来保存要抛出的异常。这比你想象的要复杂得多:在要抛出一个异常时,栈会发生一些神奇的事情,因此在这里分配资源不是一个好主意。不过在堆上分配内存也不是一个好主意,因为如果我们耗尽内存,我们可能要抛出异常。静态分配同样不是好主意,因为我们需要这是线程安全的(否则两个线程同时访问同样悲剧)。鉴于这些限制,绝大多数实现看起来在一个局部线程储存(堆)上分配内存,如果内存耗尽转向一个紧急储存(大概是静态的)。当然我们不希望操心那些丑陋的细节,因此如果愿意我们可以只有一个静态缓冲。
__cxa_throw
这个函数执行所有的抛出魔术!根据ABI文献,一旦创建了异常,__cxa_throw将被调用。这个函数将负责启动栈回滚。这的一个重要后果是:__cxa_throw从不预期会返回。它要么把执行委托给正确的catch块来处理异常,要么(缺省)调用std::terminate,但它从不返回。
用于 __cxxabiv1::__class_type_info 的 vtable
一件离奇的事……__class_type_info显然是某种RTTI,但它究竟是什么?现在这是不容易回答的,并且对我们的小ABI而言它不是特别重要;我们把它放在附录里,留待我们完成抛出异常过程分析之后,现在我们只说这是ABI定义的入口,以(在运行时)知晓两个类型是否相同。这是调用来确定一个catch(父亲)是否能处理一个throw孩子的函数。目前我们将关注在基础:我们需要给它一个地址用于链接器(即定义它是不足够的,我们需要具现它),并且它必须有一个vtable(即,它必须有虚函数)。
在这些函数上发生了很多事情,但让我们尝试实现尽可能简单的异常抛出器:当一个异常抛出时,调用exit。我们的应用程序几乎没有问题,但缺少某些ABI内容,因此让我们创建一个mycppabi.cpp。阅读我们的ABI规范,可以得出__cxa_allocate_exception与__cxa_throw的署名:
|
|
备注:你可以从我的 github repo 下载完整的源代码。
如果我们现在编译mycppabi.cpp并把它与其他两个.o文件链接,我们将得到一个可工作的二进制文件,它将打印“alloc ex 1\nthrow”,然后退出。相当简单,但这是一个惊人的壮举:我们设法抛出一个异常而没有调用libc++。我们已经编写了C++ ABI一个(非常小的)部分!
通过创建我们自己的小ABI,我们获得的另一个重要的知识:throw关键字被编译为libstdc++的两个函数。这里没有双关语,它实际上是相当简单的翻译。我们甚至可以反汇编我们的抛出函数来验证它。让我们运行这个命令“g++ -S throw.cpp”。
|
|
更神奇的事情发生了:在throw关键字被翻译为这个两个调用,编译器甚至不知道怎样处理异常。因为libstdc++是定义__cxa_throw及其朋友的地方,且libstdc++是在运行时动态链接的,在第一次运行我们的可执行文件时,可以选择异常处理方法。
现在我们看到了一些进展,但我们仍然有很长的路要走。我们的ABI仅能抛出异常。我们可以扩展它来处理一个捕捉吗?我们下一节来看。
以上所述就是小编给大家介绍的《[译]C++异常的幕后3:取悦链接器的ABI》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 静态链接和动态链接
- linux中硬链接、软链接的建立
- 微信公众号开发C#系列-10、长链接转短链接
- linux – 硬链接或软链接占用文件系统中的任何空间吗?
- Linux 环境下 gcc 链接库 编译、链接(概览) 以及 自动化工具Makefile的编写
- iPhone慎点链接
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hit Refresh
Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37
Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!