我的(AT& T)程序集(x86-x64)代码应该增加但不增加

时间:2017-09-24 12:18:34

标签: c assembly x86-64 att

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

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行是必要的,但是,我认为我没有使用我在那里的堆栈空间,所以我怀疑错误与此有关。我不确定当然。提前谢谢。

编辑,更新的代码(现在仍在子程序中调用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  

它现在运行并递增,但是,当递增的值被输出时,在值之后会出现一些奇怪的符号。

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

2 个答案:

答案 0 :(得分: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),而当您致电printf时,您会传递要打印的movq -8(%rbp), %rsi; incq %rsi)。

(事实上,当你致电printf时,你应该使用稍微不同的格式字符串,因为你需要在数字后打印换行符,所以"%ld\n"会是更好。)

您当前的代码以不同的方式几乎。我是这样做的,因为在函数中间弄乱堆栈指针(popq %rax)是不好的做法。 (还记得我所说的没有正确对齐堆栈吗?如果你在进入时设置一个完整的"调用框架"然后单独留下堆栈指针直到退出,那么保持堆栈对齐要容易得多从技术上讲,只有必需才能使堆栈指针在每条调用指令的位置对齐。)

您也没有正确结束该功能:

 27     addq $8, %rs  

我认为你没有复制并粘贴整个程序 - 看起来它已经被中断了。无论如何,如果您首先要打扰一个帧指针(x86-64上不需要帧指针),您应该再次使用它来退出:

        movq %rbp, %rsp
        popq %rbp
        ret

顺便提一下," AT& T"汇编语法用于许多不同的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编译器做出与你不同的事情时,这可能意味着它知道你不知道的东西,你应该了解那些东西。

答案 1 :(得分:0)

re:更新代码:

  

它现在运行并递增,但是,当递增的值被输出时,在值之后会出现一些奇怪的符号。

Arg传递寄存器是call-clobbered。你调用printf而不将格式字符串放入%rdi,你必须假设在scanf返回后保留垃圾。

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