Как работает longjmp? - PullRequest
       0

Как работает longjmp?

8 голосов
/ 27 мая 2011

Мне нужно понять, КАК работает функция 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>

Может кто-нибудь кратко объяснить мне код или указать, где я могу найти исходный код в системе?

Ответы [ 5 ]

5 голосов
/ 27 мая 2011

Вот код i386 для longjmp, в стандартном ABI 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
5 голосов
/ 27 мая 2011

В основном, это восстанавливает регистры и стек, как они были во время соответствующего setjmp().Требуется некоторая дополнительная очистка (исправление обработки сигналов и разматывание ожидающих обработчиков стека), а также возврат другого значения в качестве кажущегося возвращаемого значения setjmp , но восстановление состояния является сущностью операции.

Чтобы это работало, стек не может быть ниже точки, в которой был вызван setjmp.Longjmp - это грубый способ просто забыть все, что было вызвано ниже, до того же уровня в стеке вызовов (или последовательности вложений вызовов функций), в основном, просто установив указатель стека на тот же кадр, который был при вызове setjmp.1006 *

Чтобы все работало без ошибок, longjmp() вызывает все обработчики выхода для промежуточных функций, чтобы они могли удалять переменные и все остальные операции очистки, которые обычно выполняются при возврате функции.Сброс стека к точке, менее глубокой, освобождает все переменные auto, но если одна из них является FILE *, файл должен быть закрыт и буфер ввода-вывода также освобожден.

3 голосов
/ 27 мая 2011

Я думаю, вам нужно увидеть Записи активации процедуры и Стеки вызовов и Setjmp.h jmp_buf.

По материалам Expert C Программирование: Deep C Секреты:

Setjmp сохраняет копию счетчика программы и текущий указатель на вершину стека. Это сохраняет некоторые начальные значения, если хотите. Затем longjmp восстанавливает эти значения, эффективно передавая управление и сбрасывая состояние обратно туда, где вы были, когда вы делали сохранение. Это называется «разматывание стека», поскольку вы разворачиваете записи активации из стека до тех пор, пока не доберетесь до сохраненного.

Смотрите также страницу 153.

Кадр стека будет сильно зависеть от машины и исполняемого файла, но идея та же.

0 голосов
/ 28 июня 2019

Вот версии setmp и longjmp, которые я написал для небольшого подмножества clib (написано и протестировано в 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 целых).

0 голосов
/ 27 мая 2011

Вы передаете setjmp () параметр буфера. Затем он сохраняет текущую информацию о регистре и т. Д. В этот буфер. Вызов longjmp () затем восстанавливает эти значения из буфера. Кроме того, что сказал Уоллик.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...