正如你所担心的那样,movq %rcx, %rsi
是不正确的。您需要传递一个指向内存的指针。寄存器不是内存地址空间的一部分,因此不能有指向它们的指针。您需要全局或本地分配存储。顺便说一句,您不应该将数据(尤其是可写数据)放入默认值.text
部分,因为它用于代码并且通常是只读的。另外,调用约定通常要求 16 字节堆栈指针对齐,因此您也应该注意这一点。
.globl main
main:
push %rbp # keep stack aligned
mov $0, %eax # clear AL (zero FP args in XMM registers)
leaq f(%rip), %rdi # load format string
leaq x(%rip), %rsi # set storage to address of x
call scanf
pop %rbp
ret
.data
f: .string "%d" # could be in .rodata instead
x: .long 0
(如果您的环境需要符号上有前导下划线,则使用_main
,并且可能_scanf
.)
实际上有 3 种选择将符号/标签的地址放入寄存器。与 RIP 相关的 LEA 是 x86-64 上的标准方式。如何在 GNU 汇编器中将函数或标签的地址加载到寄存器中 https://stackoverflow.com/questions/57212012/how-to-load-address-of-function-or-label-into-register-in-gnu-assembler
作为一种优化,如果您的变量位于地址空间的较低 4GiB 中,例如在 Linux 非 PIE 中(位置-依赖的) 可执行文件,您可以使用 32 位绝对立即数:
mov $f, %edi # load format string
mov $x, %esi # set storage to address of x
movq $f, %rdi
将使用 32 位符号扩展立即数(而不是通过写入 EDI 隐式零扩展到 RDI),但具有与 RIP 相关的 LEA 相同的代码大小。
您还可以使用助记符加载完整的 64 位绝对地址movabsq
。但不要这样做,因为 10 字节指令对代码大小不利,并且仍然需要运行时修复,因为它与位置无关。
movabsq $f, %rdi # load format string
movabsq $x, %rsi # set storage to address of x
根据要求:使用局部变量作为输出可能如下所示:
subq $8, %rsp # allocate 8 bytes from stack
xor %eax, %eax # clear AL (and RAX)
leaq f(%rip), %rdi # load format string
movq %rsp, %rsi # set storage to local variable
call scanf
addq $8, %rsp # restore stack
ret