setjmp和longjump原理
来源:互联网 发布:mac os lion无法升级 编辑:程序博客网 时间:2024/06/01 11:57
在讨论setjmp的实现原理之前,我们先看一个setjmp和longjmp的例子:
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf jmpbuf_1;
int g_a = 0;
void test(int index, int *local_val)
{
g_a = 1111;
*local_val = 2222;
longjmp(jmpbuf_1,index);
}
int main(int argc, char *argv[])
{
int ret = 0, index = 0;
int l_a=1;
if(argc!=2)
index = 1;
else
{
index = atoi(argv[1]);
if(index==0 || index>2)
index = 1;
}
ret = setjmp(jmpbuf_1);
if(ret == 0)
{
printf("Orig setjmp\n");
}
else if(ret == 1)
{
printf("Return From longjmp 1\n");
printf("Global_a: %d\n", g_a);
printf("Local_a: %d\n", l_a);
return 0;
}
else if(ret == 2)
{
printf("Return From longjmp 2\n");
printf("Global_a: %d\n", g_a);
printf("Local_a: %d\n", l_a);
return 0;
}
test(index, &l_a);
return 0;
}
下面是测试代码在不同的编译优化条件下的执行结果:
wangyao@wangyao-laptop:~/Test/sys$ gcc longjmp.c -O2
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 1
Orig setjmp
Return From longjmp 1
Global_a: 1111
Local_a: 1
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 2
Orig setjmp
Return From longjmp 2
Global_a: 1111
Local_a: 1
wangyao@wangyao-laptop:~/Test/sys$ gcc longjmp.c
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 1
Orig setjmp
Return From longjmp 1
Global_a: 1111
Local_a: 2222
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 2
Orig setjmp
Return From longjmp 2
Global_a: 1111
Local_a: 2222
man 3 longjmp中提到:
The values of automatic variables are unspecified after a call to longjmp() if they meet all the following criteria:
· they are local to the function that made the corresponding setjmp(3) call;
· their values are changed between the calls to setjmp(3) and longjmp(); and
· they are not declared as volatile.
posix中没有定义自动变量在调用longjmp后的动作。
在调用longjmp后,自动变量、全局变脸、寄存器变量、静态变量和易失变量的变化是不同的。全局变量、静态变量和易失变量的分配要么是在data段, 要么是在bss段中,上下文切换对它们基本上没有什么影响;但是对于栈和寄存器来讲,上下文切换就有很大的影响。因此对于一些自动变量、寄存器变量就可能 会在longjmp的时候发生变化,当然这个还与编译时候的优化条件有关。
下面再来讨论setjmp和longjmp的实现原理,这里我们只关注x86架构的实现,具体来讲是i386的实现。
首先,回想一下x86架构下面,函数调用过程栈的变化情况:
在x86架构下,调用函数的时候,首先自右向左将压栈,(早期的gcc版本是采用push $变量的方法;后期gcc使用的是sub $num,esp 然后mov $变量,esp+$num的方法)
接下来就是call func了,call func的动作是,将下一指令地址压栈,jmp到func的地址执行。
func刚开始就执行一个push ebp,将原先的栈基址保存下来。
setjmp具体的实现在:
glibc-2.7/sysdeps/i386/setjmp.S
将寄存器中的值保存到jmp_buf中。
ENTRY (BP_SYM (__sigsetjmp))
ENTER
movl JMPBUF(%esp), %eax
CHECK_BOUNDS_BOTH_WIDE (%eax, JMPBUF(%esp), $JB_SIZE)
/* Save registers. */
movl %ebx, (JB_BX*4)(%eax)
movl %esi, (JB_SI*4)(%eax)
movl %edi, (JB_DI*4)(%eax)
leal JMPBUF(%esp), %ecx /* Save SP as it will be after we return. */
#ifdef PTR_MANGLE
PTR_MANGLE (%ecx)
#endif
movl %ecx, (JB_SP*4)(%eax)
movl PCOFF(%esp), %ecx /* Save PC we are returning to now. */
#ifdef PTR_MANGLE
PTR_MANGLE (%ecx)
#endif
movl %ecx, (JB_PC*4)(%eax)
LEAVE /* pop frame pointer to prepare for tail-call. */
movl %ebp, (JB_BP*4)(%eax) /* Save caller's frame pointer. */
#if defined NOT_IN_libc && defined IS_IN_rtld
/* In ld.so we never save the signal mask. */
xorl %eax, %eax
ret
#else
/* Make a tail call to __sigjmp_save; it takes the same args. */
jmp __sigjmp_save
#endif
longjmp的具体实现在:
glibc-2.7/sysdeps/i386/__longjmp.S
将jmp_buf中的值恢复到相应的寄存器中,并将longjmp的第2个参数作为返回值返回,由于longjmp中将寄存器的eip设置回setjmp时候的值。longjmp的返回值在程序逻辑上就是setjmp的返回值了。
#ifdef PTR_DEMANGLE
movl JBUF(%esp), %eax /* User's jmp_buf in %eax. */
CHECK_BOUNDS_BOTH_WIDE (%eax, JBUF(%esp), $JB_SIZE)
/* Save the return address now. */
movl (JB_PC*4)(%eax), %edx
/* Get the stack pointer. */
movl (JB_SP*4)(%eax), %ecx
PTR_DEMANGLE (%edx)
PTR_DEMANGLE (%ecx)
cfi_def_cfa(%eax, 0)
cfi_register(%eip, %edx)
cfi_register(%esp, %ecx)
cfi_offset(%ebx, JB_BX*4)
cfi_offset(%esi, JB_SI*4)
cfi_offset(%edi, JB_DI*4)
cfi_offset(%ebp, JB_BP*4)
/* Restore registers. */
movl (JB_BX*4)(%eax), %ebx
movl (JB_SI*4)(%eax), %esi
movl (JB_DI*4)(%eax), %edi
movl (JB_BP*4)(%eax), %ebp
cfi_restore(%ebx)
cfi_restore(%esi)
cfi_restore(%edi)
cfi_restore(%ebp)
movl VAL(%esp), %eax /* Second argument is return value. */
movl %ecx, %esp
#else
movl JBUF(%esp), %ecx /* User's jmp_buf in %ecx. */
CHECK_BOUNDS_BOTH_WIDE (%ecx, JBUF(%esp), $JB_SIZE)
movl VAL(%esp), %eax /* Second argument is return value. */
/* Save the return address now. */
movl (JB_PC*4)(%ecx), %edx
/* Restore registers. */
movl (JB_BX*4)(%ecx), %ebx
movl (JB_SI*4)(%ecx), %esi
movl (JB_DI*4)(%ecx), %edi
movl (JB_BP*4)(%ecx), %ebp
movl (JB_SP*4)(%ecx), %esp
#endif
/* Jump to saved PC. */
jmp *%edx
END (BP_SYM (__longjmp))
-----------------------------------------------------------------------------
对于setjmp和longjmp的使用还应该注意一些问题:
setjmp和longjmp函数,这两个函数在跳转时会带信号屏蔽字跳转,在信号处理程序(hanlder)中使用longjmp会导致后来产生的这种信号被屏蔽,无法调用此种信号的信号处理函数。
POSIX.1 也没有具体说明setjmp和longjmp对信号屏蔽字的作用,而是定义了两个新函数: sigsetjmp和siglongjmp。
sigsetjmp在参数为非0的时候,会保存进程的当前信号屏蔽字;在调用siglongjmp的时候,再恢复保存的信号屏蔽字。
一段演示siglongjmp的代码:
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
/* Jump buffer */
static sigjmp_buf jmpbuf;
/* Signal handler */
static void myfunc(int signo)
{
printf("SIGQUIT\n");
sleep(1);
siglongjmp(jmpbuf, 1);
}
int main()
{
char *p = NULL;
struct sigaction act;
act.sa_handler = myfunc;
act.sa_flags = SA_INTERRUPT;
sigemptyset(&act.sa_mask);
if (sigaction(SIGQUIT, &act, NULL) < 0)
perror("sigaction");
if (sigsetjmp(jmpbuf, 1) == 1)
{
printf("I'm jumped\n");
}
else
{
/* SIGSEGV */
raise(SIGQUIT);
}
/* JUMP */
printf("I'm here\n");
return 0;
}
运行结果为:
wangyao@wangyao-laptop:~/Test/sys$ ./a.out
SIGQUIT
I'm jumped
I'm here
参考:
http://blog.csdn.net/dai_weitao/archive/2007/09/28/1804610.aspx
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf jmpbuf_1;
int g_a = 0;
void test(int index, int *local_val)
{
g_a = 1111;
*local_val = 2222;
longjmp(jmpbuf_1,index);
}
int main(int argc, char *argv[])
{
int ret = 0, index = 0;
int l_a=1;
if(argc!=2)
index = 1;
else
{
index = atoi(argv[1]);
if(index==0 || index>2)
index = 1;
}
ret = setjmp(jmpbuf_1);
if(ret == 0)
{
printf("Orig setjmp\n");
}
else if(ret == 1)
{
printf("Return From longjmp 1\n");
printf("Global_a: %d\n", g_a);
printf("Local_a: %d\n", l_a);
return 0;
}
else if(ret == 2)
{
printf("Return From longjmp 2\n");
printf("Global_a: %d\n", g_a);
printf("Local_a: %d\n", l_a);
return 0;
}
test(index, &l_a);
return 0;
}
下面是测试代码在不同的编译优化条件下的执行结果:
wangyao@wangyao-laptop:~/Test/sys$ gcc longjmp.c -O2
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 1
Orig setjmp
Return From longjmp 1
Global_a: 1111
Local_a: 1
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 2
Orig setjmp
Return From longjmp 2
Global_a: 1111
Local_a: 1
wangyao@wangyao-laptop:~/Test/sys$ gcc longjmp.c
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 1
Orig setjmp
Return From longjmp 1
Global_a: 1111
Local_a: 2222
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 2
Orig setjmp
Return From longjmp 2
Global_a: 1111
Local_a: 2222
man 3 longjmp中提到:
The values of automatic variables are unspecified after a call to longjmp() if they meet all the following criteria:
· they are local to the function that made the corresponding setjmp(3) call;
· their values are changed between the calls to setjmp(3) and longjmp(); and
· they are not declared as volatile.
posix中没有定义自动变量在调用longjmp后的动作。
在调用longjmp后,自动变量、全局变脸、寄存器变量、静态变量和易失变量的变化是不同的。全局变量、静态变量和易失变量的分配要么是在data段, 要么是在bss段中,上下文切换对它们基本上没有什么影响;但是对于栈和寄存器来讲,上下文切换就有很大的影响。因此对于一些自动变量、寄存器变量就可能 会在longjmp的时候发生变化,当然这个还与编译时候的优化条件有关。
下面再来讨论setjmp和longjmp的实现原理,这里我们只关注x86架构的实现,具体来讲是i386的实现。
首先,回想一下x86架构下面,函数调用过程栈的变化情况:
在x86架构下,调用函数的时候,首先自右向左将压栈,(早期的gcc版本是采用push $变量的方法;后期gcc使用的是sub $num,esp 然后mov $变量,esp+$num的方法)
接下来就是call func了,call func的动作是,将下一指令地址压栈,jmp到func的地址执行。
func刚开始就执行一个push ebp,将原先的栈基址保存下来。
setjmp具体的实现在:
glibc-2.7/sysdeps/i386/setjmp.S
将寄存器中的值保存到jmp_buf中。
ENTRY (BP_SYM (__sigsetjmp))
ENTER
movl JMPBUF(%esp), %eax
CHECK_BOUNDS_BOTH_WIDE (%eax, JMPBUF(%esp), $JB_SIZE)
/* Save registers. */
movl %ebx, (JB_BX*4)(%eax)
movl %esi, (JB_SI*4)(%eax)
movl %edi, (JB_DI*4)(%eax)
leal JMPBUF(%esp), %ecx /* Save SP as it will be after we return. */
#ifdef PTR_MANGLE
PTR_MANGLE (%ecx)
#endif
movl %ecx, (JB_SP*4)(%eax)
movl PCOFF(%esp), %ecx /* Save PC we are returning to now. */
#ifdef PTR_MANGLE
PTR_MANGLE (%ecx)
#endif
movl %ecx, (JB_PC*4)(%eax)
LEAVE /* pop frame pointer to prepare for tail-call. */
movl %ebp, (JB_BP*4)(%eax) /* Save caller's frame pointer. */
#if defined NOT_IN_libc && defined IS_IN_rtld
/* In ld.so we never save the signal mask. */
xorl %eax, %eax
ret
#else
/* Make a tail call to __sigjmp_save; it takes the same args. */
jmp __sigjmp_save
#endif
longjmp的具体实现在:
glibc-2.7/sysdeps/i386/__longjmp.S
将jmp_buf中的值恢复到相应的寄存器中,并将longjmp的第2个参数作为返回值返回,由于longjmp中将寄存器的eip设置回setjmp时候的值。longjmp的返回值在程序逻辑上就是setjmp的返回值了。
#ifdef PTR_DEMANGLE
movl JBUF(%esp), %eax /* User's jmp_buf in %eax. */
CHECK_BOUNDS_BOTH_WIDE (%eax, JBUF(%esp), $JB_SIZE)
/* Save the return address now. */
movl (JB_PC*4)(%eax), %edx
/* Get the stack pointer. */
movl (JB_SP*4)(%eax), %ecx
PTR_DEMANGLE (%edx)
PTR_DEMANGLE (%ecx)
cfi_def_cfa(%eax, 0)
cfi_register(%eip, %edx)
cfi_register(%esp, %ecx)
cfi_offset(%ebx, JB_BX*4)
cfi_offset(%esi, JB_SI*4)
cfi_offset(%edi, JB_DI*4)
cfi_offset(%ebp, JB_BP*4)
/* Restore registers. */
movl (JB_BX*4)(%eax), %ebx
movl (JB_SI*4)(%eax), %esi
movl (JB_DI*4)(%eax), %edi
movl (JB_BP*4)(%eax), %ebp
cfi_restore(%ebx)
cfi_restore(%esi)
cfi_restore(%edi)
cfi_restore(%ebp)
movl VAL(%esp), %eax /* Second argument is return value. */
movl %ecx, %esp
#else
movl JBUF(%esp), %ecx /* User's jmp_buf in %ecx. */
CHECK_BOUNDS_BOTH_WIDE (%ecx, JBUF(%esp), $JB_SIZE)
movl VAL(%esp), %eax /* Second argument is return value. */
/* Save the return address now. */
movl (JB_PC*4)(%ecx), %edx
/* Restore registers. */
movl (JB_BX*4)(%ecx), %ebx
movl (JB_SI*4)(%ecx), %esi
movl (JB_DI*4)(%ecx), %edi
movl (JB_BP*4)(%ecx), %ebp
movl (JB_SP*4)(%ecx), %esp
#endif
/* Jump to saved PC. */
jmp *%edx
END (BP_SYM (__longjmp))
-----------------------------------------------------------------------------
对于setjmp和longjmp的使用还应该注意一些问题:
setjmp和longjmp函数,这两个函数在跳转时会带信号屏蔽字跳转,在信号处理程序(hanlder)中使用longjmp会导致后来产生的这种信号被屏蔽,无法调用此种信号的信号处理函数。
POSIX.1 也没有具体说明setjmp和longjmp对信号屏蔽字的作用,而是定义了两个新函数: sigsetjmp和siglongjmp。
sigsetjmp在参数为非0的时候,会保存进程的当前信号屏蔽字;在调用siglongjmp的时候,再恢复保存的信号屏蔽字。
一段演示siglongjmp的代码:
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
/* Jump buffer */
static sigjmp_buf jmpbuf;
/* Signal handler */
static void myfunc(int signo)
{
printf("SIGQUIT\n");
sleep(1);
siglongjmp(jmpbuf, 1);
}
int main()
{
char *p = NULL;
struct sigaction act;
act.sa_handler = myfunc;
act.sa_flags = SA_INTERRUPT;
sigemptyset(&act.sa_mask);
if (sigaction(SIGQUIT, &act, NULL) < 0)
perror("sigaction");
if (sigsetjmp(jmpbuf, 1) == 1)
{
printf("I'm jumped\n");
}
else
{
/* SIGSEGV */
raise(SIGQUIT);
}
/* JUMP */
printf("I'm here\n");
return 0;
}
运行结果为:
wangyao@wangyao-laptop:~/Test/sys$ ./a.out
SIGQUIT
I'm jumped
I'm here
参考:
http://blog.csdn.net/dai_weitao/archive/2007/09/28/1804610.aspx
0 0
- setjmp和longjump原理
- setjmp和longjump 用法
- setjump和longjump
- setjump和longjump
- setjump和longjump
- setjump 和 longjump
- setjmp和longjmp的实现原理与应用
- setjmp与longjmp原理分析
- setjmp()和longjmp()
- setjmp和longjmp函数
- setjmp()和longjmp()
- longjmp 和 setjmp
- setjmp和longjmp【转】
- setjmp和longjmp
- c setjmp 和 longjmp
- setjmp和longjmp
- setjmp和longjmp
- setjmp 和 longjmp 函数
- android layout 相关
- 模拟新浪微博随便看看栏目
- LINUX操作系统学习--1操作系统概述
- JS中的日期(Date)对象
- 指针作为函数的参数传递的问题
- setjmp和longjump原理
- android 滚动条 相关属性
- android 更具File 启动不同的intent
- IP数据包格式
- android学习之 webview 原生与js的交互
- swift dictonary convert to (转为 )json
- 常见音频编码格式(注:编码格式不同于文件格式)
- 关于AppDelegate及内部方法的简单介绍
- android.support.v8.renderscript相关类找不到的错误解决