2017-09-24 174 views
2

我正在尝试在程序集中制作一个小程序(对于AT & T)。我试图以整型的形式从用户那里得到一个输入,然后递增,然后输出递增的值。但是,该值不会增加。我花了最后几个小时尝试所有我能想到的东西,但它仍然不起作用,所以我有这样的想法,即我可能理解集会中的一个概念不好,导致我没有发现错误。这是我的代码:我的(AT&T)程序集(x86-x64)代码应该增加,但不会

1 hiString: .asciz "Hi\n" 
    2 formatstr: .asciz "%ld" 
    3 
    4 .global main 
    5 
    6 main: 
    7  movq $0, %rax   #no vector registers printf 
    8  movq $hiString, %rdi #load hiString 
    9  call printf    #printf 
10  call inout    #inout 
11  movq $0, %rdi   #loading exit value into register rdi 
12  call exit    #exit 
13 
14 inout: 
15  pushq %rbp    #Pushing bp 
16  movq %rsp, %rbp   #Moving sp to bp 
17  subq $8, %rsp   #Space on stack for variable 
18  leaq -8(%rbp), %rsi 
19  movq $formatstr, %rdi #1st argument scanf 
20  movq $0, %rax   #no vector for scanf registers 
21  call scanf    #scanf 
22  incq %rsi 
23  call printf 

从教程中,我得到了我的朋友,我了解到,线路17至19是必要的,但是,我想我不使用堆栈空间我ADRESS那里,所以我怀疑这个错误有什么。我不确定的课程。先谢谢你。

编辑,更新的代码(printf的仍然是所谓的现在子程序)

1 hiString: .asciz "hi\n" 
    2 formatstr: .asciz "%ld" 
    3 
    4 .global main 
    5 
    6 main: 
    7  movq $0, %rax   
    8  movq $hiString, %di 
    9  call printf    
10  call inout    
11  movq $0, %rdi   
12  call exit    
13 
14 inout: 
15  pushq %rbp    
16  movq %rsp, %rbp   
17  subq $8, %rsp   
18  leaq -8(%rbp), %rsi 
19  movq $formatstr, %rdi 
20  movq $0, %rax   
21  call scanf    
22  popq %rax 
23  incq %rax 
24  movq %rax, %rsi 
25  movq $0, %rax 
26  call printf 
27  addq $8, %rs 

它运行和增量但是,现在,当增加值outputed,那里显示的数值后一些奇怪的迹象。

编辑:无所谓,上面只发生过一次,现在没有增加值输出,只有奇怪的迹象。

+0

您的上次编辑再次从'inout'的末尾删除'ret'。 –

回答

1

这是关于如何正确调用scanf的经典混淆的汇编级版本。

14 inout: 
15  pushq %rbp    #Pushing bp 
16  movq %rsp, %rbp   #Moving sp to bp 
17  subq $8, %rsp   #Space on stack for variable 
18  leaq -8(%rbp), %rsi 
19  movq $formatstr, %rdi #1st argument scanf 
20  movq $0, %rax   #no vector for scanf registers 
21  call scanf    #scanf 

到现在为止你的代码是正确的(除非你没有正确对齐堆栈,但不用担心,现在,scanf将可能让你逃脱它)。

22  incq %rsi 

这是你出错的地方。在调用之前,您将RSI(scanf的第二个参数寄存器)设置为指针到存储位置。 scanf从stdin读取一个数字,并将它写入到那个存储位置,而不是RSI。

从评论中的讨论,你的意图是增加一个scanf读取的值,并立即打印出来。正如其他几个人指出的那样,在scanf返回后,您不能认为您加载到RSI,RDI或RAX的值是完整的。 (x86-64 psABI指定通过函数调用保留哪些寄存器:整数寄存器中只有RBX,RBP和R12到R15被保留。如果您打算在x86上进行大量的汇编编程,您应该阅读本文档以了解覆盖范围。-64(注意:Windows使用不同的ABI这不,据我所知,任何相关文档。)),所以你必须在呼叫从无到有设立printf

 movq -8(%rbp), %rsi # load variable as arg 2 of printf 
     incq %rsi    # and add one 
     movq $formatstr, %rdi # first argument to printf 
     xorl %rax, %rax  # no vector args to printf 
     call printf 

狠抓之间的区别scanfprintf这里:你可以对两者使用相同的格式字符串,但是当你拨打scanf时,你通过地址的存储位置leaq -8(%rbp), %rsi),whe当您拨打printf时,您会通过要打印的movq -8(%rbp), %rsi; incq %rsi)。

(其实你应该当你调用printf使用略有不同的格式字符串,因为你需要的号码后打印换行符,所以"%ld\n"效果会更好。)

您当前的代码做几乎这个,用不同的方式。我这样做是因为在函数的中间乱堆栈指针(popq %rax)是不好的做法。 (还记得我之前提到的不正确对齐堆栈吗?如果你在进入时设置了一个完整的“调用帧”,然后直接退出堆栈指针直到退出,那么保持堆栈对齐就容易多了。从技术上讲,你只需要需要必须在每次调用指令的点对准堆栈指针,虽然)

您还没有正确地结束这个函数:

27  addq $8, %rs 

我想你没有复制和粘贴整个程序 - 这看起来像是在线路中间被切断了。无论如何,如果你去打扰首先有一个帧指针(帧指针不需要对x86-64的),你应该再使用它退出:“AT & T”

 movq %rbp, %rsp 
     popq %rbp 
     ret 

顺便说一句,汇编语法用于许多不同的CPU体系结构。在谈论汇编语言时,我们总是需要知道CPU架构第一个;语法变体(如果有的话)是次要的。你应该已经题为这个问题:“我的汇编程序(X86-64,AT & T语法)......”

作为建议的最后一块,我建议你编译这个C程序

#include <stdio.h> 

static void inout(void) 
{ 
    long x; 
    scanf("%ld", &x); 
    printf("%ld\n", x+1); 
} 

int main(void) 
{ 
    printf("hi\n"); 
    inout(); 
    return 0; 
} 

选择C编译器,使用等效于-S -O2 -fno-inline(即:生成文本汇编语言,优化但不执行任何内联)的选项,然后逐行读取汇编输出。每当C编译器做了与你不同的事情时,这可能意味着它知道你不知道的东西,你应该知道这件事。

+0

感谢您的回答和整体反馈,但是,我认为我仍然需要一些更多的指针。我实际上尝试了您提供的代码,但是,我仍然没有得到正确的输出结果。(当我在inout子程序中调用printf时,我仍然得到非递增的输入,当我在那里删除printf,并在我的主例程中调用它(将rax的内容移动到rsi后),我根本没有输出。所以,我仍然不明白我认为的东西 –

+1

OP应该进一步注意,函数被允许(并且通常会)破坏他们在返回时收到的参数,rsi包含你最初放在那里的值从'scanf'返回 – fuz

+0

@RomeeB请给我们显示您的更新代码,不要从问题中删除原始代码,只需将更新后的代码粘贴到下面 – fuz

0

重:更新的代码:

它运行和增量但是,现在,当增加值outputed,那里显示的值后一些奇怪的迹象。

通过转换的寄存器被调用了。您在不将格式字符串放入%rdi的情况下调用printf,在scanf返回后,您必须假定其保留垃圾。

用调试器单步执行代码。使用ni来跳过gdb中的call。 (有关GDB提示,请参阅标记wiki的底部)。

相关问题