fei*_*ty2 3 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");
}
Run Code Online (Sandbox Code Playgroud)
集会:
.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
Run Code Online (Sandbox Code Playgroud)
如果我在“call r14”周围注释“sub rsp, 8”和“add rsp, 8”,程序将立即崩溃,这对我来说没有意义,我想知道它为什么会发生,而且,如果我用“push rcx”和“pop rcx”替换了“mov [rsp + 32], rcx”和“mov rcx,[rsp + 32]”,输出将是垃圾,我也很好奇
该视窗64位调用约定要求RSP的16B对准CALL指令之前(但因此保证 rsp%16 == 8在函数入口,后call推返回地址)。这解释了sub rsp,8围绕函数调用。
它还需要为被调用函数的使用保留32B 的阴影空间(又名home 空间),这就是它sub rsp, 32 + 16正在做的事情。
将它们组合在一起并sub rsp, 32 + 16 + 8在函数输入时将它们组合在一起是明智的,然后在结尾之前不要弄乱 RSP。(在执行奇数个pushes的函数中,+8负责重新对齐堆栈。)
[rsp+32]高字节不会被 a 踩到call,而低字节则不然。
被调用的函数可以自由使用其返回地址上方的那 32 个字节。这解释了为什么如果你只是在 CALL 周围推送/弹出你会得到乱码的输出,因为你的数据将在阴影空间中。
有关ABI/调用约定链接,请参阅x86标记 wiki。