将 scanf 与 x86-64 GAS 程序集一起使用
Posted
技术标签:
【中文标题】将 scanf 与 x86-64 GAS 程序集一起使用【英文标题】:Using scanf with x86-64 GAS assembly 【发布时间】:2014-11-23 22:41:51 【问题描述】:我在尝试调用系统函数 scanf 以在我的 x86 汇编程序中工作时遇到了很多问题。目前我已经从标准中读取它,但是它只会读取没有段错误的字符(我不知道为什么,指定的字符串是 %d)。我在 x86 在线看到的 scanf 示例使用 quarky 或使用 NASM 语法编写,因此我尝试将它们改编为我的程序。
f:
.string "%d"
_main:
movq $0, %rax #Clean rax
movq $f, %rdi #Load string format
movq %rcx, %rsi #Set storage to rcx (Not sure if this is valid)
call scanf
ret
在输入字符或字符串后,使用 printf 检查 rcx 和 rax 分别返回 1 和 0(只有这样程序不会出现段错误)。
非常感谢任何有关如何在 x86 气体组装中正确使用 scanf 的见解!
【问题讨论】:
scanf
是一个标准库函数,而不是“系统函数”。对于第二个参数,它需要一个存储结果的内存地址。您可以将寄存器值移动到 RSI 并期望它将数据存储在前一个寄存器中 - 这没有任何意义。使用 LEA。
【参考方案1】:
正如您所担心的,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-relative LEA 是 x86-64 上的标准方式。 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 相同的代码大小。
您还可以使用助记符movabsq
加载完整的 64 位绝对地址。但不要这样做,因为 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
【讨论】:
非常感谢!为了将来参考,我将如何用本地内存空间替换全局 x? @user3210373:lea 4(%rsp), %rsi
例如。或者mov %rsp, %rsi
,如果您的本地恰好在(%rsp)
。 My (AT&T) assembly (x86-x64) code should increment but doesn't 显示了一个示例。
呃,mov $f, %edi
用于低地址(Linux 非 PIE 可执行文件),否则 lea f(%rip), %rdi
。 movq
或 movabs
不是好的建议。以上是关于将 scanf 与 x86-64 GAS 程序集一起使用的主要内容,如果未能解决你的问题,请参考以下文章
x86-64 GAS Intel 语法中的 RIP 相对变量引用(如“[RIP + _a]”)如何工作?
AT&T 语法中的 3 或 4 参数 x86 程序集[重复]
x86-64 在线汇编,带有诸如 https://www.mycompiler.io/new/asm-x86_64 之类的 IDE