我知道嵌套函数调用中的数据会转到堆栈.堆栈本身实现了一个逐步的方法,用于在函数被调用或返回时从堆栈中存储和检索数据.这些方法的名称大多称为Prologue和结语.
我试图搜索关于这个主题的材料没有成功.你们知道关于函数序言和结语如何在C中起作用的任何资源(网站,视频,文章)吗?或者,如果你能解释会更好.
PS:我只是想要一些一般的观点,不是太详细.
我正在阅读一本教科书,其中显示了基于 C 代码的汇编代码:
代码:
void echo()
{
char buf[8];
otherFunction(buf);
}
Run Code Online (Sandbox Code Playgroud)
汇编代码:
echo:
subq $24, %rsp //Allocate 24 bytes on stack, but why allocate 24 instead of 8 bytes?
movq %rsp, %rdi //Compute buf as %rsp
call otherFunction
Run Code Online (Sandbox Code Playgroud)
我不明白为什么堆栈指针%rsp会减少 24 个字节。我只将 8 个字节的缓冲区分配为char buf[8];,并且没有被调用者保存的寄存器压入堆栈,指令不应该是
subq $8, %rsp
Run Code Online (Sandbox Code Playgroud) 有人可以解释为什么这个代码:
#include <stdio.h>
int main()
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当使用tcc使用tcc编译时,生成这个asm:
00401000 |. 55 PUSH EBP
00401001 |. 89E5 MOV EBP,ESP
00401003 |. 81EC 00000000 SUB ESP,0
00401009 |. 90 NOP
0040100A |. B8 00000000 MOV EAX,0
0040100F |. E9 00000000 JMP fmt_vuln1.00401014
00401014 |. C9 LEAVE
00401015 |. C3 RETN
Run Code Online (Sandbox Code Playgroud)
我猜可能是
00401009 |. 90 NOP
Run Code Online (Sandbox Code Playgroud)
也许有一些内存对齐,但是怎么样
0040100F |. E9 00000000 JMP fmt_vuln1.00401014
00401014 |. C9 LEAVE
Run Code Online (Sandbox Code Playgroud)
我的意思是为什么编译器会插入跳转到下一条指令的近跳转,LEAVE会执行呢?
我在64位Windows上使用TCC 0.9.26生成32位可执行文件.
这就是我通过阅读一些内存分段文档所理解的:当调用一个函数时,有一些指令(称为函数序言)将帧指针保存在堆栈上,将堆栈指针的值复制到基本指针中并保存一些局部变量的内存.
这是我尝试使用GDB调试的一个简单代码:
void test_function(int a, int b, int c, int d) {
int flag;
char buffer[10];
flag = 31337;
buffer[0] = 'A';
}
int main() {
test_function(1, 2, 3, 4);
}
Run Code Online (Sandbox Code Playgroud)
调试此代码的目的是了解调用函数时堆栈中发生的情况:因此我必须在执行程序的各个步骤(在调用函数之前和执行期间)检查内存.虽然我设法通过检查基指针来查看返回地址和保存的帧指针之类的东西,但我真的无法理解在反汇编代码之后我要写的内容.
拆解:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400509 <+0>: push rbp
0x000000000040050a <+1>: mov rbp,rsp
0x000000000040050d <+4>: mov ecx,0x4
0x0000000000400512 <+9>: mov edx,0x3
0x0000000000400517 <+14>: mov esi,0x2
0x000000000040051c <+19>: mov edi,0x1
0x0000000000400521 <+24>: call 0x4004ec <test_function>
0x0000000000400526 <+29>: pop rbp
0x0000000000400527 <+30>: ret
End …Run Code Online (Sandbox Code Playgroud) 编辑:
我在下面接受了一个答案,并在代码的最终修订版中添加了自己的答案。希望它能向人们展示阴影空间分配的实际示例,而不是更多的单词。
编辑2:我还设法在(所有内容的)YouTube视频的注释中找到了调用约定PDF的链接,该链接在Shadow Space和Linux的Red Zone上有一些有趣的花絮。可以在这里找到:http : //www.agner.org/optimize/calling_conventions.pdf
原版的:
我在这里和整个Internet上都看过其他几个问题,但是当在64位Windows程序集中调用子例程/ Windows API时,似乎找不到合适的分配“影子空间”的示例。
我的理解是:
sub rsp,<bytes here>先call calleeadd rsp,<bytes here>考虑到这一点,这就是我尝试过的方法:
section .text
start:
sub rsp,0x20 ; <---- Allocate 32 bytes of "Shadow space"
mov rcx,msg1
mov rdx,msg1.len
call write
add rsp,0x20
mov rcx,NULL
call ExitProcess
ret
write:
mov [rsp+0x08],rcx ; <-- use the Shadow space
mov [rsp+0x10],rdx ; <-- and again
mov rcx,STD_OUTPUT_HANDLE ; Get handle to StdOut
call GetStdHandle
mov rcx,rax ; …Run Code Online (Sandbox Code Playgroud)