督促自己:2020-9-16
学习记录:
参数获取:
#include <stdio.h>
int f (int a, int b,int c)
(
return a*b+c;
);
int main()
{
printf("%d\n",f(1,2,3));
return 0;
);
-
x86
-
MSVC
关键汇编代码:
从被调用方函数的数据栈的角度来看,外部参考的偏移量是正值,而局部变量的偏移量是负值。当需要访问栈帧(stack frame)以外的数据时,被调用方函数可把会变宏(例如_a$)与EBP寄存器的值相加,从而求得所需地址。
-
GCC
汇编代码:
编译结果和MSVC结果相似,不同在用leave指令还原指令,而不是return 0。
-
-
x64
x86-64系统能够使用寄存器传递(前4个或前6个)参数,完全不需要访问栈。
-
MSVC
f()函数通过寄存器获取了全部的所需参数,此处求址的加法运算时通过lea指令实现。
-
MSVC未开启优化
位于寄存器的3个参数被推送到栈里,这种现象叫作“阴影空间/shadow space”。
使用阴影空间有以下两个优点:
1)通过栈传递参数,可避免浪费寄存器资源(有时可能会占用4个寄存器);
2)便于调试器debugger在程序中断时找到函数参数。
在使用阴影空间时,由调用方函数分配栈空间,由被调用方函数根据需要将寄存器参数转储到它们的阴影空间中。
-
GCC -x64
阴影空间只是微软的概念,GCC只有在寄存器数量容纳不下所有参数的情况下,才会使用栈传递参数。
-
ARM
-
Non-optimizing Keil 6/2013(ARM mode)
- 前4个寄存器(R0~R3)负责传递前4个参数
- f()函数通过前3个寄存器(R0~R2)读取参数。
- MLA(Multiply Accumulate)指令(乘法累加指令)将前两个操作数(R3和R1里的值)想乘,然后再计算第三个操作数(R2里的值)和这个积的和,并且把最终运算结果存储在零号寄存器R0之中。(R0寄存器存放返回值)
- BL指令把程序的控制流交给LR寄存器里的地址,而且会在必要的时候切换处理器的运行模式(Thumb模式和ARM模式之间进行模式切换)。如果它被Thumb模式的代码调用,BX指令不仅会进行相应的跳转,还会把处理器模式调整为Thumb。如果它被ARM模式的指令调用,则不会进行模式切换。
-
Optimizing Keil 6/2013(ARM mode)
MLA直接使用所有寄存器的参数并把返回值保存在R0寄存器里
-
Optimizing Keil 6/2013(Thumb mode)
Thmub模式的指令集里没有MLA指令,分为了两个指令:
- MULS指令计算R0和R1的积,结果存储在R1寄存器;
- ADDS计算R1和R2的和,结果存在R0寄存器里。
-
ARM64
ARM64中,MADD指令可以一次进行乘法和加法的混合算法,与MLA类似
“寄存器保护区/Register Save Area”:与阴影空间相似,把传入的参数保存在数据栈里,以防止后期的指令占用W0~W2寄存器,防止后续指令覆盖函数参数,起到保护传入参数的作用。
-
MIPS
函数所需的前4个参数由4个A-字头寄存器传递;
-
MIPS两个特殊的寄存器:HI和LO(用来存储MULT指令的乘法计算结果,64位的积)。
-
只有MFLO(访问积的低32位)和MFHI指令能够访问HI和LO寄存器。
-
ADDU(Add Unsigned)指令计算第三个参数与积的和
ADD指令可以出发溢出处理机制
ADDU不会引发溢出(因为C/C++不支持这种机制)
-
$V0寄存器存储32位的运算结果
JAL(Jump and Link)指令:使用相对地址–偏移量
JALR指令:跳转到寄存器存储的绝对地址里
-
明日任务:《逆向工程权威指南》上 第九章