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

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

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 

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

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

这是关于如何正确调用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 )是不好的做法。 (还记得我所说的没有正确对齐堆栈吗?如果你在进入时设置一个完整的“调用框架”然后单独留下堆栈指针直到退出,那么保持堆栈对齐要容易得多。从技术上讲,你只需要拥有尽管如此,堆栈指针在每个调用指令处对齐。)

您也没有正确结束该function:

  27 addq $8, %rs 

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

  movq %rbp, %rsp popq %rbp ret 

顺便提一下,“AT&T”汇编语法用于许多不同的CPU架构。 在谈论汇编语言时,我们总是需要首先了解CPU架构; 语法变体(如果有)是次要的。 您应该标题为“我的汇编程序(x86-64,AT&T语法)……”

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

 #include  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编译器执行与您不同的操作时,这可能意味着它知道您不知道的内容,并且您应该了解这些内容。

re:更新代码:

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

Arg传递寄存器被调用破坏。 你调用printf而不将format-string放入%rdi ,你必须假设在scanf返回后保持垃圾。

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