Сборка х86 с какой целью "введите 0,0" - PullRequest
2 голосов
/ 26 июня 2019

На данный момент я изучаю ассемблер для x86 с NASM.Я попытался написать следующую функцию, которая вычисляет числа Фибоначчи:

unsigned int fibonacci(unsigned int n) {
    if (n < 2)
        return 1;
    else
        return fibonacci(n - 1) + fibonacci(n - 2);
}

Это то, что я сделал до сих пор, и это прекрасно работает, точно так же, как и вышеописанный метод.Но, как я понимаю, инструкция enter 0,0 предназначена для освобождения места в стеке для локальных переменных.Я помещаю две локальные переменные в стек: push dword ptr[n].Но разве это не возможно, только если я использую инструкцию enter, например, такую: enter 8,0.Чтобы освободить место для двух переменных типа int.Если я пытаюсь это сделать, я получаю исключение StackOverflow.

__declspec(naked) unsigned int fibonacci2(unsigned int n) {
    __asm {
        enter 0,0
        cmp dword ptr [n], 2
        jae elsee
        mov eax, 1
        jmp end
        elsee:
        push dword ptr[n] // Here I am pushing two local variables to the stack.
        push dword ptr[n] // 2 * 4 Bytes
        dec [ebp-4]
        dec [ebp-8]
        dec [ebp-8]
        push[ebp-4]
        call fibonacci2     
        mov [ebp-4], eax
        push [ebp-8]
        call fibonacci2
        add eax, [ebp-4]
        end:
        leave
        ret
    }
 }

Это скомпилированная версия функции:

00C426E0  enter       0,0  
00C426E4  cmp         dword ptr [n],2  
00C426E8  jae         fibonacci2+11h (0C426F1h)  
00C426EA  mov         eax,1  
00C426EF  jmp         end (0C42716h)  
elsee:
00C426F1  push        dword ptr [n]  
00C426F4  push        dword ptr [n]  
00C426F7  dec         byte ptr [ebp-4]  
00C426FA  dec         byte ptr [ebp-8]  
00C426FD  dec         byte ptr [ebp-8]  
00C42700  push        dword ptr [ebp-4]  
00C42703  call        _fibonacci2 (0C413BBh)  
00C42708  mov         dword ptr [ebp-4],eax  
00C4270B  push        dword ptr [ebp-8]  
00C4270E  call        _fibonacci2 (0C413BBh)  
00C42713  add         eax,dword ptr [ebp-4]  
end:
00C42716  leave  
00C42717  ret

1 Ответ

2 голосов
/ 27 июня 2019

Чтобы освободить место для двух переменных типа int.

Компилятору не нужно использовать стек для локальных переменных. В некоторых случаях он может даже полностью оптимизировать использование стека и хранить локальные переменные в регистрах.

Я помещаю в стек две локальные переменные: push dword ptr [n]. Но не должно ли это быть возможно, только если я использую инструкцию ввода, как это: введите 8,0.

Инструкция enter делает полностью противоположной тому, что вы думаете, она делает:

Он не выделяет (стек) памяти, которая может использоваться push инструкциями, но он использует (стек) памяти так же, как инструкция push:

enter 80,0 работает как сначала enter 0,0, а затем выполняет push 10 раз с 10 случайными значениями. Это полезно для «создания» 10 неинициализированных локальных переменных в стеке.

Как уже написано enter 0,0, будет только push и инициализируется регистр ebp. Используя настоящий компилятор C с включенной оптимизацией, вы, вероятно, не получите инструкцию enter в этом случае.

Если я пытаюсь это сделать, я получаю исключение StackOverflow.

Сложно сказать, почему:

Если вы вызываете свою функцию для большого числа, вашей функции потребуется много стека. Используя enter 8,0 вместо enter 0,0, вам потребуется 8*n байт стека для аргумента функции n.

Если ваш стек уже почти "заполнен" при использовании enter 0,0, он обязательно будет заполнен при использовании enter 8,0.

Во-вторых, ваша разборка не завершена:

  • Очевидно, компилятор C добавил некоторую оболочку с именем _fibonacci2 (с подчеркиванием), расположенную по адресу 0C413BBh.
  • Разборка не показывает n как ebp+8, но как n.

Возможно, ошибка не видна в опубликованном вами коде:

  • Может находиться в упаковке по адресу 0C413BBh
  • или n не был правильно заменен (ebp+8)
...