C 通过%esp进行程序集堆栈管理

C 通过%esp进行程序集堆栈管理,c,gcc,assembly,C,Gcc,Assembly,我真的不明白为什么gcc在调用函数之前要将12减去esp pushl %ebp movl %esp,%ebp sub $12,%esp socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); movl $AF_INET,(%esp) 首先,推送基指针的值,这会使堆栈指针的值递减。由于推送操作实际上会使sp向上移动,因此实际上会使地址递减。然后,c程序的堆栈框架由代码seg组成,代码seg上面有函数的参数,函数的

我真的不明白为什么gcc在调用函数之前要将12减去esp

    pushl   %ebp
    movl    %esp,%ebp
    sub $12,%esp

    socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    movl    $AF_INET,(%esp)

首先,推送基指针的值,这会使堆栈指针的值递减。由于推送操作实际上会使sp向上移动,因此实际上会使地址递减。然后,c程序的堆栈框架由代码seg组成,代码seg上面有函数的参数,函数的参数位于sp之上。现在,当您想要访问传递给函数的第一个参数时,您需要添加12个字节,因为3个字最终需要弹出12个字节才能获得该参数

我发现这个资源非常有用

当前的*x86 ABI要求堆栈指针在函数调用时与mod 16对齐。这是对堆栈指针进行其他无法解释的调整的典型原因


*我之所以说当前,是因为GCC实际上单方面更改了ABI,并在3.x系列的某个地方引入了此需求。我手头没有参考资料,但也许其他人可以提供。该更改旨在优化SIMD指令的使用,但实际上并不需要这样做,最终在旧代码调用假定对齐的新代码时破坏了ABI与旧代码的兼容性。整个故事一团糟。

sub
是减法而不是加法;)它是用来分配局部变量的,与套接字调用无关。我试图让这个.esp指向堆栈,从中减去一些值使它指向一个较低的地址,然后中间的内存用于变量?我说得通吗?是的,就是这样。你的代码片段是C和asm的混合体,写出来时没有意义。它没有写出来,它由gccIf生成,调用时堆栈在16字节边界上对齐,然后在32位X86上,调用将4字节返回地址推送到堆栈上,堆栈不再在16字节边界上。此外,只有被调用函数的第一个参数位于16字节边界上,其余参数取决于第一个参数的大小和其余参数的对齐规则。我不确定这是什么意思。@rcgldr:重点是被调用方可以在进入时知道堆栈指针mod 16的对齐方式,从而可以创建与16字节边界对齐的自动存储对象,而无需对齐堆栈指针本身。当然,对齐堆栈指针本身并不重要,只会给需要对齐到16字节的对象的函数带来成本,并且可以避免破坏与旧ABI的兼容性,这就是为什么整个约定是完全愚蠢的。但这就是历史……或者被调用方可以只向典型的输入序列添加一条指令:| push ebp | mov ebp,esp |和esp,0fffff0h |。@rcgldr:没错。如果被调用方没有使用帧指针,则情况会稍微复杂一些,但仍然不错。你在重复我的论点,为什么GCC团队对ABI的这一更改是一个错误,但遗憾的是,这是一个很久以前的错误,我们现在一直在坚持。如果没有帧指针,编译器可以尝试跟踪esp/rsp,但是具有可变大小的_alloca()会打乱这一策略。编译器必须将esp保存在其他寄存器中才能处理_alloca()大小写,因此最好使用ebp。我想VisualStudio不会这么做。