我需要了解longjmp函数的工作原理;我知道它的作用,但我需要知道它是如何做到的。
我试图取消gdb中的代码,但我无法理解一些步骤。代码是:
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>
有人可以简单地向我解释一下代码,或者说明我可以在系统中找到源代码的位置吗?
答案 0 :(得分:5)
大多数情况下,它会恢复寄存器和堆栈,就像它们在相应的setjmp()
时一样。需要一些额外的清理(修复信号处理和展开待处理的堆栈处理程序),以及返回一个不同的值作为 setjmp 的表观返回值,但恢复状态是操作的本质。
要使其工作,堆栈不能低于调用setjmp的点。 Longjmp是一种野蛮的方法,只是将调用堆栈(或函数调用嵌套序列)中调用的所有内容调用到同一级别,通常只需将堆栈指针设置为调用setjmp时的相同帧即可。 / p>
为了使它干净地工作,longjmp()
调用所有的中间函数的退出处理程序,因此它们可以删除变量,以及在函数返回时通常进行的任何其他清理。将堆栈重置为深度较低的点将释放所有auto
个变量,但如果其中一个变量为FILE *
,则需要关闭该文件并释放i / o缓冲区。
答案 1 :(得分:5)
以下是标准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
答案 2 :(得分:2)
我认为您需要看到Procedure Activation Records和Call Stacks以及Setjmp.h的jmp_buf
结构。
引自Expert C编程:Deep C Secrets:
Setjmp将程序计数器的副本和当前指针保存到堆栈顶部。如果您愿意,这可以节省一些初始值。然后longjmp恢复这些值,有效地转移控制并将状态重置回您执行保存时的位置。它被称为“展开堆栈”,因为您从堆栈中取消激活记录,直到您找到保存的记录。
Have a look at page 153 also here.
堆栈框架将高度依赖于机器和可执行文件,但想法是一样的。
答案 3 :(得分:1)
在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
答案 4 :(得分:0)
传递setjmp()一个缓冲区参数。然后它将当前寄存器信息等存储到该缓冲区中。然后调用longjmp()然后从缓冲区恢复这些值。此外,wallyk说了什么。
答案 5 :(得分:0)
这里是我为一个小的clib子集(在Visual Studio 2008中编写和测试)编写的setmp和longjmp版本。汇编代码存储在单独的.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
一个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");
}
请注意,我使用了'setjmp.h'中的声明作为缓冲区,但是如果您愿意,可以使用一个整数数组(最少6个整数)。