Why do I have to play with “rsp” to call a c++ fun

2020-02-14 07:53发布

问题:

I just started my assembly journey like recently, so obviously I'm a newbie, I've been writing fairly simple and basic programs and I just noticed something weird (to me).

a program giving the count of numbers in a table ending with 111 in binary

entry point:

#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");
}

assembly:

.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

if I comment "sub rsp, 8" and "add rsp, 8" around "call r14" out, the program will crash immediately, that doesn't make sense to me, I want to know why it happens, and also, if I replace "mov [rsp + 32], rcx" and "mov rcx, [rsp + 32]" with "push rcx" and "pop rcx", the output will be garbage, I'm also curious about that

回答1:

The Windows x86-64 calling convention requires 16B alignment of RSP before a CALL instruction. This explains the sub rsp,8 around the function call.

It also requires 32B of shadow space reserved for the use of the called function, and that's what the sub rsp, 32 + 16 is doing.

It would be smart to just combine those together, and sub rsp, 32 + 16 + 8 on function entry, and then don't mess with RSP until the epilogue. You might change the offset you're using for mov [rsp + 32], rcx, I'm not sure if that matters. I didn't try to read your whole code since the question was just about stack alignment/usage.

The called function making use of its shadow space also explains why you get garbled output if you just push/pop around the CALL, because then your data will be in the shadow space.


See the x86 tag wiki for ABI / calling convention links.