【发布时间】:2017-04-10 11:08:33
【问题描述】:
所以我必须编写一个调用 extern C 函数的汇编程序。所以我写了简单的pow函数,我用这个C代码编译了我的汇编程序。一切正常。但正如我从gcc 的-S 命令中看到的,编译器为局部变量留出了空间。我以为会是这样的:
int func(int number)
{
int a = 10;
int b = 5;
int c = 0;
}
我们有 3 个局部变量,所以编译器会subl $12, %esp。但它适用于subl $16, %esp。即使我在这里只留下一个数字,它仍然会减少16。现在我有了我的代码:
main.s:
.section .data
.XD:
msg: .ascii "%d\n"
msg_len = . - msg
.text
.globl _pow
.globl main
main:
pushl %ebp
movl %esp, %ebp
movl $5, %eax #like int a = 5;
pushl %eax
call _pow #_pow(a);
movl %eax, -8(%ebp)
pushl -8(%ebp)
pushl $.XD
call printf #printf("%d\n", _pow(a));
movl $0, %eax
leave
ret
func.c:
int _pow(int number)
{
return number * number;
}
它按预期工作,./main 打印出25。但是现在,我没有subl 来自%esp 的任何东西。我的意思是我可以添加这一行,但它不会改变任何东西。我在互联网上搜索过,我发现我的代码可能只是偶然地工作,通常我应该减少%esp。所以我的问题是:我应该在main 中减少%esp 的值是多少?应该是12 还是16?
【问题讨论】:
-
16的使用很可能使堆栈保持 16 字节对齐。 -
我猜你是对的,但是我应该让它总是 16 字节对齐,还是应该
subl特定值? -
该要求可能在平台的应用程序二进制接口 (ABI) 文档中指定。由于它是一个规范,因此您应该遵循它以便能够与其他代码进行交互。
-
这是偶然的,因为有两个原因:1)在
movl %eax, -8(%ebp)和pushl -8(%ebp)之间没有其他东西会使用你的堆栈空间(32b x86保护模式下的中断在内核级别使用它们自己的堆栈空间)。 2)pushl -8(%ebp)会将值完全存储在同一位置[ebp-8],因此您正在用自己覆盖该值,因此不会损坏任何东西(如果我在头中正确模拟它,请检查调试器,验证esp和@ 987654348@ 地址在push之前/之后,并查看内存视图中的值)。推送其他内容会覆盖本地 var 值。 -
关于通过
sub而不是and对齐堆栈 -> 如果 gcc 可以完全控制上面的函数调用,它知道当前堆栈对齐在特定调用深度,所以它可以产生一个sub esp,imm32和16之类的常量,既为局部变量保留空间,又在未来调用其他函数之前将堆栈再次对齐16B。所以and esp,-16不是唯一可能的方法(尽管and更健壮,因为即使在输入此类代码时堆栈状态未知,它也可以工作)。
标签: c linux assembly x86-64 stack-pointer