Ошибка переполнения стека в сборке - PullRequest
0 голосов
/ 10 мая 2018

Я пытаюсь написать код, который принимает ввод от пользователя и отображает факториал в сборке x64. Каждый раз, когда я запускаю код, он дает мне необработанное исключение в 0x00007FF64B910B11 в finalProjectAssembly.exe: 0xC00000FD: переполнение стека (параметры: 0x0000000000000001, 0x00000019F1603FF8)., Но я не понимаю, как переполнился стек. Чего мне не хватает?

INCLUDELIB libcmt.lib
INCLUDELIB legacy_stdio_definitions.lib

EXTERN printf:PROC
EXTERN scanf:PROC

.DATA
prompt  BYTE    "Enter a number: ", 0
inFmt   BYTE     "%d", 0
prompt3 BYTE    "The factorial is: %d",10,0
cont    QWORD   ?
disp    BYTE    "You Entered %d", 10,0
num     QWORD   ?
num2    REAL8   1.5

.CODE
main PROC C
sub     rsp, 24
lea     rcx, prompt          
call    printf
lea     rdx, num             
lea     rcx, inFmt           
call    scanf
push    num

mov     rdx,num              
lea     rcx, disp            
call    printf
call    factorial
mov     rdx, rax
lea     rcx, prompt3
call    printf

lea     rcx, prompt2
call    printf
lea     rdx, cont
lea     rcx, inFmt
call    scanf

add     rsp,24             
mov     rax,0
ret
main ENDP

factorial PROC
push    rbp
mov     rbp,rsp
mov     rax, [rbp + 16]
cmp     rax, 1
jle     quit
dec     rax 
push    rax
call    factorial
mov     rbx, [rbp+16]
imul    rbx

quit:
mov     rsp, rbp
pop     rbp
ret
factorial ENDP

1 Ответ

0 голосов
/ 10 мая 2018

Если вы вызываете внешние функции (printf, scanf), вы должны соблюдать их соглашения о вызовах. Соответствующее Соглашение о программном обеспечении Microsoft x64 определяет, что вам нужно зарезервировать на одном месте четыре стека (= 32 байта) в стеке перед вызовом функции. Функция может свободно использовать это «пространство тени». Кроме того, ESP должны быть выровнены по адресу, кратному 16.

Изменить

sub     rsp, 24

до

sub     rsp, 32
and     spl, -16        ; Align to 16

Вам не нужен add rsp,24 в конце функции main. Правильный пролог (push rbp; mov rsp, rbp) и правильный эпилог (leave) будут вставлены автоматически ассемблером ML64.

Теневое пространство должно быть прямо перед адресом возврата функции, выдвигаемым инструкцией call. Переместите push num близко к call factorial и не забудьте очистить стек:

push    num
call    factorial
add     rsp, 8

Не забудьте очистить стек:

push    rax
call    factorial
add     rsp, 8

Я не проверял спецификатор формата %d, как рекомендовано Дэвидом Вольфердом. Программа в основном работает с изменениями выше.

...