是否所有函数都有一个 "function prologue" 来减少局部变量的堆栈指针?
do all functions have a "function prologue" that decrements the stack pointer for locals?
我有几个关于组装的基本问题。
在汇编中编写函数时,是否所有函数都有一个"function prologue"?我在网上看到一些功能,但他们没有,这让我很困惑。我认为您总是需要通过递减堆栈来获取新的基指针和局部变量?
另外,是否每条入栈指令都需要栈指针的减法指令,因为我们总是希望栈指针位于栈顶,如果不是,为什么?所以如果
push %eax
sub , %esp
我的最后一个问题是,是否有任何资源可以通俗易懂地解释装配?
制作您自己的示例相当简单,只需要在函数的堆栈上有一些东西(不会优化掉)。
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 [=10=]x110,%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 [=10=]x1,%rbx
2e: 48 83 fb 40 cmp [=10=]x40,%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 [=10=]x110,%rsp
56: 5b pop %rbx
57: c3 retq
(它没有链接所以调用中的立即数是零所以链接器可以填充它们)
正如 Jester 在评论中指出的那样,您可以代替向 rsp 减去或添加常量,而是放置一长串推送和弹出。这只是浪费代码 space 和执行时间,加法和减法更有意义。
现在有人可能会争辩说,您可以实现此代码,以便每次通过循环推送 more_fun(); 的结果;仅在函数需要时增加堆栈,并根据需要使用推送而不是从 rsp 中减去。但是在 return 上,您希望在一条指令中添加到 rsp,而不是浪费时间和 space 在单个 pops 或 pops 循环上。作为编译器作者,此解决方案更难调试,并且更难理解为编译器输出的 user/reader。 (如果你想在堆栈上保持 64 位对齐并说这里的 unsigned int 是 32 位,那么你将需要更多的指令来解决这个问题)
我有几个关于组装的基本问题。
在汇编中编写函数时,是否所有函数都有一个"function prologue"?我在网上看到一些功能,但他们没有,这让我很困惑。我认为您总是需要通过递减堆栈来获取新的基指针和局部变量?
另外,是否每条入栈指令都需要栈指针的减法指令,因为我们总是希望栈指针位于栈顶,如果不是,为什么?所以如果
push %eax
sub , %esp
我的最后一个问题是,是否有任何资源可以通俗易懂地解释装配?
制作您自己的示例相当简单,只需要在函数的堆栈上有一些东西(不会优化掉)。
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 [=10=]x110,%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 [=10=]x1,%rbx
2e: 48 83 fb 40 cmp [=10=]x40,%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 [=10=]x110,%rsp
56: 5b pop %rbx
57: c3 retq
(它没有链接所以调用中的立即数是零所以链接器可以填充它们)
正如 Jester 在评论中指出的那样,您可以代替向 rsp 减去或添加常量,而是放置一长串推送和弹出。这只是浪费代码 space 和执行时间,加法和减法更有意义。
现在有人可能会争辩说,您可以实现此代码,以便每次通过循环推送 more_fun(); 的结果;仅在函数需要时增加堆栈,并根据需要使用推送而不是从 rsp 中减去。但是在 return 上,您希望在一条指令中添加到 rsp,而不是浪费时间和 space 在单个 pops 或 pops 循环上。作为编译器作者,此解决方案更难调试,并且更难理解为编译器输出的 user/reader。 (如果你想在堆栈上保持 64 位对齐并说这里的 unsigned int 是 32 位,那么你将需要更多的指令来解决这个问题)