windows - 为什么我必须使用 "rsp"来调用 C++ 函数?

标签 windows assembly x86-64 masm calling-convention

我最近才开始我的汇编之旅,所以显然我是一个新手,我一直在编写相当简单和基本的程序,我只是注意到一些奇怪的东西(对我来说)。

一个程序给出以二进制形式以 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

如果我在“call r14”周围注释“sub rsp, 8”和“add rsp, 8”,程序会立即崩溃,这对我来说没有意义,我想知道为什么会这样,并且另外,如果我用“push rcx”和“pop rcx”替换“mov [rsp + 32],rcx”和“mov rcx,[rsp + 32]”,输出将是垃圾,我也很好奇

最佳答案

Windows x64 calling convention在 CALL 指令之前需要 RSP 的 16B 对齐(但因此 保证 rsp%16 == 8 在函数入口,在 call 推送返回地址之后).这解释了围绕函数调用的 sub rsp,8

它还需要 32B 的影子空间(又名 home space )为被调用函数的使用保留,这就是 sub rsp, 32 + 16 正在做的事情。


将它们组合在一起会很聪明,sub rsp, 32 + 16 + 8 在函数入口处,然后在尾声之前不要弄乱 RSP。 (在执行奇数次 pushes 的函数中,负责 +8 以重新对齐堆栈。)

[rsp+32] 和高位字节不会被 call 踩踏,低位字节则不然。

被调用函数可以自由使用其返回地址上方的 32 个字节。这解释了为什么如果你只是在 CALL 周围压入/弹出,你会得到乱码输出,因为那样你的数据将在阴影空间中。


参见 为 ABI/调用约定链接标记 wiki。

关于windows - 为什么我必须使用 "rsp"来调用 C++ 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40344385/

相关文章:

assembly - 需要帮助验证汇编代码

c++ - 如何在编译时加密字符串?

assembly - 我可以将 al 插入 TASM 中的堆栈吗?

c++ - 如何启动我的简单 hello world 程序?

assembly - 就64位除法而言,是否可以在不分支的情况下执行128位/64位除法?

c++ - 在 x86-64 平台上用 C(++) 计算 64 位无符号参数的 (a*b)%n FAST?

Windows 下的 Phpize

java - 表示 Windows 下以 `Path` 开头但没有磁盘驱动器的 `\`

windows - 整数原子的使用

debugging - 为什么 int 3 生成 64 位的 SIGSEGV 而不是停止调试器?