2016-10-31 14 views
2

Я только что начал свое путешествие на поездку, как недавно, поэтому, очевидно, я новичок, я пишу довольно простые и простые программы, и я просто заметил что-то странное (для меня).Почему мне нужно играть с «rsp» для вызова функции C++?

программа дает подсчет чисел в таблице, оканчивающийся 111 в двоичном

точки входа:

#include <iostream> 
#include <cstdlib> 

extern "C" auto _start(void *, void *)->void; 

auto print_msg(char *msg) { 
    std::cout << msg; 
} 

auto print_int(uint64_t val) { 
    std::cout << val; 
} 

auto main()->int { 
    _start(print_int, print_msg); 
    std::cout << std::endl; 
    system("pause"); 
} 

сборки:

.const 
_tab dw 65535, 61951, 61949, 61925, 61927, 61734, 61735, 61728 
_LENGTH = ($ - _tab)/2 
_msg_1 db 'There are ', 0 
_msg_2 db ' numbers ending with 111 in binary!', 0 

.code 
_start proc 
     push  r15 
     push  r14 
     sub  rsp, 32 + 16 
     mov  r14, rcx 
     mov  r15, rdx 
     xor  rcx, rcx 
     xor  r9, r9 
     lea  r8, _tab 
_LOOP: movzx rax, word ptr [r8] 
     and  rax, 111b 
     cmp  rax, 111b 
     jz  _INC 
     jmp  _END_IF 
_INC: inc  rcx 
_END_IF: inc  r9 
     add  r8, 2 
     cmp  r9, _LENGTH 
     jne  _LOOP 
     mov  [rsp + 32], rcx 
     lea  rcx, _msg_1 
     call  r15 
     mov  rcx, [rsp + 32] 

     sub  rsp, 8 
     call  r14 
     add  rsp, 8 

     lea  rcx, _msg_2 
     call  r15 
     add  rsp, 32 + 16 
     pop  r14 
     pop  r15 
     ret 
_start endp 

end 

если я комментирую " sub rsp, 8 "и" add rsp, 8 "вокруг" call r14 ", программа немедленно сработает, что делает не имеет смысла для меня, я хочу знать, почему это происходит, а также, если я заменил «mov [rsp + 32], rcx» и «mov rcx, [rsp + 32]» с «push rcx» и «push rcx», pop rcx ", выход будет мусором, мне также интересно, что

+1

Не вызывайте свою функцию '_start'; это имя по умолчанию для точки входа (по крайней мере, в Linux). Программы, которые определяют собственный '_start', не связывают стандартный код запуска CRT, а' _start' не является даже функцией, которая может возвратиться; это просто точка входа. TL: DR: '_start' - это запутанное имя функции для многих читателей. –

ответ

4

В соглашении на использование Windows x86-64 требуется 16B выравнивание RSP перед инструкцией CALL. Это объясняет sub rsp,8 вокруг вызова функции.

Для этого требуется также 32B теневого пространства для использования вызываемой функции, и это то, что делает sub rsp, 32 + 16.

Было бы разумно просто объединить их вместе, и sub rsp, 32 + 16 + 8 о вводе функции, а затем не вступать в RSP до эпилога. Вы можете изменить смещение, которое вы используете для mov [rsp + 32], rcx, я не уверен, что это важно. Я не пробовал читать весь ваш код, так как вопрос касался только выравнивания/использования стека.

Вызванная функция, использующая его пространство теней, также объясняет, почему вы получаете искаженный вывод, если вы просто нажимаете/выскакиваете по CALL, потому что тогда ваши данные будут находиться в тени.


Смотрите теги вики для ABI/соглашение о вызове ссылки.