Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 汇编(AT&;T 32位)扫描问题_Assembly_Printf_Scanf_Att - Fatal编程技术网

Assembly 汇编(AT&;T 32位)扫描问题

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

我想在汇编中编写以下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之前添加):

我不明白为什么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