7

I need to understand HOW longjmp function works; 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. 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?

4

6 回答 6

6

这是标准 i386 ABI 中的 i386 代码longjmp,没有任何与 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
于 2011-05-26T21:56:25.577 回答
4

大多数情况下,它将寄存器和堆栈恢复为相应setjmp(). 需要进行一些额外的清理(修复信号处理和展开挂起的堆栈处理程序),以及返回不同的值作为setjmp的明显返回值,但恢复状态是操作的本质。

要使其工作,堆栈不能低于调用 setjmp 的点。Longjmp 是一种粗暴的方法,它只是将在调用堆栈(或函数调用嵌套序列)中被调用的所有内容都忘记到同一级别,主要是通过简单地将堆栈指针设置为与调用 setjmp 时相同的帧。

为了让它干净地工作,longjmp()调用中间函数的所有退出处理程序,以便它们可以删除变量,以及在函数返回时通常进行的任何其他清理。将堆栈重置为不那么深的点会释放所有auto变量,但如果其中一个是 a FILE *,则需要关闭文件并释放 i/o 缓冲区。

于 2011-05-26T21:17:53.347 回答
3

我认为您需要查看过程激活记录调用堆栈以及Setjmp.hjmp_buf结构。

引自 Expert C Programming: Deep C Secrets:

Setjmp 将程序计数器的副本和当前指针保存到堆栈顶部。如果您愿意,这会保存一些初始值。然后 longjmp 恢复这些值,有效地转移控制并将状态重置回您进行保存时的状态。它被称为“展开堆栈”,因为您从堆栈中展开激活记录,直到您到达已保存的记录。

也可以在这里查看第 153 页。

stackframe 将高度依赖于机器和可执行文件,但想法是一样的。

于 2011-05-27T08:17:29.047 回答
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
于 2020-03-07T11:08:59.257 回答
0

您将 setjmp() 传递给缓冲区参数。然后它将当前寄存器信息等存储到此缓冲区中。对 longjmp() 的调用然后从缓冲区中恢复这些值。此外,沃利克所说的。

于 2011-05-26T21:30:57.880 回答
0

这是我为一个小的 clib 子集编写的 setmp 和 longjmp 版本(在 Visual Studio 2008 中编写和测试)。汇编代码存储在单独的 .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

AC 代码片段来测试它:

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 个整数)。

于 2019-06-28T11:57:29.553 回答