问题描述
我最近才刚刚开始我的汇编之旅,所以显然我是一个新手,我一直在编写相当简单和基本的程序,我只是发现一些奇怪的东西(对我而言).
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).
给出二进制表中以111结尾的表中的数字计数的程序
a program giving the count of numbers in a table ending with 111 in binary
进入点:
#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]",输出将是垃圾,我也对此感到好奇
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
推荐答案
Windows x86-64调用约定要求CALL指令之前RSP的16B对齐.这说明了函数调用周围的sub rsp,8
.
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.
它还需要保留32B的阴影空间以供调用的函数使用,而这正是sub rsp, 32 + 16
所做的.
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.
将它们组合在一起,然后在函数入口上输入sub rsp, 32 + 16 + 8
,然后在结语之前不要与RSP混为一谈是很明智的.您可能会更改用于mov [rsp + 32], rcx
的偏移量,但不确定是否很重要.我没有尝试阅读您的全部代码,因为问题仅在于堆栈对齐/使用.
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.
利用其影子空间进行调用的函数还解释了为什么仅在CALL上推入/弹出会导致输出乱码,因为那样您的数据将位于影子空间中.
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.
请参见 x86 标签Wiki的ABI/调用约定链接.
See the x86 tag wiki for ABI / calling convention links.
这篇关于为什么我必须和"rsp"一起玩?调用C ++函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!