写点什么

C++ 学习 ------csetjmp 头文件的源码学习

作者:桑榆
  • 2022 年 9 月 13 日
    广东
  • 本文字数:2160 字

    阅读完需:约 7 分钟

引言

csetjmp 是 C++对 setjmp.h 头文件的封装,通过这个头文件提供的工具允许程序员通过提供执行跳转的方法来绕过正常的函数调用和返回规程,从而保留调用环境。其中定义了一些函数和宏定义来完成这项工作。

setjmp.h

代码参考:http://www.aospxref.com/android-13.0.0_r3/xref/bionic/libc/include/setjmp.h

变量定义 sigjmp_buf 和 jmp_buf

根据不同平台定义了不同的数组别名 sigjmp_buf 和 jmp_buf,其中保存的元素都是 long,sigjmp_buf 中的元素多 1 个。

45  #if defined(__aarch64__)46  #define _JBLEN 3247  #elif defined(__arm__)48  #define _JBLEN 6449  #elif defined(__i386__)50  #define _JBLEN 1051  #elif defined(__x86_64__)52  #define _JBLEN 1153  #endif54  55  typedef long sigjmp_buf[_JBLEN + 1];56  typedef long jmp_buf[_JBLEN];
复制代码

宏函数定义 setjmp

68  #define setjmp(__env) setjmp(__env)
复制代码

这里实际上调用的还是函数实现的 setjmp

函数定义

定义了 setjmp 和 longjmp 函数,sigsetjmp 和 siglongjmp 函数

62  int _setjmp(jmp_buf __env) __returns_twice;63  __noreturn void _longjmp(jmp_buf __env, int __value);64  65  int setjmp(jmp_buf __env) __returns_twice;66  __noreturn void longjmp(jmp_buf __env, int __value);67  68  #define setjmp(__env) setjmp(__env)69  70  int sigsetjmp(sigjmp_buf __env, int __save_signal_mask);71  __noreturn void siglongjmp(sigjmp_buf __env, int __value);
复制代码

setjmp 函数---保存函数调用环境

代码参考:http://www.aospxref.com/android-13.0.0_r3/xref/external/musl/src/setjmp/x86_64/setjmp.s可以看到 setjmp 函数入参为 jmp_buf 数组首地址,观察汇编代码,该入参存储在 rdi 中,依次将 rbx,rbp,r12,r13,r14,r15,rsp+8,(%rsp)位置上的值都保存到该数组中,即保存函数调用环境。setjmp 第一次返回 0,第二次(即被 longjmp 加载函数调用环境之后)返回值由 longjmp 的第二个输入参数决定。

1 /* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */2 .global __setjmp3 .global _setjmp4 .global setjmp5 .type __setjmp,@function6 .type _setjmp,@function7 .type setjmp,@function8 __setjmp:9 _setjmp:10 setjmp:11 	mov %rbx,(%rdi)         /* rdi is jmp_buf, move registers onto it */12 	mov %rbp,8(%rdi)13 	mov %r12,16(%rdi)14 	mov %r13,24(%rdi)15 	mov %r14,32(%rdi)16 	mov %r15,40(%rdi)17 	lea 8(%rsp),%rdx        /* this is our rsp WITHOUT current ret addr */18 	mov %rdx,48(%rdi)19 	mov (%rsp),%rdx         /* save return addr ptr for new rip */20 	mov %rdx,56(%rdi)21 	xor %eax,%eax           /* always return 0 */22 	ret
复制代码

longjump---加载函数调用环境

代码参考:http://www.aospxref.com/android-13.0.0_r3/xref/external/musl/src/setjmp/x86_64/longjmp.s可以看到,依次还原输入的 jmp_buf,然后跳转到之前保存的 rsp 地址中;注意,这里还对输入参数 int __value 做了处理,保存在 esi 中,如果不等于 0,则 eax=__value,如果等于 0 则 eax = 1.

1 /* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */2 .global _longjmp3 .global longjmp4 .type _longjmp,@function5 .type longjmp,@function6 _longjmp:7 longjmp:8 	xor %eax,%eax9 	cmp $1,%esi             /* CF = val ? 0 : 1 */10 	adc %esi,%eax           /* eax = val + !val */11 	mov (%rdi),%rbx         /* rdi is the jmp_buf, restore regs from it */12 	mov 8(%rdi),%rbp13 	mov 16(%rdi),%r1214 	mov 24(%rdi),%r1315 	mov 32(%rdi),%r1416 	mov 40(%rdi),%r1517 	mov 48(%rdi),%rsp18 	jmp *56(%rdi)           /* goto saved address without altering rsp */
复制代码

有点类似于 goto 语句,又重新跳回某处执行,不过这里是将之前某处的指令执行现场保存下来,然后再加载执行。

sigsetjmp

代码参考:http://www.aospxref.com/android-13.0.0_r3/xref/external/musl/src/signal/x86_64/sigsetjmp.s入参是:int sigsetjmp(sigjmp_buf __env, int __save_signal_mask)分别存储在 rdi 和 esi 中通过__save_signal_mask 判断,如果等于 0,就跳转到标号 1 的位置;否则就保存信息到 sigjmp_buf 数组中

1 .global sigsetjmp2 .global __sigsetjmp3 .type sigsetjmp,@function4 .type __sigsetjmp,@function5 sigsetjmp:6 __sigsetjmp:7 	test %esi,%esi8 	jz 1f9 10 	popq 64(%rdi)11 	mov %rbx,72+8(%rdi)12 	mov %rdi,%rbx13 14 	call setjmp@PLT15 16 	pushq 64(%rbx)17 	mov %rbx,%rdi18 	mov %eax,%esi19 	mov 72+8(%rbx),%rbx20 21 .hidden __sigsetjmp_tail22 	jmp __sigsetjmp_tail23 24 1:	jmp setjmp@PLT
复制代码

siglongjmp

代码参考:http://www.aospxref.com/android-13.0.0_r3/xref/external/musl/src/signal/siglongjmp.c借助 longjmp 进行实现

1  #include <setjmp.h>2  #include <signal.h>3  #include "syscall.h"4  #include "pthread_impl.h"5  6  _Noreturn void siglongjmp(sigjmp_buf buf, int ret)7  {8  	longjmp(buf, ret);9  }
复制代码


发布于: 56 分钟前阅读数: 11
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020.02.29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
C++学习------csetjmp头文件的源码学习_c++_桑榆_InfoQ写作社区