为什么在 x64 汇编程序中调用 printf 时需要 "mov rcx, rax"?
Why "mov rcx, rax" is required when calling printf in x64 assembler?
我正在尝试学习 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
一开始我在没有 "mov rcx, rax" 的情况下调用了 printf,结果以访问冲突告终。感到非常沮丧,我只是用 C++ 编写了对 printf 的调用并查看了反汇编程序。在那里我看到了 "mov rcx, rax" 这行修复了所有问题,但是 为什么 我需要将 RAX 移动到 RCX???显然我遗漏了一些基本的东西。
感谢您的帮助!
p.s。非常欢迎参考优秀的 x64 汇编器教程 :-) 找不到。
RCX、RDX、R8 和 R9 中的 Windows 64-bit (x64/AMD64) calling convention passes the first four integer arguments。
return 值存储在 RAX 中并且它是易变的,因此允许 C/C++ 编译器将其用作函数中的通用存储。
这不是必需的,这段代码只是浪费了一条指令,在 RAX 中执行 lea
然后复制到 RCX,而它可以执行 [=24] =]
lea rcx, hello_msg
call printf ; printf(rcx, rdx, r8, r9, stack...)
printf
在 64 位 Windows 上忽略 RAX 作为输入; RAX 是 the Windows x64 calling convention 中的 return 值寄存器(也可以被 void
函数破坏)。前 4 个 args 进入 RCX、RDX、R8 和 R9(如果它们像这里一样 integer/pointer)。
另请注意,xmm0..3 中的 FP args 必须镜像到可变参数函数的相应整数寄存器,如 printf
(MS's docs),但对于整数 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 语法。
我正在尝试学习 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
一开始我在没有 "mov rcx, rax" 的情况下调用了 printf,结果以访问冲突告终。感到非常沮丧,我只是用 C++ 编写了对 printf 的调用并查看了反汇编程序。在那里我看到了 "mov rcx, rax" 这行修复了所有问题,但是 为什么 我需要将 RAX 移动到 RCX???显然我遗漏了一些基本的东西。
感谢您的帮助!
p.s。非常欢迎参考优秀的 x64 汇编器教程 :-) 找不到。
RCX、RDX、R8 和 R9 中的 Windows 64-bit (x64/AMD64) calling convention passes the first four integer arguments。
return 值存储在 RAX 中并且它是易变的,因此允许 C/C++ 编译器将其用作函数中的通用存储。
这不是必需的,这段代码只是浪费了一条指令,在 RAX 中执行 lea
然后复制到 RCX,而它可以执行 [=24] =]
lea rcx, hello_msg
call printf ; printf(rcx, rdx, r8, r9, stack...)
printf
在 64 位 Windows 上忽略 RAX 作为输入; RAX 是 the Windows x64 calling convention 中的 return 值寄存器(也可以被 void
函数破坏)。前 4 个 args 进入 RCX、RDX、R8 和 R9(如果它们像这里一样 integer/pointer)。
另请注意,xmm0..3 中的 FP args 必须镜像到可变参数函数的相应整数寄存器,如 printf
(MS's docs),但对于整数 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 语法。