【问题标题】:Why "mov rcx, rax" is required when calling printf in x64 assembler?为什么在 x64 汇编器中调用 printf 时需要“mov rcx, rax”?
【发布时间】:2017-08-29 06:47:03
【问题描述】:

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

EXTERN printf: PROC

PUBLIC hello_world_asm

.data
hello_msg db "Hello world", 0


.code
hello_world_asm PROC
push rbp ; save frame pointer
mov rbp, rsp ; fix stack pointer
sub rsp, 8 * (4 + 2) ; shadow space (32bytes)

lea rax, offset hello_msg
mov rcx, rax ; <---- QUESTION ABOUT THIS LINE
call printf

; epilog. restore stack pointer
mov rsp, rbp
pop rbp
ret
hello_world_asm ENDP


END

一开始我调用 printf 时没有“mov rcx, rax”,结果导致访问冲突。我刚刚用 C++ 编写了一个对 printf 的调用并查看了反汇编程序,这让我感到非常沮丧。在那里,我看到了“mov rcx, rax”这一行,它修复了所有问题,但是为什么我需要将 RAX 移动到 RCX 吗?显然我缺少一些基本的东西。

感谢您的帮助!

附言非常欢迎参考优秀的 x64 汇编器教程 :-) 找不到。

【问题讨论】:

  • rax 是不稳定的。 printf 使用 rcx 加载字符串,所以在我的理解中,这只是你错误的调用约定。很好的解释here
  • 因为 x64 printf 的第一个参数进入 rcx,而不是 rax。
  • 查看x86 tag wiki 获取文档和教程的链接。由于您使用的是 Windows,software.intel.com/en-us/articles/introduction-to-x64-assembly 可能是一个不错的选择。
  • 与问题无关,但在我看来,您可以使用 leaoffset hello_msg 加载到 RCX 中,而不是使用 RAX 作为一个中间步骤。

标签: windows assembly x86-64 calling-convention


【解决方案1】:

这不是必需的,这段代码只是浪费了一条指令,通过在 RAX 中执行 lea 然后复制到 RCX,如果可以这样做的话

lea   rcx, hello_msg
call  printf              ; printf(rcx, rdx, r8, r9, stack...)

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

另请注意,对于像 printf (MS's docs) 这样的可变参数函数,xmm0..3 中的 FP args 必须镜像到相应的整数寄存器,但对于整数 args,它不需要 movq xmm0, rcx


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

一些 32 位调用约定在 EAX 中传递一个 arg,例如 Irvine32 或 gcc -m32 -mregparm=1。但是没有标准的 x86-64 调用约定。你可以用你编写的私有 asm 函数做任何你喜欢的事情,但是在调用库函数时你必须遵循标准的调用约定。


还要注意lea rax, offset hello_msg 很奇怪; LEA 使用内存操作数语法和机器编码(并为您提供地址而不是数据)。 offset hello_msg 是立即数,而不是内存操作数。但是在这种情况下,MASM 无论如何都接受它作为内存操作数。

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

【讨论】:

  • lea rax, offset hello_msg 会组装在我曾经使用过的每个版本的 MASM 和 TASM 上,这可以追溯到 80 年代(当然是 16 位地址)。 MASM 对括号的使用是可选的,因此它与lea rax, [offset hello_msg] 相同,并且偏移量被有效丢弃,因此等效于lea rax, [hello_msg]
【解决方案2】:

Windows 64-bit (x64/AMD64) calling convention 在 RCX、RDX、R8 和 R9 中通过 the first four integer arguments

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

【讨论】:

    猜你喜欢
    • 2017-10-14
    • 2017-05-05
    • 2022-12-12
    • 2016-01-15
    • 2015-06-07
    • 2011-01-29
    • 1970-01-01
    • 2016-11-15
    • 2019-02-19
    相关资源
    最近更新 更多