mingw32 exception在sjlj与dwarf差别-反汇编分析
sjlj (setjump/longjump)与dwarf-2为mingw32两种异常处理模型的实现。sjlj有着开销,而随linux发行的mingw32开发库包都是用sjlj版编译的,而Qt却采用dwarf-2版,那么两者之间有多少差异,本文就这问题对两版的异常代码的反汇编进行分析比较。
我使用mingw-w65-i686-810的sjlj与dwarf-2两个版本对下面异常代码编译。
__attribute__((dllimport)) int dllfunc();
int main()
{
dllfunc();
//_create_locale(LC_ALL, "C");
printf("abc");
//return 0; try
{
try
{
throw std::exception();
}
catch(std::exception&)
{
std::rethrow_exception(std::current_exception());
} }
catch(int)
{ }
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
}
catch(...)
{
std::cout << "unknown" << std::endl;
}
return ;
}
代码逻辑:
两层 try/catch,
1. 里层 try/catch
1.1 try块, throw 异常
1.2 catch块, rethrow
2. 外层 try/catch
2.1 有三catch分支。
开刷前,先定义一下。
如果将 try/catch 去除 c++语言特性后,基本就是一种由c++库还有c++编译器共同管理的 goto。
throw相当于goto, catch相当于label(一种以类型区分的)。
那么c++编译器与c++库为我们提供了什么样的管理呢?
c++编译器
0. 利用c++支持对象析构进行try块保护。
1. 将 throw 关键字生成汇编 call __cxa_throw,调用 c++库的函数。
2. 为每个catch块生成代码片断,只能通过jmp跳转进来。
2.1 开头 call __cxa_begin_catch。
2.2 结尾 call __cxa_end_catch。
2.3 最后跳出到 try/catch块逻辑代码的下条执行指令。
3. 为同一try/catch块的所有catch块产生分支控制代码。
4. 为try块的析构代码产生跳转入口。
5. 为每一层try/catch块生成 uncaught 代码块,调用 _Unwind_Resume。
c++库:
1. __cxa_throw,马上_Unwind_RaiseException。跳转到当前最里面一层 try/catch的支路控制代码片断。
2. _Unwind_Resume,向上继续展开。
3. std::rethrow_exception,调用 __gcclibcxx_demangle_callback,
3.1 要么有 catch可达跳回到原来代码的控制流,直接离开std::rethrow_exception的调用上下文。
3.2 要么从__gcclibcxx_demangle_callback返回,执行terminate结束进程。
sjlj 版的反汇编代码比 dwarf-2 版的多了50行。
先来看dwarf-2的反汇编代码
<+>: lea 0x4(%esp),%ecx
<+>: and $0xfffffff0,%esp
<+>: pushl -0x4(%ecx)
<+>: push %ebp
<+>: mov %esp,%ebp
<+>: push %esi
<+>: push %ebx
<+>: push %ecx
<+>: sub $0x2c,%esp
<+>: call 0x401890 <__main>
<+>: mov 0x4071a4,%eax
<+>: call *%eax
<+>: movl $0x404045,(%esp)
<+>: call 0x4027c4 <printf>
<+>: movl $0x4,(%esp)
<+>: call 0x4017ac <__cxa_allocate_exception>
<+>: mov %eax,%ebx
<+>: mov %ebx,%ecx
<+>: call 0x402890 <std::exception::exception()>
<+>: movl $0x4017d4,0x8(%esp)
<+>: movl $0x4042a8,0x4(%esp)
<+>: mov %ebx,(%esp)
<+>: call 0x401794 <__cxa_throw>
<+>: mov $0x0,%eax
<+>: jmp 0x401723 <main()+>
<+>: mov %edx,%ecx
<+>: cmp $0x2,%ecx
<+>: je 0x40162b <main()+>
<+>: jmp 0x401663 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017a4 <__cxa_begin_catch>
<+>: mov %eax,-0x1c(%ebp)
<+>: lea -0x28(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: call 0x4017cc <_ZSt17current_exceptionv>
<+>: lea -0x28(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: call 0x4017c4 <_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE>
<+>: mov %eax,%esi
<+>: mov %edx,%ebx
<+>: lea -0x28(%ebp),%eax
<+>: mov %eax,%ecx
<+>: call 0x4017ec <_ZNSt15__exception_ptr13exception_ptrD1Ev>
<+>: call 0x40179c <__cxa_end_catch>
<+>: mov %esi,%eax
<+>: mov %ebx,%edx
<+>: cmp $0x1,%edx
<+>: je 0x40166f <main()+>
<+>: cmp $0x2,%edx
<+>: je 0x401683 <main()+>
<+>: jmp 0x4016ca <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017a4 <__cxa_begin_catch>
<+>: mov (%eax),%eax
<+>: mov %eax,-0x24(%ebp)
<+>: call 0x40179c <__cxa_end_catch>
<+>: jmp 0x401618 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017a4 <__cxa_begin_catch>
<+>: mov %eax,-0x20(%ebp)
<+>: mov -0x20(%ebp),%eax
<+>: mov (%eax),%eax
<+>: add $0x8,%eax
<+>: mov (%eax),%eax
<+>: mov -0x20(%ebp),%edx
<+>: mov %edx,%ecx
<+>: call *%eax
<+>: mov %eax,0x4(%esp)
<+>: movl $0x6ff07a00,(%esp)
<+>: call 0x4017b4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
<+>: movl $0x4017bc,(%esp)
<+>: mov %eax,%ecx
<+>: call 0x4017f4 <_ZNSolsEPFRSoS_E>
<+>: sub $0x4,%esp
<+>: call 0x40179c <__cxa_end_catch>
<+>: jmp 0x401618 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017a4 <__cxa_begin_catch>
<+>: movl $0x404049,0x4(%esp)
<+>: movl $0x6ff07a00,(%esp)
<+>: call 0x4017b4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
<+>: movl $0x4017bc,(%esp)
<+>: mov %eax,%ecx
<+>: call 0x4017f4 <_ZNSolsEPFRSoS_E>
<+>: sub $0x4,%esp
<+>: call 0x40179c <__cxa_end_catch>
<+>: jmp 0x401618 <main()+>
<+>: mov %eax,%ebx
<+>: call 0x40179c <__cxa_end_catch>
<+>: mov %ebx,%eax
<+>: mov %eax,(%esp)
<+>: call 0x402770 <_Unwind_Resume>
<+>: mov %eax,%ebx
<+>: call 0x40179c <__cxa_end_catch>
<+>: mov %ebx,%eax
<+>: mov %eax,(%esp)
<+>: call 0x402770 <_Unwind_Resume>
<+>: lea -0xc(%ebp),%esp
<+>: pop %ecx
<+>: pop %ebx
<+>: pop %esi
<+>: pop %ebp
<+>: lea -0x4(%ecx),%esp
<+>: ret
我们的主要代码逻辑只有20-30条指令
当 throw时,__cxa_throw函数是不会返回的, 如同goto最后是跳转到他处,若被本层catch处理完才会跳转回来<+88>。
然后看c++编译器为我们生成的异常代码 。
对于没有发生异常时,代码执行路径基本不会去涉及到异常代码支路,开销几近为0,只是代码量增大。
下面来看 sjlj 版的汇编代码,
function main():
<+>: lea 0x4(%esp),%ecx
<+>: and $0xfffffff0,%esp
<+>: pushl -0x4(%ecx)
<+>: push %ebp
<+>: mov %esp,%ebp
<+>: push %edi
<+>: push %esi
<+>: push %ebx
<+>: push %ecx
<+>: sub $0x68,%esp
<+>: movl $0x4017ac,-0x44(%ebp)
<+>: movl $0x402958,-0x40(%ebp)
<+>: lea -0x3c(%ebp),%eax
<+>: lea -0x18(%ebp),%ebx
<+>: mov %ebx,(%eax)
<+>: mov $0x4015b4,%edx
<+>: mov %edx,0x4(%eax)
<+>: mov %esp,0x8(%eax)
<+>: lea -0x5c(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: call 0x402790 <_Unwind_SjLj_Register>
<+>: call 0x4018b0 <__main>
<+>: mov 0x406194,%eax
<+>: movl $0xffffffff,-0x58(%ebp)
<+>: call *%eax
<+>: movl $0x404001,(%esp)
<+>: call 0x4027e4 <printf>
<+>: movl $0x4,(%esp)
<+>: call 0x4017cc <__cxa_allocate_exception>
<+>: mov %eax,-0x60(%ebp)
<+>: mov %eax,%ecx
<+>: call 0x4028b0 <std::exception::exception()>
<+>: movl $0x4017f4,0x8(%esp)
<+>: movl $0x404264,0x4(%esp)
<+>: mov -0x60(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: movl $0x1,-0x58(%ebp)
<+>: call 0x4017b4 <__cxa_throw>
<+>: mov $0x0,%eax
<+>: mov %eax,-0x60(%ebp)
<+>: jmp 0x401733 <main()+>
<+>: lea 0x18(%ebp),%ebp
<+>: mov -0x54(%ebp),%edx
<+>: mov -0x50(%ebp),%ecx
<+>: mov -0x58(%ebp),%eax
<+>: test %eax,%eax
<+>: je 0x4015e6 <main()+>
<+>: sub $0x1,%eax
<+>: test %eax,%eax
<+>: je 0x40161b <main()+>
<+>: sub $0x1,%eax
<+>: test %eax,%eax
<+>: je 0x4016f8 <main()+>
<+>: sub $0x1,%eax
<+>: test %eax,%eax
<+>: je 0x401712 <main()+>
<+>: sub $0x1,%eax
<+>: ud2
<+>: mov %edx,%eax
<+>: mov %ecx,%edx
<+>: mov %edx,%ecx
<+>: cmp $0x2,%ecx
<+>: je 0x4015f3 <main()+>
<+>: jmp 0x401642 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017c4 <__cxa_begin_catch>
<+>: mov %eax,-0x1c(%ebp)
<+>: lea -0x28(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: call 0x4017ec <_ZSt17current_exceptionv>
<+>: lea -0x28(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: movl $0x2,-0x58(%ebp)
<+>: call 0x4017e4 <_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE>
<+>: mov %edx,-0x60(%ebp)
<+>: mov %ecx,-0x64(%ebp)
<+>: lea -0x28(%ebp),%eax
<+>: mov %eax,%ecx
<+>: call 0x40180c <_ZNSt15__exception_ptr13exception_ptrD1Ev>
<+>: mov -0x60(%ebp),%eax
<+>: mov %eax,-0x60(%ebp)
<+>: mov -0x64(%ebp),%esi
<+>: mov %esi,-0x64(%ebp)
<+>: call 0x4017bc <__cxa_end_catch>
<+>: mov -0x60(%ebp),%eax
<+>: mov -0x64(%ebp),%edx
<+>: cmp $0x1,%edx
<+>: je 0x40164e <main()+>
<+>: cmp $0x2,%edx
<+>: je 0x401665 <main()+>
<+>: jmp 0x4016b3 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017c4 <__cxa_begin_catch>
<+>: mov (%eax),%eax
<+>: mov %eax,-0x20(%ebp)
<+>: call 0x4017bc <__cxa_end_catch>
<+>: jmp 0x4015a7 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017c4 <__cxa_begin_catch>
<+>: mov %eax,-0x24(%ebp)
<+>: mov -0x24(%ebp),%eax
<+>: mov (%eax),%eax
<+>: add $0x8,%eax
<+>: mov (%eax),%eax
<+>: mov -0x24(%ebp),%edx
<+>: mov %edx,%ecx
<+>: call *%eax
<+>: mov %eax,0x4(%esp)
<+>: movl $0x6ff29a00,(%esp)
<+>: movl $0x3,-0x58(%ebp)
<+>: call 0x4017d4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
<+>: movl $0x4017dc,(%esp)
<+>: mov %eax,%ecx
<+>: call 0x401814 <_ZNSolsEPFRSoS_E>
<+>: sub $0x4,%esp
<+>: call 0x4017bc <__cxa_end_catch>
<+>: jmp 0x4015a7 <main()+>
<+>: mov %eax,(%esp)
<+>: call 0x4017c4 <__cxa_begin_catch>
<+>: movl $0x404005,0x4(%esp)
<+>: movl $0x6ff29a00,(%esp)
<+>: movl $0x4,-0x58(%ebp)
<+>: call 0x4017d4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
<+>: movl $0x4017dc,(%esp)
<+>: mov %eax,%ecx
<+>: call 0x401814 <_ZNSolsEPFRSoS_E>
<+>: sub $0x4,%esp
<+>: movl $0xffffffff,-0x58(%ebp)
<+>: call 0x4017bc <__cxa_end_catch>
<+>: jmp 0x4015a7 <main()+>
<+>: mov %edx,-0x60(%ebp)
<+>: call 0x4017bc <__cxa_end_catch>
<+>: mov -0x60(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: movl $0xffffffff,-0x58(%ebp)
<+>: call 0x402788 <_Unwind_SjLj_Resume>
<+>: mov %edx,-0x60(%ebp)
<+>: movl $0x0,-0x58(%ebp)
<+>: call 0x4017bc <__cxa_end_catch>
<+>: mov -0x60(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: movl $0xffffffff,-0x58(%ebp)
<+>: call 0x402788 <_Unwind_SjLj_Resume>
<+>: lea -0x5c(%ebp),%eax
<+>: mov %eax,(%esp)
<+>: call 0x402780 <_Unwind_SjLj_Unregister>
<+>: mov -0x60(%ebp),%eax
<+>: lea -0x10(%ebp),%esp
<+>: pop %ecx
<+>: pop %ebx
<+>: pop %esi
<+>: pop %edi
<+>: pop %ebp
<+>: lea -0x4(%ecx),%esp
<+>: ret
下面的分析只列出不同的地方
支路控制代码:
可以看出,支路选路控制指令多而且复杂,还有就是跳转多。
最后是函数结束前。
可以看出在 sjlj 版本中,即使代码不发生异常,函数在进入与离开时都要为登记维护付出一此成本,当涉及异常代码时,支路选路控制更加复杂更多跳转。这里有一个成本比例,你的函数逻辑简单,上面的开销比重就越大,如果是频繁调用的轻量函数就要考虑不用exception这样的error handle。
还有就是当发生异常时,需要交给c++库去管理,不同异常处理模型的实现,有着不同的开销,本文并没有涉及到。只是单纯从c++库以外的代码进行分析,也足够看出他们之间有着一定的差别。
mingw32 exception在sjlj与dwarf差别-反汇编分析的更多相关文章
- Linux下简单C语言小程序的反汇编分析
韩洋原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在开始,本文为因为参加MOO ...
- 反汇编分析objc函数枢纽objc_msgSend
在分析objc_msgSend之前,先来搞清楚另一个问题. 函数是什么?可能会答 void foo(void) {} 像这样就是一个函数.或者函数包括函数原型和函数定义,是一段执行某样功能的机器代码. ...
- objc反汇编分析,block函数块为何物?
上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西. 我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境. ...
- 反汇编分析__stdcall和__cdecl的异同
C++代码如下:.h头文件 #pragma once#ifdef DLLTestAPI#else#define DLLTestAPI _declspec(dllimport)#endifint DLL ...
- objc反汇编分析__strong和__weak
如题所说反汇编看__strong和__weak的真实样子,代码列举自然多,篇幅长不利于阅读,我就先搬出结论,后面是分析. 在NON-ARC环境,__strong和__weak不起作用.相反在ARC环境 ...
- 反汇编分析NSString,你印象中的NSString是这样吗
我们先来定义三个NSString -(void) testNSString { NSString* a = @"abc"; NSString* b = [NSString stri ...
- objc反汇编分析,手工逆向libsystem_blocks.dylib
上一篇<block函数块为何物?>介绍了在函数中定义的block函数块的反汇编实现,我在文中再三指出__block变量和block函数块自始还都是stack-based的,还不完全适合在离 ...
- 实验4 汇编应用编程和c语言程序反汇编分析
1. 实验任务1 教材「实验9 根据材料编程」(P187-189)编程:在屏幕中间分别显示绿色.绿底红色.白底蓝色的字符串'welcome to masm!'. 解题思路:根据学习的知识,我知道该页在 ...
- 反汇编分析C++代码
编译环境:Windows 10 + VS2015 1.问题引入 在Win32环境下,CPU小端模式,参数用栈来传递,写出输出结果. 代码如下: int main() { long long a = 1 ...
随机推荐
- jdk8 Collections#sort究竟做了什么
前言 Collections#sort 追踪代码进去看,会调用到Arrays.sort,看到这里时,你肯定会想,这不是很简单,Arrays.sort在元素较少时使用插入排序,较多时使用快速排序,再多时 ...
- python之module 'unittest' has no attribute 'TestCase' 解决方案
脚本报错如下: 解决方案: 这是脚本名称冲突所导致的报错,修改脚本名中重新执行,运行正常 注:脚本取名最好不要与模块和方法一致,避免不必要的冲突
- Java-main方法中调用非static方法
java的calss中,在public static void main(String[] args) { }方法中调用非static的方法:在main方法中创建该calss的对象,用对象调用非sta ...
- Siemens PLC分类和基本性能指标
PLC分类 整体式plc也成为单元式,特点是电源,中央处理器单元以及I/O借口都集成在一个机壳内. 标准摸板试结构化,也成为组合式,特点是电源,中央处理器单元模板以及I/O模板在结构上都是相互独立的, ...
- C#日志记录类
public class WriteLog { /// <summary> /// 将错误写入文件中 /// </summary> /// <param name=&qu ...
- 快服务常见TOP3审核雷区,再不过审就要崩溃啦!
仰天大笑出门去,审核提交很神气. 垂死病中惊坐起,突然驳回伤不起. 江州司马青衫湿,重新修改苦自知. 梦里花落知多少,回复马上就改好. 审批被驳回可能是最常见的令开发者头大的问题了,明明看起来&quo ...
- [SD心灵鸡汤]003.每月一则 - 2015.07
乔布斯去世了,但他留给世人的财富却很多,值得每个人学习.他是个精力充沛魅力无限的家伙,同时也是一个很会鼓动人心的激励大师,甚至在他的平常对话中,经典的语句也常常脱口而出. 这里摘取了一些他的经典语录, ...
- [Python番外]001.用Sublime开发Python
用Sublime开发Python 准备 安装Package Control插件 安装Python插件 Python环境配置 修改快捷键 准备 安装Python 详见 Python准备 下载Sublim ...
- [工具推荐]003.Tortoisegit使用教程
Git简介: Git是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目,是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理.Git 是 ...
- ZooKeeper未授权漏洞
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作.最终, ...