Assembly 汇编(AT&;T 32位)扫描问题
我想在汇编中编写以下C代码:Assembly 汇编(AT&;T 32位)扫描问题,assembly,printf,scanf,att,Assembly,Printf,Scanf,Att,我想在汇编中编写以下C代码: int main(void) { int x,y; scanf("%d%d",&x,&y); printf("%d%d",x,y); return 0; } 首先,我尝试只扫描/打印一个整数: .section .rodata #read only data section fmt: .string "%d%d" .text .globl main .type main
int main(void)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d%d",x,y);
return 0;
}
首先,我尝试只扫描/打印一个整数:
.section .rodata #read only data section
fmt: .string "%d%d"
.text
.globl main
.type main, @function
main:
pushl %ebp #save the old frame pointer
movl %esp, %ebp #create the new frame pointer
pushl %esp #location of x
pushl $fmt
call scanf
#stack should now have exactly the scanned number x and then the format, as needed for printf.
call printf
movl $0, %eax
movl %ebp, %esp #restore the old stack pointer - release all used memory.
popl %ebp #restore old frame pointer (the caller function frame)
ret
但它没有起作用。出于某种原因,以下技巧使其起作用(在printf之前添加):
我不明白为什么pushl 4(%esp)能让它工作,所以对于我的第一个问题,我想澄清一下这个问题。
然后我试着用两个变量做同样的事情:
fmt: .string "%d%d"
[...]
pushl %esp #location of x
pushl %esp #location of y
pushl $fmt
call scanf
但它造成了分割错误。它甚至没有到达printf部分,在那里我会尝试这样的东西:
addl $4,%esp #pop format
pushl 8(%esp)
pushl 8(%esp)
pushl $fmt
call printf
(遵循与之前pushl 4(%esp)相同的逻辑。
所以我的第二个问题是,我怎样才能使它在两个变量下工作。
谢谢大家!
编辑:为什么下面的代码不能扫描两个变量
subl $8,%esp #leave place for two vars
pushl -4(%ebp) #location of x
pushl -8(%ebp) #location of y
pushl $fmt
call scanf
为此:
pushl %esp #location of x
pushl $fmt
call scanf
您可以覆盖EBP
首先,CPU记住寄存器的值(旧的esp值),然后减去4,然后保存旧的esp值。在这种情况下,这是旧的EBP。当您第一次减去4时,您在堆栈上分配一个变量(最好推送EAX-它更短,仅1字节)
第二种情况也存在类似问题:
addl $4,%esp #pop format
pushl 8(%esp)
pushl 8(%esp)
pushl $fmt
这里第一个参数不是指向X,而是指向第二个参数。第二个参数指向EBP。
您必须首先在堆栈上分配变量:
push ebp
mov ebp, esp
push eax
mov edx, esp
push eax
mov eax, esp
push eax
push edx
push offset fmt
call xyz
更重要的是:如果您不使用本地变量,则不需要推动ebp,也不需要创建帧指针。或者,在堆栈上分配变量后,您可以使用以下方法:
LEA eax, [EBP - 4]
LEA edx, [EBP - 8]
“它应该将%esp减去4,然后将%esp保存在位置esp
指的是“
这可能发生在8086 CPU上。从80286开始,它在内部存储ESP,然后减去,然后写入存储在内部的值。您必须首先分配变量(一次推送或一次ESP-4),然后存储此变量的地址(第二次推送)。对于第一个问题,您必须进行3次推送或一次sub和2次推送。在您的情况下,esp指向存储旧EBP的堆栈位置。
你可以用
push eax
push esp
push fmt
这也行得通
另外,关于第二个问题,您提到了没有提到的行
即使是这样
哦,是的,我复制了错误的代码行,对不起。我正在参考这篇文章:
pushl %esp #location of x
pushl %esp #location of y
pushl $fmt
call scanf
我指出,为什么您的代码不正确。您必须推送变量的2个地址。相反,您推送旧EBP的地址,然后是带有prev参数(指向旧EBP)的堆栈中单元格的地址;结果是,在读取一个参数时出错,接收到您在scanf上输入的值。当它想写入另一个值而不是单元格地址时,它具有先前的int
最后,你能解释一下你建议的代码吗?我为什么要移动edx
而eax进入esp,我甚至不知道里面有什么
抱歉,这是Intel语法,因此mov eax,esp表示“将esp写入eax”。
这确实不是一个很好的代码。只是举个例子
我在堆栈上分配一个变量,在eax中获取它的地址。然后分配另一个变量,在edx中存储它的地址。然后推送两个地址,然后推送fmt的偏移量
您必须首先分配空间,并且不需要帧,除非您要使用EBP相对地址来寻址本地VAR。
你可以按ebp-4等。
只需编译代码并查看它在任何调试器中的工作方式(我使用ollydbg检查代码);
最后,您可以要求C编译器生成asm列表,并查看编译器是如何做到这一点的。我不明白,为什么PUSH%esp会覆盖%ebp?它应该将%esp减去4,然后将%esp保存在esp指向的位置。另外,关于第二个问题,您提到的行甚至都不重要,因为分段错误是更早触发的,所以这不是问题。最后,你能解释一下你建议的代码吗?为什么我要将edx和eax移动到esp中,我甚至不知道它们里面有什么…谢谢!作为另一个答案发布,因为它比允许的注释大。@nodwj:
push%esp
本身不会覆盖保存的EBP值(从推送%ebp
)。但它会将指向它的指针传递给scanf,要求scanf覆盖它。现在我明白了一切,除了为什么要麻烦推送%eax或诸如此类的东西。为什么不在esp之前简单地加上-4(即,让代码推送-4(%esp)#x的位置(和y相同,在scanf之前)做这个把戏?因为它不“做这个把戏”。如果你按esp-4,你仍然不会为变量分配内存。因此,如果我现在理解了这个问题,如果给scanf的地址指向自身,scanf就不能覆盖A地址。另外,为什么我编辑的问题最后的代码不起作用?(虽然它可以工作,但只有当scanf只写一次这个位置时——在第一次写之后,它会被写上值,因此,这是不安全的事情)您必须分配2个DWORD,然后获取这些DWORD的地址。如果我没记错的话,esp-val没有操作码,所以汇编器必须将其转换为2个补码。所以push reg-val需要6-8个字节,push eax-1个字节。对于您的解决方案,在scanf之后(如果有效)堆栈将有值,而不是地址。所以您必须为后续的printf调用计算smth。
pushl %esp #location of x
pushl %esp #location of y
pushl $fmt
call scanf