• setjmp/longjmp原理分析


    1、使用

    setjmp的man手册上可以看到使用方法:

    1 #include <setjmp.h>
    2 
    3 int setjmp(jmp_buf env);

    setjmp() saves the stack context/environment in env for later use by longjmp(3)

    RETURN VALUE
      setjmp() return 0 if returning directly, and non-zero when returning from longjmp(3) using the saved context.

    1  #include <setjmp.h>
    2 
    3  void longjmp(jmp_buf env, int val);

    longjmp() restores the environment saved by the last call of setjmp(3) with the corresponding env argument.

    由此可知:

    1、setjmp第一次返回0,第二次返回值是longjmp中的val参数。

    2、调用longjmp后,返回到setjmp函数的返回地址处。

    举例:

     1 #include <stdio.h>
     2 #include <setjmp.h>
     3 
     4 jmp_buf buf;
     5 int times = 0;
     6 
     7 void second(int* k)
     8 {
     9     printf("second times = %d
    ", ++(*k));
    10     longjmp(buf, 65536);
    11 }
    12 
    13 void first(int* k)
    14 {
    15     printf("first times = %d
    ", ++(*k));
    16     second(k);
    17 }
    18 
    19 int main(void)
    20 {
    21     int ret = setjmp(buf);
    22     if (ret == 0)
    23     {
    24         printf("1. ret is %d
    ", ret);
    25         first(&times);
    26     }
    27     else
    28     {
    29         printf("2. ret is %d
    ", ret);
    30     }
    31 }

    运行结果:

    1. ret is 0
    first times = 1
    second times = 2
    2. ret is 65536

    二、源码分析

    (以https://android.googlesource.com/platform/bionic/+/6719500/libc/arch-x86/bionic/setjmp.S中的setjmp为例分析,其他类似)

    jump_buf是个数组:

    1 #define _JBLEN  10      /* size, in longs, of a jmp_buf */
    2 typedef long jmp_buf[_JBLEN]; 

    以下是源码:

     1 #include <machine/asm.h>
     2 /*
     3  * C library -- setjmp, longjmp
     4  *
     5  *    longjmp(a,v)
     6  * will generate a "return(v)" from the last call to
     7  *    setjmp(a)
     8  * by restoring registers from the stack.
     9  * The previous signal state is restored.
    10  */
    11 ENTRY(setjmp)
    # 12-20行是为了屏蔽信号,可不关注
    12 PIC_PROLOGUE 13 pushl $0      # sigblock的参数 14 #ifdef PIC 15 call PIC_PLT(_C_LABEL(sigblock)) 16 #else 17 call _C_LABEL(sigblock) 18 #endif 19 addl $4,%esp    # 返回上述 pushl $0时的压栈 20 PIC_EPILOGUE
    # 以下是主要代码
    21 movl 4(%esp),%ecx   # 将env地址 -> ecx 22 movl 0(%esp),%edx   # 返回地址 -> edx
    # 23-29行 讲edx、ebx...eax的值保存到env中
    23 movl %edx, 0(%ecx)  24 movl %ebx, 4(%ecx) 25 movl %esp, 8(%ecx) 26 movl %ebp,12(%ecx) 27 movl %esi,16(%ecx) 28 movl %edi,20(%ecx) 29 movl %eax,24(%ecx)  # eax中保存的是sigblock的返回值
    # setjmp返回0
    30 xorl %eax,%eax 31 ret 32 END(setjmp)
    # 到此时,env[0]保存返回地址, env[1]保存ebx的值, ..., env[6]保存eax的值

    # 下面是longjmp的代码
    33 ENTRY(longjmp) 34 movl 4(%esp),%edx  # 将env的地址 -> edx 35 PIC_PROLOGUE 36 pushl 24(%edx)    # env[6], 即setjmp中sigblock返回值压栈,当做sigsetmask的参数 37 #ifdef PIC 38 call PIC_PLT(_C_LABEL(sigsetmask)) 39 #else 40 call _C_LABEL(sigsetmask) 41 #endif 42 addl $4,%esp    # 回复堆栈 43 PIC_EPILOGUE
    # 上面 33-43行, 是为了恢复信号

    # 下面是longjmp的主代码
    44 movl 4(%esp),%edx  # 将env地址 -> edx 45 movl 8(%esp),%eax  # val -> eax 46 movl 0(%edx),%ecx  # setjmp函数返回地址 -> ecx

    # 下面是恢复ebx、esp ... edi等
    47 movl 4(%edx),%ebx 48 movl 8(%edx),%esp 49 movl 12(%edx),%ebp 50 movl 16(%edx),%esi 51 movl 20(%edx),%edi
    # 下面三行确保eax(即val, longjmp返回到setjmp时的返回值)不为零
    # 如果eax为零则加一,否则保持原值
    52 testl %eax,%eax 53 jnz 1f 54 incl %eax
    # 这一步非常重要, 将ecx(setjmp的返回地址) -> longjmp的返回地址
    # 这样,ret返回后,就跳转到了setjmp的返回地址
    55 1: movl %ecx,0(%esp) 56 ret 57 END(longjmp)
  • 相关阅读:
    位运算符设置权限
    urlencode、urldecode、rawurlencode、rawurldecod
    二分查找法的mid值 整数溢出问题
    GIT 常用命令
    nginx配置反向代理转发
    PHP实现无限极分类
    PHP面试题目整理(持续更新)
    去除input的默认样式
    git 常用指令
    数组去重
  • 原文地址:https://www.cnblogs.com/ym65536/p/4984339.html
Copyright © 2020-2023  润新知