我有一些关于装配的基本问题。
在汇编中编写函数时,所有函数都有一个"函数序言"?我看到一些在线写的功能,而且他们没有这个让我感到困惑的功能。我认为你总是需要通过减少堆栈来获得新的基本指针和局部变量吗?
每个push指令都需要一个stackpointer的减法指令,因为我们总是希望堆栈指针位于堆栈的顶部,如果不是为什么?所以,如果
push %eax
sub $4, %esp
我的最后一个问题是,是否有任何资源用于解释外行人的装配?
答案 0 :(得分:2)
制作你自己的例子相当简单,只需要在函数堆栈中需要一些东西(不会优化掉)。
unsigned int more_fun ( unsigned int );
void fun_too ( unsigned int *);
void fun ( void )
{
unsigned int ra;
unsigned int ray[64];
for(ra=0;ra<64;ra++) ray[ra]=more_fun(ra);
fun_too(ray);
}
0000000000000000 <fun>:
0: 53 push %rbx
1: 31 db xor %ebx,%ebx
3: 48 81 ec 10 01 00 00 sub $0x110,%rsp
a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
11: 00 00
13: 48 89 84 24 08 01 00 mov %rax,0x108(%rsp)
1a: 00
1b: 31 c0 xor %eax,%eax
1d: 0f 1f 00 nopl (%rax)
20: 89 df mov %ebx,%edi
22: e8 00 00 00 00 callq 27 <fun+0x27>
27: 89 04 9c mov %eax,(%rsp,%rbx,4)
2a: 48 83 c3 01 add $0x1,%rbx
2e: 48 83 fb 40 cmp $0x40,%rbx
32: 75 ec jne 20 <fun+0x20>
34: 48 89 e7 mov %rsp,%rdi
37: e8 00 00 00 00 callq 3c <fun+0x3c>
3c: 48 8b 84 24 08 01 00 mov 0x108(%rsp),%rax
43: 00
44: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4b: 00 00
4d: 75 09 jne 58 <fun+0x58>
4f: 48 81 c4 10 01 00 00 add $0x110,%rsp
56: 5b pop %rbx
57: c3 retq
(它没有链接,因此调用中的立即数为零,因此链接器可以填充它们)
而不是向rsp中减去或添加常量,而是可以像Jester在注释中指出的那样添加一长串推送和弹出。这只是浪费代码空间和执行时间,加减法更有意义。
现在有人可能会争辩说你可以实现这个代码,这样每次循环都可以推送more_fun();仅在函数需要时增加堆栈,并根据需要使用推送而不是从rsp中减去。但是在返回时你需要在一条指令中添加rsp,而不是浪费时间和空间在单个弹出或一个流行音乐循环。作为编译器作者,此解决方案更难以调试,并且更难理解为编译器输出的用户/读取器。 (如果你想在堆栈上保持说64位对齐并说这里的unsigned int是32位,如图所示那么你就会烧掉一些更多的指令来解决这个问题)