装配新手。我有以下功能
int foo(char *argv[])
{
char buf[256];
bar(argv[1], buf);
}
汇编中的哪一个 -
0x08048473 <foo+0>:push %ebp
0x08048474 <foo+1>:mov %esp,%ebp
0x08048476 <foo+3>:sub $0x118,%esp
0x0804847c <foo+9>:mov 0x8(%ebp),%eax
0x0804847f <foo+12>:add $0x4,%eax
0x08048482 <foo+15>:mov (%eax),%edx
0x08048484 <foo+17>:lea -0x100(%ebp),%eax
0x0804848a <foo+23>:mov %eax,0x4(%esp)
0x0804848e <foo+27>:mov %edx,(%esp)
0x08048491 <foo+30>:call 0x8048454 <bar>
0x08048496 <foo+35>:leave
0x08048497 <foo+36>:ret
有人可以向我解释一下吗?为什么那里有280个?确保堆栈上分配256个字节。我无法解释其余的事情。
答案 0 :(得分:4)
堆栈用于局部变量,但也用于函数所需的中间值。在这里,您的foo()
函数通过给它两个指针调用bar()
,一个指向字符串(argv[1]
),另一个指向buf
变量;那些指针值被压入堆栈,即mov %eax,0x4(%esp)
和mov %edx,(%esp)
操作码。因此,foo()
需要多于256字节的堆栈空间。
更多细节:
0x08048473 <foo+0>:push %ebp
0x08048474 <foo+1>:mov %esp,%ebp
这是标准函数序言:函数将在调用之前使用%ebp
指向堆栈元素(即其参数)。
0x08048476 <foo+3>:sub $0x118,%esp
堆栈上保留了一些空间,主要(但不仅限于)buf[]
。
0x0804847c <foo+9>:mov 0x8(%ebp),%eax
0x0804847f <foo+12>:add $0x4,%eax
0x08048482 <foo+15>:mov (%eax),%edx
0x8(%ebp)
是argv
函数参数;这些操作码从argv[1]
获取指针并将结果存储在%edx
中。这将成为bar()
的第一个参数。
0x08048484 <foo+17>:lea -0x100(%ebp),%eax
这将%eax
的地址存储在buf[]
中 - 编译器确定buf[]
位于它为{{1}保留的堆栈空间的高256字节中}。
sub
0x0804848a <foo+23>:mov %eax,0x4(%esp)
0x0804848e <foo+27>:mov %edx,(%esp)
的两个参数被推到堆栈上(实际上,写在两个顶部堆栈位置,bar()
已经调整过了。)
%esp
0x08048491 <foo+30>:call 0x8048454 <bar>
被调用。
bar()
0x08048496 <foo+35>:leave
0x08048497 <foo+36>:ret
取消序幕(相当于leave
)。 mov %ebp, %esp; pop %ebp
退出该功能。
众所周知,GCC会在堆栈上进行一些分配;在这里,它可能保留了264个字节而不是280个。这似乎是其内部寄存器分配优化器的一个人工制品(额外的堆栈插槽 用于存储中间值,但优化器终于找到了方法仅在寄存器中保留相应的值。)