C++ 多参数masm x64上的Printf
我需要在asm(Windows上)中创建一个复制以下行为的函数:C++ 多参数masm x64上的Printf,c++,assembly,x86-64,masm,C++,Assembly,X86 64,Masm,我需要在asm(Windows上)中创建一个复制以下行为的函数: _int64 q(_int64 a, _int64 b, _int64 c, _int64 d, _int64 e) { _int64 sum = a + b + c + d + e; printf("a = %I64d b = %I64d c = %I64d d = %I64d e = %I64d sum = %I64d\n", a, b, c, d, e, sum); return sum;
_int64 q(_int64 a, _int64 b, _int64 c, _int64 d, _int64 e) {
_int64 sum = a + b + c + d + e;
printf("a = %I64d b = %I64d c = %I64d d = %I64d e = %I64d sum = %I64d\n", a, b, c, d, e, sum);
return sum;
}
我知道我需要为printf分配阴影空间,还需要在堆栈中存储一些参数,因为只有前4个参数在寄存器中(rcx、rdx、r8和r9)
我的问题是字符串格式和堆栈管理。
到目前为止,我的代码如下所示:
.data
string1 dq 'a = %I64d b = %I64d c = %I64d d = %I64d e = %I64d sum = %I64d', 10, 0 ; The printf format, "\n",'0'
.code
public q ;a in rcx, b in rdx, c in r8, d in r9, e in stack
q: push rbp
mov rbp, rsp
sub rsp, 32 ;allocating shadow space for printf
;for calling printf, we need to have [string] in rcx,
;a in rdx, b in r8, c in r9, d in stack1, e in stack2, and sum in stack3
add rax, rcx ;first make the sum
add rax, rdx
add rax, r8
add rax, r9
mov rbx, [rbp + 8] ;getting e from the stack
add rax, rbx ;final add, in rax now is sum
push rax ;changing parameters in registers (last 3 in stack)
push rbx
push r9
mov r9, r8 ;c in r9
mov r8, rdx ;b in r8
mov rdx, rcx ;a in rdx
lea rcx, [string] ;string in rcx
call printf
mov rsp, rbp ;back to previous pointer
pop rbp ;release resources
ret 0
end
此时它没有编译,带有
错误A2084:常量值太大
我不知道是否需要更改格式或将其拆分为2,在这种情况下,我需要在堆栈中存储一些其他参数,然后我不确定如何继续…如果该错误与字符串在同一行,请使用
db
而不是dq
:您不需要10,0
元素填充为qwords,即使它确实像NASM那样接受引用的部分作为字符串
我认为MASM允许为db
引用常量,因此应该可以将其组装起来
然后我们在运行时会遇到多个其他错误:
此外,如果要在按下3个寄存器后恢复它们,则需要
sub rsp,32
在按下3个寄存器后保留阴影空间。否则,这24个字节是被调用函数(printf
)看到的阴影空间的底部
但这是毫无意义的,因为你没有在通话后恢复它们。因此,您通过重击
rbx
,违反了调用约定;调用后,您似乎没有对它们做任何处理,因此只需使用call clobbered reg,或者最好首先计算正确的arg传递寄存器中的值。如果该错误与字符串在同一行,请使用db
而不是dq
:您不需要10,0
元素填充为qwords,即使它确实像NASM那样接受引用的部分作为字符串
我认为MASM允许为db
引用常量,因此应该可以将其组装起来
然后我们在运行时会遇到多个其他错误:
此外,如果要在按下3个寄存器后恢复它们,则需要
sub rsp,32
在按下3个寄存器后保留阴影空间。否则,这24个字节是被调用函数(printf
)看到的阴影空间的底部
但这是毫无意义的,因为你没有在通话后恢复它们。因此,您通过重击
rbx
,违反了调用约定;在调用之后,您似乎没有对它们做任何处理,因此只需使用call clobbered reg,或者最好首先计算正确的arg passing寄存器中的值。哪一行是错误?在绳子上?使用db
,您不希望10,0
元素填充到qwords中,即使它确实像NASM那样接受qwords。此外,如果您想在之后恢复它们,您需要sub rsp,32
以在按下3个寄存器后保留阴影空间。否则,这24个字节是被调用函数(printf
)看到的阴影空间的底部。但这是毫无意义的,因为你没有在通话后恢复它们。因此,您通过重击rbx
,违反了调用约定;使用不同的注册表。另外,您还需要调用_printf
,因为Windows就是这样装饰C名称的。@PeterCordes在字符串声明中是。。。因此,我有以下错误A2006:未定义符号:string错误在哪一行?在绳子上?使用db
,您不希望10,0
元素填充到qwords中,即使它确实像NASM那样接受qwords。此外,如果您想在之后恢复它们,您需要sub rsp,32
以在按下3个寄存器后保留阴影空间。否则,这24个字节是被调用函数(printf
)看到的阴影空间的底部。但这是毫无意义的,因为你没有在通话后恢复它们。因此,您通过重击rbx
,违反了调用约定;使用不同的注册表。另外,您还需要调用_printf
,因为Windows就是这样装饰C名称的。@PeterCordes在字符串声明中是。。。因此,我有以下错误A2006:未定义符号:StringThank,现在它可以工作,但e和sum不能正常工作。我真的不理解最后一段,但是:S。我是否仍然需要为printf分配阴影空间才能使用它???@MartaLobo:是的,您调用的任何函数都可以在返回地址上方超过32字节。您需要确保这些字节中没有任何有价值的内容。现在它已经组装好了,使用一个调试器来找出在执行单个步骤时代码在做什么。但是我注意到你从添加rax,rcx开始,而不是从mov开始,rax最初保存你的调用者留下的垃圾。是的,我注意到了,我添加了几个异或来重置寄存器。现在我唯一剩下的问题是如何从堆栈中获取“e”并适当地添加它…@MartaLobo:你有没有试过只查看C函数的优化编译器输出?打开函数并用x86-64 MSVC-Ox
编译它。不要忘记在堆栈args之前,您自己的调用方在返回地址上方留下的阴影空间。非常感谢,该网站非常有用!谢谢,现在它可以工作了,但是e和sum不能正常工作。我真的不理解最后一段,但是:S。我是否仍然需要为printf分配阴影空间才能使用它???@MartaLobo:是的,您调用的任何函数都可以在返回地址上方超过32字节。您需要确保这些字节中没有任何有价值的内容。现在它已经组装好了,使用一个调试器来找出在执行单个步骤时代码在做什么。但是我注意到你从添加rax,rcx开始,而不是从mov开始,rax最初保存你的调用者留下的垃圾。是的,我注意到了,我添加了几个异或来重置寄存器。现在我唯一的问题是