Assembly 对装配说明感到困惑

Assembly 对装配说明感到困惑,assembly,byte,intel,att,Assembly,Byte,Intel,Att,我正在阅读关于汇编的教程:我遇到了以下基本汇编代码: .text .globl _main _main: pushl %ebp movl %esp, %ebp subl $8, %esp movl $0, %eax leave ret 我有点理解其中的大部分,但我不知道为什么要调用subl$8,%esp。我知道它从esp中减去8个字节,但我不知道为什么这是必要的,也不知道为什么要这样做。教程说它将堆栈平衡到一个16字节的边界上,但我不知道

我正在阅读关于汇编的教程:我遇到了以下基本汇编代码:

    .text
.globl _main
_main:
    pushl %ebp
    movl %esp, %ebp
    subl $8, %esp
    movl $0, %eax
    leave
    ret
我有点理解其中的大部分,但我不知道为什么要调用
subl$8,%esp
。我知道它从esp中减去8个字节,但我不知道为什么这是必要的,也不知道为什么要这样做。教程说它将堆栈平衡到一个16字节的边界上,但我不知道堆栈的“平衡”是什么意思,也不知道为什么使用数字8会产生一个16字节的边界

本教程后面将演示如何定义函数,并按如下方式调用它:

    .text
.globl _doSomething
_doSomething:
    pushl %ebp
    movl %esp, %ebp
    subl $8, %esp
    nop
    leave
    ret

.globl _main
_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $3, (%esp)
    call    _doSomething
    movl    $0, %eax
    leave
    ret
这一行的教程是“8对齐,16表示我们的4字节参数和填充:
subl$24,%esp

但是如果有一个4字节的参数和填充,为什么我们要使用数字16?还有,什么参数

我在英特尔Core mac上,运行OS X 10.9.3,使用gcc-S-m32进行编译


我对组装很陌生,所以请尽可能简单地回答。谢谢

如何在机器语言级别调用函数是由一种称为调用约定的东西强制规定的,这种约定通常是特定于体系结构和操作系统的。它的设计使您能够以互操作的方式调用其他函数。在这种情况下,调用约定指定被调用函数可以预期,调用函数时堆栈指针在16字节边界上对齐

这意味着堆栈指针(
esp
)是16字节的倍数


之所以这样做,是因为某些指令只对存储在内存位置(16的倍数)中的数据起作用,这主要是出于性能原因。其他指令甚至可以处理未对齐的数据,但效率要低得多

如何在机器语言级别调用函数是由一种称为调用约定的东西强制规定的,这种约定通常是特定于体系结构和操作系统的。它的设计使您能够以互操作的方式调用其他函数。在这种情况下,调用约定指定被调用函数可以预期,调用函数时堆栈指针在16字节边界上对齐

这意味着堆栈指针(
esp
)是16字节的倍数


之所以这样做,是因为某些指令只对存储在内存位置(16的倍数)中的数据起作用,这主要是出于性能原因。其他指令甚至可以处理未对齐的数据,但效率要低得多

让我们看一系列说明:

1. nop   #call-stack is aligned to 16 bytes (sp is multiple of 16) to start.
2. call function #pushes return address (4 bytes) onto stack.

---(called function)
3. push %ebp #pushes base-pointer (4 bytes) onto stack, which is now 8-byte aligned
---cannot call function that expects to find 16-byte aligned stack---
4. sub $8, %esp #aligns stack to 16 bytes
5. call other_function

让我们看一系列说明:

1. nop   #call-stack is aligned to 16 bytes (sp is multiple of 16) to start.
2. call function #pushes return address (4 bytes) onto stack.

---(called function)
3. push %ebp #pushes base-pointer (4 bytes) onto stack, which is now 8-byte aligned
---cannot call function that expects to find 16-byte aligned stack---
4. sub $8, %esp #aligns stack to 16 bytes
5. call other_function
如果您想问“为什么程序看起来不像这样?”:

你完全正确。以下四条说明:

pushl %ebp
movl %esp, %ebp
subl $8, %esp
leave
在本例中,它们一点用处都没有

如果手工编写此汇编函数(或对C编译器输出进行后期优化),则不会编写(或删除)这四条指令

然而,大多数C编译器都是针对“正常情况”进行优化的:只返回常量值(这里就是这种情况)的C函数并不经常出现

然而,在大多数C函数(有变量和实代码)中,上面显示的四条指令是有意义的!这就是为什么大多数C编译器总是插入这四条指令,即使在特殊情况下不需要它们

我猜汇编教程之所以插入这四条指令,是因为在下一步中会向函数添加更多指令…

如果您想问“为什么程序看起来不像这样?”:

你完全正确。以下四条说明:

pushl %ebp
movl %esp, %ebp
subl $8, %esp
leave
在本例中,它们一点用处都没有

如果手工编写此汇编函数(或对C编译器输出进行后期优化),则不会编写(或删除)这四条指令

然而,大多数C编译器都是针对“正常情况”进行优化的:只返回常量值(这里就是这种情况)的C函数并不经常出现

然而,在大多数C函数(有变量和实代码)中,上面显示的四条指令是有意义的!这就是为什么大多数C编译器总是插入这四条指令,即使在特殊情况下不需要它们


我猜汇编教程之所以插入这四条指令,是因为在下一步中会向函数中添加更多指令…

谢谢。在我的第一个示例中,当调用subl$8,%esp时,我如何知道它需要减去8个字节?当调用函数时,您能够知道堆栈对齐方式是什么,返回地址为4个字节(64位8个字节),堆栈传递的每个参数为适当的字节数。谢谢。因此,在我的第一个示例中,当调用subl$8,%esp时,我如何知道它需要减去8个字节?当调用函数时,您能够知道,返回地址的堆栈对齐将从4个字节(8对64位)中减去什么以及堆栈传递的每个参数的适当字节数。如何知道%ebp是4字节?
%ebp
是一个4字节(32位)寄存器。它不可能是其他任何东西。@addison好吧,如果堆栈在
调用
-指令之前对齐了16字节(%esp=0模16),那么它在被调用函数的开头对齐了4字节(%esp=4模16)。您如何知道%ebp是4字节?
%ebp
是一个4字节(32位)寄存器。它不可能是其他任何东西。@addison好的,如果堆栈在
调用
-指令之前对齐了16字节(%esp=0模16),那么它将在被调用函数的开头对齐4字节(%esp=4模16)。请看。您现在可以跳过优化部分,但这也是一篇关于调用约定和对齐等的非常好的文章。谢谢!我会开始读这篇文章。看一看。您现在可以跳过优化部分,但这也是一篇关于调用约定和对齐等的非常好的文章。谢谢!我要开始读这个了。