为什么在x64汇编程序中调用printf时需要“mov rcx,rax”?

时间:2017-08-29 06:47:03

标签: windows assembly x86-64 calling-convention

我正在尝试学习x64汇编程序。我写了“hello world”并尝试使用以下代码调用printf:

CatWebServiceApplication

一开始我在没有“mov rcx,rax”的情况下调用printf,最终导致访问冲突。让所有人感到沮丧我只是用C ++编写了一个对printf的调用并查看了反汇编程序。在那里,我看到“mov rcx,rax”这一行修复了所有内容,但为什么我需要将RAX移动到RCX ???显然,我遗漏了一些基本的东西。

感谢您的帮助!

P.S。对于优秀的x64汇编程序教程的参考非常受欢迎:-)找不到一个。

2 个答案:

答案 0 :(得分:2)

这不是必需的,这段代码只是浪费了一条指令,只需要lea进入RAX,然后复制到RCX就可以了

lea   rcx, hello_msg
call  printf              ; printf(rcx, rdx, r8, r9, stack...)
64位Windows上的

printf忽略RAX作为输入; RAX是the Windows x64 calling convention中的返回值寄存器(也可能被void函数破坏)。前4个参数进入RCX,RDX,R8和R9(如果它们是像这里的整数/指针那样)。

另请注意,xmm0..3中的FP args必须镜像到printfMS's docs)等可变函数的相应整数寄存器,但对于整数args,{{1}不需要}}

在x86-64 System V调用约定中,可变参数函数需要movq xmm0, rcx =寄存器中传递的FP args的数量。 (所以你要al将它归零)。但是x64 Windows约定并不需要这样;它经过优化,可以使变量函数易于实现(而不是用于普通函数的更高性能/更多寄存器参数)。

一些32位调用约定在EAX中传递一个arg,例如Irvine32或xor eax,eax。但是没有标准的x86-64调用约定。您可以使用您编写的私有asm函数执行任何操作,但在调用库函数时必须遵循标准调用约定。

另请注意,gcc -m32 -mregparm=1很奇怪; LEA使用内存操作数语法和机器编码(并为您提供地址而不是数据)。 lea rax, offset hello_msg是立即操作数,而不是内存操作数。但是MASM在这种情况下接受它作为内存操作数。

您可以在位置相关的代码中使用offset hello_msg,否则您需要与RIP相关的LEA。我不确定MASM的语法。

答案 1 :(得分:1)

Windows 64-bit (x64/AMD64) calling convention在RCX,RDX,R8和R9中传递the first four integer arguments

返回值存储在RAX中并且是易失性的,因此允许C / C ++编译器将其用作函数中的通用存储。