我的(AT&T)程序集(x86-x64)代码应该增加,但不会
我正在尝试在程序集中制作一个小程序(对于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,那里显示的数值后一些奇怪的迹象。
编辑:无所谓,上面只发生过一次,现在没有增加值输出,只有奇怪的迹象。
这是关于如何正确调用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
狠抓之间的区别scanf
和printf
这里:你可以对两者使用相同的格式字符串,但是当你拨打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编译器做了与你不同的事情时,这可能意味着它知道你不知道的东西,你应该知道这件事。
重:更新的代码:
它运行和增量但是,现在,当增加值outputed,那里显示的值后一些奇怪的迹象。
通过转换的寄存器被调用了。您在不将格式字符串放入%rdi
的情况下调用printf
,在scanf
返回后,您必须假定其保留垃圾。
用调试器单步执行代码。使用ni
来跳过gdb中的call
。 (有关GDB提示,请参阅x86标记wiki的底部)。
您的上次编辑再次从'inout'的末尾删除'ret'。 –