汇编作业疑问:多参数函数调用时被调用者寄存器入栈及栈操作分析
嘿,我完全懂你在汇编作业里卡壳的那种迷茫——原理没摸透的时候,对着汇编代码确实像看天书。我把你需要完成的分析任务对应的核心原理拆解清楚,你可以对着这些思路去对应代码:
一、计算栈的字节大小
栈的字节大小本质上是当前函数栈帧的总占用空间,核心思路是看栈指针的变化和被保存数据的总和:
- 对于x86(32位)架构:栈帧从基址寄存器
ebp延伸到栈指针esp。首先看函数开头的sub esp, N指令,N是直接分配给局部变量的字节数;再加上被push到栈上的被调用者保存寄存器的总字节数(比如每个32位寄存器占4字节);如果函数用了enter指令(比如enter 0x10, 0),那第一个参数就是分配的栈空间大小,同样要加上保存的寄存器字节数。 - 对于x86-64(64位)架构:逻辑类似,只是寄存器是64位的(每个占8字节),栈指针用
rsp,分配空间的指令是sub rsp, N。
举个简单例子:如果函数开头有push ebp(占4字节)、mov ebp, esp、sub esp, 0x10,还push了ebx和esi(各4字节),那栈帧总大小就是0x10 + 4 + 4 + 4 = 0x1C字节(额外加的4是保存的ebp)。
二、识别分配和释放局部栈空间的汇编语句
不同架构下的核心指令很明确:
- 分配栈空间:
- 最常见的是直接调整栈指针:x86用
sub esp, 立即数,x86-64用sub rsp, 立即数; - 也可能用
enter指令,比如enter 0x20, 0,第一个参数就是要分配的栈空间字节数。
- 最常见的是直接调整栈指针:x86用
- 释放栈空间:
- 函数结尾的
leave指令(等价于mov esp, ebp; pop ebp),直接把栈指针恢复到函数开始前的位置,释放所有局部栈空间; - 也可能直接用
add esp, 立即数/add rsp, 立即数手动恢复栈指针,再配合ret指令弹出返回地址。
- 函数结尾的
三、找出存储在被调用者保存寄存器中的局部值
首先得明确被调用者保存寄存器的范围:
- x86(32位):
ebp、ebx、esi、edi; - x86-64(64位):
rbp、rbx、r12-r15。
这些寄存器的规则是:被调用函数如果要修改它们,必须先把原值push到栈上保存,函数结束前再pop恢复。所以找局部值的方法是:
- 先看函数开头有没有
push这些寄存器的语句(比如push ebx); - 然后找函数中对这些寄存器的赋值操作(比如
mov ebx, 0x456),这里的0x456就是存在被调用者保存寄存器里的局部值; - 这类寄存器适合存储需要在调用其他函数时保留的局部数据,因为调用约定保证它们的值不会被被调用函数破坏。
四、找出存储在栈上的局部值
栈上的局部变量通常通过基址寄存器(ebp/rbp)的偏移来访问,核心特征是:
- x86中,局部变量的访问地址是
[ebp - N](比如mov DWORD PTR [ebp-4], eax,就是把eax的值存在栈上的局部变量里); - x86-64中,是
[rbp - N]或者直接用rsp的偏移(不过rbp作为基址更常见); - 另外,函数参数是存在
[ebp + N](x86)的位置,要注意和局部变量区分开; - 还要注意:被push到栈上的寄存器原值不属于局部值,那是为了遵守调用约定保存的寄存器内容。
关于你提到的「程序为何无法将所有……」的部分,虽然内容不完整,但常见的原因可能包括:栈空间大小限制、寄存器数量有限(无法容纳所有局部值)、调用约定强制要求某些数据存在栈上、或者内存对齐规则限制等。如果能补充完整问题描述,我可以帮你更精准地拆解原理~
内容的提问来源于stack exchange,提问作者Sean Weiss
相关产品推荐
相关产品推荐

