我想在Assembly中编写以下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, @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之前添加):
addl $4,%esp #pop format
pushl 4(%esp)
pushl $fmt
我不明白为什么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
(遵循与之前推送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
答案 0 :(得分:1)
“它应该将%esp减去4,然后在esp位置保存%esp 指的是“
这可能发生在8086 CPU上。从80286开始,它在内部存储ESP,然后减去,然后写入内部存储的值。您必须首先分配变量(一次推送或一次esp-4)然后存储此变量的地址(第二次推送)。对于第一个问题,您必须进行3次推送或1次推送和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中获取它的地址。然后分配另一个var,将其地址存储在edx中。然后推送两个地址,然后推送fmt的偏移量。
你必须先分配空间。除非您要使用EBP相对地址解决本地变量,否则您不需要帧。 你可以推ebp - 4等 只需编译你的代码,看看它在任何调试器中是如何工作的(我使用ollydbg来检查你的代码); 最后,您可以要求C编译器生成asm列表并查看编译器如何执行此操作。
答案 1 :(得分:0)
有了这个:
pushl %esp #location of x
pushl $fmt
call scanf
你覆盖了EBP。
首先,CPU记住寄存器的值(旧的esp值),然后减去4,然后保存旧的ESP值。在这种情况下,这是旧的EBP。当你第一次减去4时,你在堆栈上分配一个变量(更好的是PUSH 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]