通过使用O2选项,就不会有检查栈平衡的代码,还可能没有保存环境、使用ebp保存当前栈底等一系列操作,代码会变得简洁高效。
因为函数调用会有不定参数的问题,如果参数是不定参数的时候,被调用函数不知道具体的参数数目,就需要调用他的函数平衡栈。但是正常情况下,被调用函数可以自己平衡栈。
三种调用约定
所以,可以通过传参方式和平衡栈的方式来判断调用方式
退出函数时会通过 ret x; 的方式平衡栈顶,等价于 esp += x
有时不一定通过ret平衡,也可能通过pop等指令平衡,具体需要看代码怎么使用栈的
在被调用函数中不需要操作,调用函数的call指令下面add esp,x;来平衡栈
复写传播,_cdecl方式的函数在同一作用域多次使用的时候,,最后可以一起平衡栈
所以,实际传参效率 _fastcall > _cdecl > _stdcall
在不是O2选项时,会使用ebp寻址局部变量
否则在O2选项中,为了节省寄存器,使用esp寻址
寻址的本质不过是对ebp或者esp做加减法操作,使得地址产生偏移,获取对应的值
因为IDA考虑到方便区分参数和局部变量,所以对于局部变量的寻址使用负数标号
因为调用函数的过程,是先将参数压栈,然后使用call指令,所以参数的偏移应该是正数。
函数调用过程
使用push指令将数据压入栈中,而push实际上是把操作数复制到栈顶,所以此时压入栈的数据和原数据是不同的,相互独立。所以函数中修改参数,不会影响原来的数据。
C/C++将不定长参数的函数定义为:
只要获得第一个参数的地址,那么只要对这个地址多加法就能获得其他参数。
获取参数类型是为了解释地址中的数据。
printf就是通过第一个参数获取参数总数的,字符串中几个%就是几个参数(%%)除外
一般来说都是给eax赋值来传递返回值的
而如果是结构体,成员只有两个就使用eax和edx传递返回值。
原文:https://www.cnblogs.com/cjdty/p/12768129.html