[英]How does longjmp work?
I need to understand HOW longjmp function works;我需要了解 longjmp function 的工作原理; I know what it does, but I need to know how it does it.
我知道它做了什么,但我需要知道它是怎么做的。
I tried to disas the code in gdb but I can't understand some steps.我试图取消 gdb 中的代码,但我无法理解某些步骤。 The code is:
代码是:
0xb7ead420 <siglongjmp+0>: push %ebp
0xb7ead421 <siglongjmp+1>: mov %esp,%ebp
0xb7ead423 <siglongjmp+3>: sub $0x18,%esp
0xb7ead426 <siglongjmp+6>: mov %ebx,-0xc(%ebp)
0xb7ead429 <siglongjmp+9>: call 0xb7e9828f <_Unwind_Find_FDE@plt+119>
0xb7ead42e <siglongjmp+14>: add $0x12bbc6,%ebx
0xb7ead434 <siglongjmp+20>: mov %esi,-0x8(%ebp)
0xb7ead437 <siglongjmp+23>: mov 0xc(%ebp),%esi
0xb7ead43a <siglongjmp+26>: mov %edi,-0x4(%ebp)
0xb7ead43d <siglongjmp+29>: mov 0x8(%ebp),%edi
0xb7ead440 <siglongjmp+32>: mov %esi,0x4(%esp)
0xb7ead444 <siglongjmp+36>: mov %edi,(%esp)
0xb7ead447 <siglongjmp+39>: call 0xb7ead4d0
0xb7ead44c <siglongjmp+44>: mov 0x18(%edi),%eax
0xb7ead44f <siglongjmp+47>: test %eax,%eax
0xb7ead451 <siglongjmp+49>: jne 0xb7ead470 <siglongjmp+80>
0xb7ead453 <siglongjmp+51>: test %esi,%esi
0xb7ead455 <siglongjmp+53>: mov $0x1,%eax
0xb7ead45a <siglongjmp+58>: cmove %eax,%esi
0xb7ead45d <siglongjmp+61>: mov %esi,0x4(%esp)
0xb7ead461 <siglongjmp+65>: mov %edi,(%esp)
0xb7ead464 <siglongjmp+68>: call 0xb7ead490
0xb7ead469 <siglongjmp+73>: lea 0x0(%esi,%eiz,1),%esi
0xb7ead470 <siglongjmp+80>: lea 0x1c(%edi),%eax
0xb7ead473 <siglongjmp+83>: movl $0x0,0x8(%esp)
0xb7ead47b <siglongjmp+91>: mov %eax,0x4(%esp)
0xb7ead47f <siglongjmp+95>: movl $0x2,(%esp)
0xb7ead486 <siglongjmp+102>: call 0xb7ead890 <sigprocmask>
0xb7ead48b <siglongjmp+107>: jmp 0xb7ead453 <siglongjmp+51>
Can someone briefly explain me the code, or indicate where I can find the source code in the system?有人可以简要解释一下代码,或者指出我可以在系统中找到源代码的位置吗?
Here is the i386 code for longjmp
, in the standard i386 ABI, without any crazy extensions for interaction with C++, exceptions, cleanup functions, signal mask, etc.:这是标准 i386 ABI 中
longjmp
的 i386 代码,没有任何与 C++、异常、清理函数、信号掩码等交互的疯狂扩展:
mov 4(%esp),%edx
mov 8(%esp),%eax
test %eax,%eax
jnz 1f
inc %eax
1:
mov (%edx),%ebx
mov 4(%edx),%esi
mov 8(%edx),%edi
mov 12(%edx),%ebp
mov 16(%edx),%ecx
mov %ecx,%esp
mov 20(%edx),%ecx
jmp *%ecx
Mostly, it restores the registers and stack as they were at the time of the corresponding setjmp()
.大多数情况下,它将寄存器和堆栈恢复为相应的
setjmp()
时的状态。 There is some additional cleanup required (fixing signal handling and unwinding pending stack handlers), as well as returning a different value as the apparent return value of setjmp , but restoring the state is the essence of the operation.需要进行一些额外的清理(修复信号处理和展开挂起的堆栈处理程序),以及返回与setjmp的明显返回值不同的值,但恢复 state 是操作的本质。
For it to work, the stack cannot be below the point at which setjmp was called.要使其工作,堆栈不能低于调用 setjmp 的点。 Longjmp is a brutish way to just forget everything that has been called below it down to the same level in the call stack (or function call nesting sequence) mostly by simply setting the stack pointer to the same frame it was when setjmp was called.
Longjmp 是一种粗暴的方法,它只需将堆栈指针设置为与调用 setjmp 时相同的帧,即可将所有在其下方调用的内容都忘记到调用堆栈(或 function 调用嵌套序列)中的同一级别。
For it to work cleanly, longjmp()
calls all the exit handlers for intermediate functions, so they can delete variables, and whatever other cleanup is normally done when a function returns.为了让它干净地工作,
longjmp()
调用中间函数的所有退出处理程序,因此它们可以删除变量,以及在 function 返回时通常进行的任何其他清理。 Resetting the stack to a point less deep releases all the auto
variables but if one of those is a FILE *
, the file needs to be closed and the i/o buffer freed too.将堆栈重置为不那么深的点会释放所有
auto
变量,但如果其中一个是FILE *
,则需要关闭文件并释放 i/o 缓冲区。
I think you need to see Procedure Activation Records and Call Stacks and Setjmp.h 's jmp_buf
's structure.我认为您需要查看过程激活记录和调用堆栈以及Setjmp.h的
jmp_buf
结构。
Quoted from Expert C Programming: Deep C Secrets:引自专家 C 编程:深度 C 秘密:
Setjmp saves a copy of the program counter and the current pointer to the top of the stack.
Setjmp 将程序计数器的副本和当前指针保存到堆栈顶部。 This saves some initial values, if you like.
如果您愿意,这会保存一些初始值。 Then longjmp restores these values effectively transferring control and resetting the state back to where you were when you did the save.
然后 longjmp 恢复这些值,有效地转移控制并将 state 重置回您进行保存时的位置。 It's termed "unwinding the stack", because you unroll activation records from the stack until you get to the saved one.
它被称为“展开堆栈”,因为您从堆栈中展开激活记录,直到您到达已保存的记录。
Have a look at page 153 also here. 也可以在这里查看第 153 页。
The stackframe will be highly dependent on the machine and the executable, but the idea is the same. stackframe 将高度依赖于机器和可执行文件,但想法是一样的。
In Windows X64 MASM在 Windows X64 MASM
.code
my_jmp_buf STRUCT
_Frame QWORD ?;
_Rbx QWORD ?;
_Rsp QWORD ?;
_Rbp QWORD ?;
_Rsi QWORD ?;
_Rdi QWORD ?;
_R12 QWORD ?;
_R13 QWORD ?;
_R14 QWORD ?;
_R15 QWORD ?;
_Rip QWORD ?;
_MxCsr DWORD ?;
_FpCsr WORD ?;
_Spare WORD ?;
_Xmm6 XMMWORD ?;
_Xmm7 XMMWORD ?;
_Xmm8 XMMWORD ?;
_Xmm9 XMMWORD ?;
_Xmm10 XMMWORD ?;
_Xmm11 XMMWORD ?;
_Xmm12 XMMWORD ?;
_Xmm13 XMMWORD ?;
_Xmm14 XMMWORD ?;
_Xmm15 XMMWORD ?;
my_jmp_buf ENDS
;extern "C" int my_setjmp(jmp_buf env);
public my_setjmp
my_setjmp PROC
mov rax, [rsp] ;save ip
mov (my_jmp_buf ptr[rcx])._Rip, rax
lea rax, [rsp + 8] ;save sp before call this function
mov (my_jmp_buf ptr[rcx])._Rsp, rax
mov (my_jmp_buf ptr[rcx])._Frame, rax
;save gprs
mov (my_jmp_buf ptr[rcx])._Rbx,rbx
mov (my_jmp_buf ptr[rcx])._Rbp,rbp
mov (my_jmp_buf ptr[rcx])._Rsi,rsi
mov (my_jmp_buf ptr[rcx])._Rdi,rdi
mov (my_jmp_buf ptr[rcx])._R12,r12
mov (my_jmp_buf ptr[rcx])._R13,r13
mov (my_jmp_buf ptr[rcx])._R14,r14
mov (my_jmp_buf ptr[rcx])._R15,r15
;save fp and xmm
stmxcsr (my_jmp_buf ptr[rcx])._MxCsr
fnstcw (my_jmp_buf ptr[rcx])._FpCsr
movdqa (my_jmp_buf ptr[rcx])._Xmm6,xmm6
movdqa (my_jmp_buf ptr[rcx])._Xmm7,xmm7
movdqa (my_jmp_buf ptr[rcx])._Xmm8,xmm8
movdqa (my_jmp_buf ptr[rcx])._Xmm9,xmm9
movdqa (my_jmp_buf ptr[rcx])._Xmm10,xmm10
movdqa (my_jmp_buf ptr[rcx])._Xmm11,xmm11
movdqa (my_jmp_buf ptr[rcx])._Xmm12,xmm12
movdqa (my_jmp_buf ptr[rcx])._Xmm13,xmm13
movdqa (my_jmp_buf ptr[rcx])._Xmm14,xmm14
movdqa (my_jmp_buf ptr[rcx])._Xmm15,xmm15
xor rax,rax
ret
my_setjmp ENDP
;extern "C" void my_longjmp(jmp_buf env, int value);
public my_longjmp
my_longjmp PROC
;restore fp and xmm
movdqa xmm15,(my_jmp_buf ptr[rcx])._Xmm15
movdqa xmm14,(my_jmp_buf ptr[rcx])._Xmm14
movdqa xmm13,(my_jmp_buf ptr[rcx])._Xmm13
movdqa xmm12,(my_jmp_buf ptr[rcx])._Xmm12
movdqa xmm11,(my_jmp_buf ptr[rcx])._Xmm11
movdqa xmm10,(my_jmp_buf ptr[rcx])._Xmm10
movdqa xmm9,(my_jmp_buf ptr[rcx])._Xmm9
movdqa xmm8,(my_jmp_buf ptr[rcx])._Xmm8
movdqa xmm7,(my_jmp_buf ptr[rcx])._Xmm7
movdqa xmm6,(my_jmp_buf ptr[rcx])._Xmm6
fldcw (my_jmp_buf ptr[rcx])._FpCsr
ldmxcsr (my_jmp_buf ptr[rcx])._MxCsr
;restore gprs
mov r15, (my_jmp_buf ptr[rcx])._R15
mov r14, (my_jmp_buf ptr[rcx])._R14
mov r13, (my_jmp_buf ptr[rcx])._R13
mov r12, (my_jmp_buf ptr[rcx])._R12
mov rdi, (my_jmp_buf ptr[rcx])._Rdi
mov rsi, (my_jmp_buf ptr[rcx])._Rsi
mov rbp, (my_jmp_buf ptr[rcx])._Rbp
mov rbx, (my_jmp_buf ptr[rcx])._Rbx
;retore sp
mov rsp, (my_jmp_buf ptr[rcx])._Rsp
;restore ip
mov rcx, (my_jmp_buf ptr[rcx])._Rip; must be the last instruction as rcx modified
;return value
mov rax, rdx
jmp rcx
my_longjmp ENDP
END
Here are versions of setmp and longjmp I wrote for a small clib subset (written and tested in Visual Studio 2008).这是我为一个小的 clib 子集编写的 setmp 和 longjmp 版本(在 Visual Studio 2008 中编写和测试)。 The assembly code is stored in a separate.asm file.
汇编代码存储在一个单独的.asm 文件中。
.586
.MODEL FLAT, C ; Flat memory model, C calling conventions.
;.STACK ; Not required for this example.
;.DATA ; Not required for this example.
.code
; Simple version of setjmp (x86-32 bit).
;
; Saves ebp, ebx, edi, esi, esp and eip in that order.
;
setjmp_t proc
push ebp
mov ebp, esp
push edi
mov edi, [ebp+8] ; Pointer to jmpbuf struct.
mov eax, [ebp] ; Save ebp, note we are saving the stored version on the stack.
mov [edi], eax
mov [edi+4], ebx ; Save ebx
mov eax, [ebp-4]
mov [edi+8], eax ; Save edi, note we are saving the stored verion on the stack.
mov [edi+12], esi ; Save esi
mov eax, ebp;
add eax, 8
mov [edi+16], eax ; Save sp, note saving sp pointing to last item on stack just before call to setjmp.
mov eax, [ebp+4]
mov [edi+20], eax ; Save return address (will be used as jump address in longjmp().
xor eax, eax ; return 0;
pop edi
pop ebp
ret
setjmp_t endp
; Simple version of longjmp (x86-32 bit).
;
; Restores ebp, ebx, edi, esi, esp and eip.
;
longjmp_t proc
mov edi, [esp+4] ; Pointer to jmpbuf struct.
mov eax, [esp+8] ; Get return value (value passed to longjmp).
mov ebp, [edi] ; Restore ebp.
mov ebx, [edi+4] ; Restore ebx.
mov esi, [edi+12] ; Restore esi.
mov esp, [edi+16] ; Restore stack pointer.
mov ecx, [edi+20] ; Original return address to setjmp.
mov edi, [edi+8] ; Restore edi, note, done last as we were using edi up to this point.
jmp ecx ; Wing and a prayer...
longjmp_t endp
end
A C code snippet to test it:一个 C 代码片段来测试它:
extern "C" int setjmp_t( int *p);
extern "C" int longjmp_t( int *p, int n);
jmp_buf test2_buff;
void DoTest2()
{
int x;
x = setjmp_t( test2_buff);
printf( "setjmp_t return - %d\n", x);
switch (x)
{
case 0:
printf( "About to do long jump...\n");
longjmp_t( test2_buff, 99);
break;
default:
printf( "Here becauuse of long jump...\n");
break;
}
printf( "Test2 passed!\n");
}
Note that I used the declaration from 'setjmp.h' for the buffer but if you want you can use an array of ints (minimum of 6 ints).请注意,我将“setjmp.h”中的声明用于缓冲区,但如果您愿意,可以使用整数数组(最少 6 个整数)。
You pass setjmp() a buffer parameter.您将 setjmp() 传递给缓冲区参数。 It then stores the current register info etc. into this buffer.
然后它将当前寄存器信息等存储到此缓冲区中。 The call to longjmp() then restores these values from the buffer.
对 longjmp() 的调用然后从缓冲区中恢复这些值。 Furthermore, what wallyk said.
此外,沃利克所说的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.