|
|
发表于 2005-10-24 15:45:41
|
显示全部楼层
非本地跳转通常是通过修改堆栈指针和程序计数器来实现的,所以不会改变你说的这些内容。
例如,下面是 GLibc 中的部分代码,它实现了 setjmp。
- ENTRY (BP_SYM (setjmp))
- /* Note that we have to use a non-exported symbol in the next
- jump since otherwise gas will emit it as a jump through the
- PLT which is what we cannot use here. */
- 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. */
- movl %ecx, (JB_SP*4)(%eax)
- movl PCOFF(%esp), %ecx /* Save PC we are returning to now. */
- 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. */
- /* Call __sigjmp_save. */
- pushl $1
- pushl 8(%esp)
- call BP_SYM (__sigjmp_save)
- popl %ecx
- popl %edx
- ret
- END (BP_SYM (setjmp))
复制代码
再看看对应的 longjmp 的代码:
- ENTRY (BP_SYM (__longjmp))
- ENTER
- 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
- /* Jump to saved PC. */
- jmp *%edx
- END (BP_SYM (__longjmp))
复制代码
这就是非本地跳转的原理,其实 fork 和 C++ 中的 try... throw... catch... 也利用了这个。 |
|