sjlj (setjump/longjump)与dwarf-2为mingw32两种异常处理模型的实现。sjlj有着开销,而随linux发行的mingw32开发库包都是用sjlj版编译的,而Qt却采用dwarf-2版,那么两者之间有多少差异,本文就这问题对两版的异常代码的反汇编进行分析比较。

我使用mingw-w65-i686-810的sjlj与dwarf-2两个版本对下面异常代码编译。

  1. __attribute__((dllimport)) int dllfunc();
  2. int main()
  3. {
  4. dllfunc();
  5. //_create_locale(LC_ALL, "C");
  6. printf("abc");
  7. //return 0;
  8.  
  9. try
  10. {
  11. try
  12. {
  13. throw std::exception();
  14. }
  15. catch(std::exception&)
  16.   {
  17. std::rethrow_exception(std::current_exception());
  18.   }
  19.  
  20. }
  21. catch(int)
  22. {
  23.  
  24. }
  25. catch(std::exception& e)
  26. {
  27. std::cout << e.what() << std::endl;
  28. }
  29. catch(...)
  30. {
  31. std::cout << "unknown" << std::endl;
  32. }
  33. return ;
  34. }

代码逻辑:

两层 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的反汇编代码

  1. <+>: lea 0x4(%esp),%ecx
  2. <+>: and $0xfffffff0,%esp
  3. <+>: pushl -0x4(%ecx)
  4. <+>: push %ebp
  5. <+>: mov %esp,%ebp
  6. <+>: push %esi
  7. <+>: push %ebx
  8. <+>: push %ecx
  9. <+>: sub $0x2c,%esp
  10. <+>: call 0x401890 <__main>
  11. <+>: mov 0x4071a4,%eax
  12. <+>: call *%eax
  13. <+>: movl $0x404045,(%esp)
  14. <+>: call 0x4027c4 <printf>
  15. <+>: movl $0x4,(%esp)
  16. <+>: call 0x4017ac <__cxa_allocate_exception>
  17. <+>: mov %eax,%ebx
  18. <+>: mov %ebx,%ecx
  19. <+>: call 0x402890 <std::exception::exception()>
  20. <+>: movl $0x4017d4,0x8(%esp)
  21. <+>: movl $0x4042a8,0x4(%esp)
  22. <+>: mov %ebx,(%esp)
  23. <+>: call 0x401794 <__cxa_throw>
  24. <+>: mov $0x0,%eax
  25. <+>: jmp 0x401723 <main()+>
  26. <+>: mov %edx,%ecx
  27. <+>: cmp $0x2,%ecx
  28. <+>: je 0x40162b <main()+>
  29. <+>: jmp 0x401663 <main()+>
  30. <+>: mov %eax,(%esp)
  31. <+>: call 0x4017a4 <__cxa_begin_catch>
  32. <+>: mov %eax,-0x1c(%ebp)
  33. <+>: lea -0x28(%ebp),%eax
  34. <+>: mov %eax,(%esp)
  35. <+>: call 0x4017cc <_ZSt17current_exceptionv>
  36. <+>: lea -0x28(%ebp),%eax
  37. <+>: mov %eax,(%esp)
  38. <+>: call 0x4017c4 <_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE>
  39. <+>: mov %eax,%esi
  40. <+>: mov %edx,%ebx
  41. <+>: lea -0x28(%ebp),%eax
  42. <+>: mov %eax,%ecx
  43. <+>: call 0x4017ec <_ZNSt15__exception_ptr13exception_ptrD1Ev>
  44. <+>: call 0x40179c <__cxa_end_catch>
  45. <+>: mov %esi,%eax
  46. <+>: mov %ebx,%edx
  47. <+>: cmp $0x1,%edx
  48. <+>: je 0x40166f <main()+>
  49. <+>: cmp $0x2,%edx
  50. <+>: je 0x401683 <main()+>
  51. <+>: jmp 0x4016ca <main()+>
  52. <+>: mov %eax,(%esp)
  53. <+>: call 0x4017a4 <__cxa_begin_catch>
  54. <+>: mov (%eax),%eax
  55. <+>: mov %eax,-0x24(%ebp)
  56. <+>: call 0x40179c <__cxa_end_catch>
  57. <+>: jmp 0x401618 <main()+>
  58. <+>: mov %eax,(%esp)
  59. <+>: call 0x4017a4 <__cxa_begin_catch>
  60. <+>: mov %eax,-0x20(%ebp)
  61. <+>: mov -0x20(%ebp),%eax
  62. <+>: mov (%eax),%eax
  63. <+>: add $0x8,%eax
  64. <+>: mov (%eax),%eax
  65. <+>: mov -0x20(%ebp),%edx
  66. <+>: mov %edx,%ecx
  67. <+>: call *%eax
  68. <+>: mov %eax,0x4(%esp)
  69. <+>: movl $0x6ff07a00,(%esp)
  70. <+>: call 0x4017b4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
  71. <+>: movl $0x4017bc,(%esp)
  72. <+>: mov %eax,%ecx
  73. <+>: call 0x4017f4 <_ZNSolsEPFRSoS_E>
  74. <+>: sub $0x4,%esp
  75. <+>: call 0x40179c <__cxa_end_catch>
  76. <+>: jmp 0x401618 <main()+>
  77. <+>: mov %eax,(%esp)
  78. <+>: call 0x4017a4 <__cxa_begin_catch>
  79. <+>: movl $0x404049,0x4(%esp)
  80. <+>: movl $0x6ff07a00,(%esp)
  81. <+>: call 0x4017b4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
  82. <+>: movl $0x4017bc,(%esp)
  83. <+>: mov %eax,%ecx
  84. <+>: call 0x4017f4 <_ZNSolsEPFRSoS_E>
  85. <+>: sub $0x4,%esp
  86. <+>: call 0x40179c <__cxa_end_catch>
  87. <+>: jmp 0x401618 <main()+>
  88. <+>: mov %eax,%ebx
  89. <+>: call 0x40179c <__cxa_end_catch>
  90. <+>: mov %ebx,%eax
  91. <+>: mov %eax,(%esp)
  92. <+>: call 0x402770 <_Unwind_Resume>
  93. <+>: mov %eax,%ebx
  94. <+>: call 0x40179c <__cxa_end_catch>
  95. <+>: mov %ebx,%eax
  96. <+>: mov %eax,(%esp)
  97. <+>: call 0x402770 <_Unwind_Resume>
  98. <+>: lea -0xc(%ebp),%esp
  99. <+>: pop %ecx
  100. <+>: pop %ebx
  101. <+>: pop %esi
  102. <+>: pop %ebp
  103. <+>: lea -0x4(%ecx),%esp
  104. <+>: ret

我们的主要代码逻辑只有20-30条指令

当 throw时,__cxa_throw函数是不会返回的, 如同goto最后是跳转到他处,若被本层catch处理完才会跳转回来<+88>。

然后看c++编译器为我们生成的异常代码 。

对于没有发生异常时,代码执行路径基本不会去涉及到异常代码支路,开销几近为0,只是代码量增大。

下面来看 sjlj 版的汇编代码,

  1. function main():
  2. <+>: lea 0x4(%esp),%ecx
  3. <+>: and $0xfffffff0,%esp
  4. <+>: pushl -0x4(%ecx)
  5. <+>: push %ebp
  6. <+>: mov %esp,%ebp
  7. <+>: push %edi
  8. <+>: push %esi
  9. <+>: push %ebx
  10. <+>: push %ecx
  11. <+>: sub $0x68,%esp
  12. <+>: movl $0x4017ac,-0x44(%ebp)
  13. <+>: movl $0x402958,-0x40(%ebp)
  14. <+>: lea -0x3c(%ebp),%eax
  15. <+>: lea -0x18(%ebp),%ebx
  16. <+>: mov %ebx,(%eax)
  17. <+>: mov $0x4015b4,%edx
  18. <+>: mov %edx,0x4(%eax)
  19. <+>: mov %esp,0x8(%eax)
  20. <+>: lea -0x5c(%ebp),%eax
  21. <+>: mov %eax,(%esp)
  22. <+>: call 0x402790 <_Unwind_SjLj_Register>
  23. <+>: call 0x4018b0 <__main>
  24. <+>: mov 0x406194,%eax
  25. <+>: movl $0xffffffff,-0x58(%ebp)
  26. <+>: call *%eax
  27. <+>: movl $0x404001,(%esp)
  28. <+>: call 0x4027e4 <printf>
  29. <+>: movl $0x4,(%esp)
  30. <+>: call 0x4017cc <__cxa_allocate_exception>
  31. <+>: mov %eax,-0x60(%ebp)
  32. <+>: mov %eax,%ecx
  33. <+>: call 0x4028b0 <std::exception::exception()>
  34. <+>: movl $0x4017f4,0x8(%esp)
  35. <+>: movl $0x404264,0x4(%esp)
  36. <+>: mov -0x60(%ebp),%eax
  37. <+>: mov %eax,(%esp)
  38. <+>: movl $0x1,-0x58(%ebp)
  39. <+>: call 0x4017b4 <__cxa_throw>
  40. <+>: mov $0x0,%eax
  41. <+>: mov %eax,-0x60(%ebp)
  42. <+>: jmp 0x401733 <main()+>
  43. <+>: lea 0x18(%ebp),%ebp
  44. <+>: mov -0x54(%ebp),%edx
  45. <+>: mov -0x50(%ebp),%ecx
  46. <+>: mov -0x58(%ebp),%eax
  47. <+>: test %eax,%eax
  48. <+>: je 0x4015e6 <main()+>
  49. <+>: sub $0x1,%eax
  50. <+>: test %eax,%eax
  51. <+>: je 0x40161b <main()+>
  52. <+>: sub $0x1,%eax
  53. <+>: test %eax,%eax
  54. <+>: je 0x4016f8 <main()+>
  55. <+>: sub $0x1,%eax
  56. <+>: test %eax,%eax
  57. <+>: je 0x401712 <main()+>
  58. <+>: sub $0x1,%eax
  59. <+>: ud2
  60. <+>: mov %edx,%eax
  61. <+>: mov %ecx,%edx
  62. <+>: mov %edx,%ecx
  63. <+>: cmp $0x2,%ecx
  64. <+>: je 0x4015f3 <main()+>
  65. <+>: jmp 0x401642 <main()+>
  66. <+>: mov %eax,(%esp)
  67. <+>: call 0x4017c4 <__cxa_begin_catch>
  68. <+>: mov %eax,-0x1c(%ebp)
  69. <+>: lea -0x28(%ebp),%eax
  70. <+>: mov %eax,(%esp)
  71. <+>: call 0x4017ec <_ZSt17current_exceptionv>
  72. <+>: lea -0x28(%ebp),%eax
  73. <+>: mov %eax,(%esp)
  74. <+>: movl $0x2,-0x58(%ebp)
  75. <+>: call 0x4017e4 <_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE>
  76. <+>: mov %edx,-0x60(%ebp)
  77. <+>: mov %ecx,-0x64(%ebp)
  78. <+>: lea -0x28(%ebp),%eax
  79. <+>: mov %eax,%ecx
  80. <+>: call 0x40180c <_ZNSt15__exception_ptr13exception_ptrD1Ev>
  81. <+>: mov -0x60(%ebp),%eax
  82. <+>: mov %eax,-0x60(%ebp)
  83. <+>: mov -0x64(%ebp),%esi
  84. <+>: mov %esi,-0x64(%ebp)
  85. <+>: call 0x4017bc <__cxa_end_catch>
  86. <+>: mov -0x60(%ebp),%eax
  87. <+>: mov -0x64(%ebp),%edx
  88. <+>: cmp $0x1,%edx
  89. <+>: je 0x40164e <main()+>
  90. <+>: cmp $0x2,%edx
  91. <+>: je 0x401665 <main()+>
  92. <+>: jmp 0x4016b3 <main()+>
  93. <+>: mov %eax,(%esp)
  94. <+>: call 0x4017c4 <__cxa_begin_catch>
  95. <+>: mov (%eax),%eax
  96. <+>: mov %eax,-0x20(%ebp)
  97. <+>: call 0x4017bc <__cxa_end_catch>
  98. <+>: jmp 0x4015a7 <main()+>
  99. <+>: mov %eax,(%esp)
  100. <+>: call 0x4017c4 <__cxa_begin_catch>
  101. <+>: mov %eax,-0x24(%ebp)
  102. <+>: mov -0x24(%ebp),%eax
  103. <+>: mov (%eax),%eax
  104. <+>: add $0x8,%eax
  105. <+>: mov (%eax),%eax
  106. <+>: mov -0x24(%ebp),%edx
  107. <+>: mov %edx,%ecx
  108. <+>: call *%eax
  109. <+>: mov %eax,0x4(%esp)
  110. <+>: movl $0x6ff29a00,(%esp)
  111. <+>: movl $0x3,-0x58(%ebp)
  112. <+>: call 0x4017d4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
  113. <+>: movl $0x4017dc,(%esp)
  114. <+>: mov %eax,%ecx
  115. <+>: call 0x401814 <_ZNSolsEPFRSoS_E>
  116. <+>: sub $0x4,%esp
  117. <+>: call 0x4017bc <__cxa_end_catch>
  118. <+>: jmp 0x4015a7 <main()+>
  119. <+>: mov %eax,(%esp)
  120. <+>: call 0x4017c4 <__cxa_begin_catch>
  121. <+>: movl $0x404005,0x4(%esp)
  122. <+>: movl $0x6ff29a00,(%esp)
  123. <+>: movl $0x4,-0x58(%ebp)
  124. <+>: call 0x4017d4 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc>
  125. <+>: movl $0x4017dc,(%esp)
  126. <+>: mov %eax,%ecx
  127. <+>: call 0x401814 <_ZNSolsEPFRSoS_E>
  128. <+>: sub $0x4,%esp
  129. <+>: movl $0xffffffff,-0x58(%ebp)
  130. <+>: call 0x4017bc <__cxa_end_catch>
  131. <+>: jmp 0x4015a7 <main()+>
  132. <+>: mov %edx,-0x60(%ebp)
  133. <+>: call 0x4017bc <__cxa_end_catch>
  134. <+>: mov -0x60(%ebp),%eax
  135. <+>: mov %eax,(%esp)
  136. <+>: movl $0xffffffff,-0x58(%ebp)
  137. <+>: call 0x402788 <_Unwind_SjLj_Resume>
  138. <+>: mov %edx,-0x60(%ebp)
  139. <+>: movl $0x0,-0x58(%ebp)
  140. <+>: call 0x4017bc <__cxa_end_catch>
  141. <+>: mov -0x60(%ebp),%eax
  142. <+>: mov %eax,(%esp)
  143. <+>: movl $0xffffffff,-0x58(%ebp)
  144. <+>: call 0x402788 <_Unwind_SjLj_Resume>
  145. <+>: lea -0x5c(%ebp),%eax
  146. <+>: mov %eax,(%esp)
  147. <+>: call 0x402780 <_Unwind_SjLj_Unregister>
  148. <+>: mov -0x60(%ebp),%eax
  149. <+>: lea -0x10(%ebp),%esp
  150. <+>: pop %ecx
  151. <+>: pop %ebx
  152. <+>: pop %esi
  153. <+>: pop %edi
  154. <+>: pop %ebp
  155. <+>: lea -0x4(%ecx),%esp
  156. <+>: ret

下面的分析只列出不同的地方

支路控制代码:

可以看出,支路选路控制指令多而且复杂,还有就是跳转多。

最后是函数结束前。

可以看出在 sjlj 版本中,即使代码不发生异常,函数在进入与离开时都要为登记维护付出一此成本,当涉及异常代码时,支路选路控制更加复杂更多跳转。这里有一个成本比例,你的函数逻辑简单,上面的开销比重就越大,如果是频繁调用的轻量函数就要考虑不用exception这样的error handle。

还有就是当发生异常时,需要交给c++库去管理,不同异常处理模型的实现,有着不同的开销,本文并没有涉及到。只是单纯从c++库以外的代码进行分析,也足够看出他们之间有着一定的差别。

mingw32 exception在sjlj与dwarf差别-反汇编分析的更多相关文章

  1. Linux下简单C语言小程序的反汇编分析

    韩洋原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在开始,本文为因为参加MOO ...

  2. 反汇编分析objc函数枢纽objc_msgSend

    在分析objc_msgSend之前,先来搞清楚另一个问题. 函数是什么?可能会答 void foo(void) {} 像这样就是一个函数.或者函数包括函数原型和函数定义,是一段执行某样功能的机器代码. ...

  3. objc反汇编分析,block函数块为何物?

    上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西. 我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境. ...

  4. 反汇编分析__stdcall和__cdecl的异同

    C++代码如下:.h头文件 #pragma once#ifdef DLLTestAPI#else#define DLLTestAPI _declspec(dllimport)#endifint DLL ...

  5. objc反汇编分析__strong和__weak

    如题所说反汇编看__strong和__weak的真实样子,代码列举自然多,篇幅长不利于阅读,我就先搬出结论,后面是分析. 在NON-ARC环境,__strong和__weak不起作用.相反在ARC环境 ...

  6. 反汇编分析NSString,你印象中的NSString是这样吗

    我们先来定义三个NSString -(void) testNSString { NSString* a = @"abc"; NSString* b = [NSString stri ...

  7. objc反汇编分析,手工逆向libsystem_blocks.dylib

    上一篇<block函数块为何物?>介绍了在函数中定义的block函数块的反汇编实现,我在文中再三指出__block变量和block函数块自始还都是stack-based的,还不完全适合在离 ...

  8. 实验4 汇编应用编程和c语言程序反汇编分析

    1. 实验任务1 教材「实验9 根据材料编程」(P187-189)编程:在屏幕中间分别显示绿色.绿底红色.白底蓝色的字符串'welcome to masm!'. 解题思路:根据学习的知识,我知道该页在 ...

  9. 反汇编分析C++代码

    编译环境:Windows 10 + VS2015 1.问题引入 在Win32环境下,CPU小端模式,参数用栈来传递,写出输出结果. 代码如下: int main() { long long a = 1 ...

随机推荐

  1. mysql 赋权语句

    grant all privileges on phplampDB.* to phplamp@localhost identified by '1234';

  2. Collection接口和list,set子类

    Collection接口常用的子接口有:List接口.Set接口List接口常用的子类有:ArrayList类.LinkedList类Set接口常用的子类有:HashSet类.LinkedHashSe ...

  3. 集合框架之ArrayList -Java

    ArrayList 1.与数组的区别 如果要存放多个对象,可以使用数组,但是数组会有长度的限制,会出现不够用或者是浪费的情况. 为了解决数组的局限性引入了容器的概念,最常用的容器就是ArrayList ...

  4. 加速国内Github代码下载速度

    标题: 加速国内Github代码下载速度 作者: 梦幻之心星 347369787@QQ.com 标签: [Github, 代码, 下载] 目录: 代码 日期: 2019-10-27 目录 前提说明 解 ...

  5. AD17无法复制原理图到Word的解决方法

    标题: 解决AD17无法复制原理图到WORD 作者: 梦幻之心星 347369787@QQ.com 标签: [AD, Word, 原理图] 目录: 软件 日期: 2019-3-17 目录 前提说明: ...

  6. 相邻元素margin的自动合并与float的坑

    css中相邻元素的margin其实是会自动合并的,且取较大值. <!DOCTYPE html> <html lang="en"> <head> ...

  7. Rocket - debug - TLDebugModuleInner - Hart Bus Access

    https://mp.weixin.qq.com/s/deNMEyJ1idJDVoZwwo0A1A 简单介绍TLDebugModuleInner中核心总线访问(Hart Bus Access). 参考 ...

  8. 从SpringBoot源码分析 主程序配置类加载过程

    1.@Import(AutoConfigurationPackages.Registrar.class) 初始SpringBoot 我们知道在SpringBoot 启动类上有一个@SpringBoot ...

  9. Java实现 蓝桥杯 算法提高VIP Substrings(暴力)

    试题 算法提高 Substrings 问题描述 You are given a number of case-sensitive strings of alphabetic characters, f ...

  10. Java实现 蓝桥杯 算法训练 相邻数对(暴力)

    试题 算法训练 相邻数对 问题描述 给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1. 输入格式 输入的第一行包含一个整数n,表示给定整数的个数. 第二行包含所给定的n个整数. 输出格式 ...