我正在尝试组装一个小程序(对于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
它现在运行并递增,但是,当递增的值被输出时,在值之后会出现一些奇怪的符号。
编辑:没关系,上面只发生过一次,现在没有输出增值,只有奇怪的迹象。
答案 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
请密切注意此处scanf
和printf
之间的区别:您可以为两者使用相同的格式字符串,但是当您致电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提示,请参阅x86标记wiki的底部。)