call_stack
- IA-32(Intel 32位) 寄存器
 Note: 32 位说明栈中指针偏移量以 32位(4字节)为一个基元。 - EAX~- EDX: 通用寄存器 (general purpose registers),- 通用寄存器是通用的,但是有一部分指令对寄存器有特殊要求,这时它们分别有如下用途:- EAX: 累加器 (“A” means Accumulator)
- EBX: 数据基地址暂存器 (“B” means Base)
- ECX: 循环计数器 (“C” means Counter)
- EDX: 操作数或操作结果暂存器 (“D” means Data)
 
- 为了方便存储较短的数据,通用寄存器都可以只使用低16位
 
- 通用寄存器是通用的,但是有一部分指令对寄存器有特殊要求,这时它们分别有如下用途:
- ESI和- EDI: 这两个寄存器优势也被包括在通用寄存器内,但是不能拆分,一般用作字符串操作的源指针和目的指针。
- ESP和- EBP:- ESP存储的是栈顶地址,即栈指针 (Stack Pointer),而- EBP存储的是位于栈顶的栈帧的栈底地址,即基指针 (Base Pointer)
 
- 控制权移交 - 控制权是指 CPU执行指令的顺序
- 当一个函数调用另一个函数时,CPU需要暂停当前函数的执行,转而去执行被调用函数。这个过程称为控制权移交。
 
- 控制权是指 
- 栈帧(Stack Frame) - 栈帧是堆栈的逻辑片段,当调用函数时逻辑栈帧被压入堆栈,当函数返回时逻辑栈帧被从堆栈中弹出。
- 栈帧的边界:EBP(Extended Base Pointer 扩展基指针) 指向当前栈帧底部(高地址),在当前栈帧内位置固定;ESP(Extended Stack Pointer 扩展栈指针) 指向当前栈帧顶部(低地址),当程序执行时ESP随着数据的入栈和出栈而移动。
 
- 函数调用栈 (Call Stack) - 函数调用栈的典型内存布局图: 
 这是 caller function 和 callee function 的栈帧布局,m(%ebp)表示以EBP为基地址、偏移量为m字节的内存空间(中的内容)。
- 函数序 (Prologue):被调函数设置 EBP,并保存其希望不变的寄存器值;被调函数为局部变量分配栈帧空间,并存储。
- 函数跋 (Epilogue):释放为局部变量分配的栈空间;恢复函数序中希望保持不变的寄存器值,包括主调函数的 EBP;被调函数将控制权交还主调函数(使用ret指令),这一步可能也会将先前的参数从栈上清除。
 
- 函数调用栈的典型内存布局图:
- 堆栈操作 - push: ESP 减小 4 个字节;将寄存器数据压入堆栈,从高到底按字节讲数据存入 ESP-1、ESP-2、ESP-3 指向的地址单元
- pop:从 ESP 指向的空间取回数据存入寄存器,ESP 增加 4 个字节。
- call:将当前的指令指针(EIP)压入栈,以备返回时恢复执行下一条指令;然后将 EIP 指向被调函数的代码开始处,跳转到被调函数的入口处执行。
- leave:恢复主调函数的栈帧。等价于:- 1 
 2- movl %ebp, %esp 
 popl %ebp
- ret:在执行- leave之后,从栈顶弹出返回地址(由- call指令压入栈中的)到 EIP,转到返回地址继续执行
 
- 函数调用约定: - 函数参数的传递顺序和方式:是通过栈传递还是通过寄存器与栈的组合传递 
- 栈的维护方式:被调函数返回时压栈的参数是由被调函数清除还是由主调函数清除 
- 名称修饰 (Name-mangling) 策略:编译器在链接时为区分不同函数,对函数名作不同修饰 
- x86函数参数传递方法:
 x86 处理器 ABI 规范规定,所有传递给被调函数的参数都通过堆栈完成,压栈顺序为以函数参数从右到左的顺序。这种方式使得被调函数通过- m(%ebp)读取参数时反而是随- m增大而顺序读取,支持不定数量参数的函数。- 整型和指针参数的传递相同,因为都是32bit,直接从右向左压栈。
- 浮点参数的传递:浮点参数的传递于整型的类似,只是浮点数据占用64bit。
- 结构体和联合体参数的传递:与整型的类似,但是注意数据对齐,占用字节数为 4 的倍数。
 
- 整型和指针参数的传递相同,因为都是
- x86函数返回值传递方法:- 若返回值不超过4字节,通常将其保存在EAX中,调用方通过EAX读取返回值。
- 若返回值大于4字节而小于8字节,则通过EAX+EDX联合返回,EAX保存低字节,EDX保存高字节。
- 若返回值为浮点类型,通过专用的协处理器浮点数寄存器的栈顶返回。
- 若返回值为结构体或联合体,传递返回值的方法根据编译器的不同,平台的不同,调用约定的不同,甚至编译参数的不同而不同,一种常见的方式是:- 在将被调函数的参数依次压栈之后再向栈中压入一个隐藏参数,它是用于保存返回结构体或联合体的一块栈空间的地址。
- 若未定义用于接收返回值的变量,在栈上额外开辟一块空间作为接收返回值的临时变量
- 被调函数将返回值拷贝到隐藏参数指向的内存空间,再将该地址存入EAX。
 
 
- 若返回值不超过4字节,通常将其保存在
- x86_64的寄存器传参:
 前六个参数分别使用- rdi, rsi, rdx, rcx, r8, r9传递,多余的参数均存储在栈上。
- x86_64的函数返回值传递:
 使用- rax传递第一个返回值。
 
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 HaoIne!
