2014-11-23 113 views
1

我一直有负载的问题,试图调用系统函数scanf在我的x86汇编程序中工作。目前我已经从标准中读取它,但它只会读取没有段错误的字符(我不知道为什么,指定的字符串是%d)。我在x86上看到的关于scanf的例子使用的是纯粹的或用NASM语法编写的,因此我试图将它们适用于我的程序。使用scanf与x86 GAS组件

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 

RCX和RAX用printf返回1和0输入字符或字符串后分别检查(唯一方法程序不段错误)。

任何有关如何在x86气体组件中正确使用scanf的见解都将非常感谢!

+0

'scanf'是*标准库*函数,而不是“系统函数”。对于第二个参数,它期望一个将存储结果的内存地址。你可以将一个寄存器的值移动到RSI中,并期望它将数据存储在前一个寄存器中 - 这没有任何意义。使用LEA。 – 2014-11-23 22:44:07

回答

3

如你所想,movq %rcx, %rsi是不正确的。你需要传递一个指向内存的指针。寄存器不是内存地址空间的一部分,因此你不能有指向它们的指针。您需要全局或本地分配存储。顺便说一句,你不应该把你的数据(特别是可写)放到默认的.text部分,因为这是用于代码的,并且通常是只读的。另外,调用约定通常要求16字节的堆栈指针对齐,所以你也应该注意这一点。

.globl main 

main: 
    push %rbp  # keep stack aligned 
    movq $0, %rax # clear rax 
    movq $f, %rdi # load format string 
    movq $x, %rsi # set storage to address of x 
    call scanf 
    pop %rbp 
    ret 

.data 

f: .string "%d" 
x: .long 0 

(如果您的环境需要一个前导下划线上的符号,然后使用_main,大概_scanf。)

,如果你的变量是在地址空间的下部4GiB以上才有效。另外,您也可以使用RIP相对寻址创建的指针,它可能看起来像:

leaq f(%rip), %rdi # load format string 
    leaq x(%rip), %rsi # set storage to address of x 

您还可以使用加载完整的64位地址助记符movabsq

movabsq $f, %rdi # load format string 
    movabsq $x, %rsi # set storage to address of x 

根据要求:本地变量版本可能如下所示:

subq $8, %rsp  # allocate 8 bytes from stack 
    movq $0, %rax  # clear rax 
    movabsq $f, %rdi # load format string 
    movq %rsp, %rsi # set storage to local variable 
    call scanf 
    addq $8, %rsp  # restore stack 
    ret 
+0

非常感谢!为了将来的参考,我将如何将全局x替换为本地内存空间? – user3210373 2014-11-23 23:36:58