Mips 栈的一些特性
原文: http://blog.csdn.net/swwcyb/article/details/8164805
参数传递:如果参数少于4个,通过a0-a3寄存器传递参数,否则其余通过堆栈传递。
参数作为调用者(caller)栈帧的一部分,4个32bits空间为a0~a3预留(即使参数通过寄存器传递)。被调者(callee)在函数前言部分 分配自己的栈空间分配(返回地址/栈帧指针/局部变量),同时栈帧指针(fp)将指向最新的栈空间,并且所有局部变量通过栈帧指针偏移寻址,堆栈指针 (sp)不再发生变化。
以下代码调用栈结构如下图所示:
- int FunA()
- {
- return FunB();
- }
- int FunB()
- {
- return FunC();
- }
在函数前言部分,往往有如下语句:
- 00400264 <fun5>:
- 400264: 27bdffe0 addiu sp,sp,-32 //分配栈帧
- 400268: afbf001c sw ra,28(sp) //保存返回地址
- 40026c: afbe0018 sw s8,24(sp) //保存堆帧指针
- 400270: 03a0f021 move s8,sp //更新栈帧指针
编译器在编译函数时,能够计算出其局部变量以及参数需要的存储空间,在函数前言部分通过偏移sp指针为这些变量分配空间。例如funB()分配的栈空间为:ra+fp+local variables+arguments。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- int fun5(int a,int b, int c,int d,int e)
- {
- int abc = e;
- return fun4(a,b,c,d)+ abc;
- }
- int main(void)
- {
- int ret;
- int a;
- int b;
- int c;
- int d;
- a = 1;
- b = 2;
- c = 3;
- d = 4;
- ret = fun5(a,b,c,d,5);
- return 0;
- }
其反汇编代码(无优化选项)
- 00400264 <fun5>:
- 400264: 27bdffe0 addiu sp,sp,-32 /*1个局部变量+4个参数+ ra +fp + 1个pad = 32*/
- 400268: afbf001c sw ra,28(sp)
- 40026c: afbe0018 sw s8,24(sp)
- 400270: 03a0f021 move s8,sp
- 400274: afc40020 sw a0,32(s8)
- 400278: afc50024 sw a1,36(s8)
- 40027c: afc60028 sw a2,40(s8)
- 400280: afc7002c sw a3,44(s8)
- 400284: 8fc20030 lw v0,48(s8)
- 400288: 00000000 nop
- 40028c: afc20010 sw v0,16(s8)
- 400290: 8fc40020 lw a0,32(s8)
- 400294: 8fc50024 lw a1,36(s8)
- 400298: 8fc60028 lw a2,40(s8)
- 40029c: 8fc7002c lw a3,44(s8)
- 4002a0: 0c100084 jal 400210 <fun4>
- 4002a4: 00000000 nop
- 4002a8: 8fc30010 lw v1,16(s8)
- 4002ac: 00000000 nop
- 4002b0: 00431021 addu v0,v0,v1
- 4002b4: 03c0e821 move sp,s8
- 4002b8: 8fbf001c lw ra,28(sp)
- 4002bc: 8fbe0018 lw s8,24(sp)
- 4002c0: 03e00008 jr ra /*jump reg*/
- 4002c4: 27bd0020 addiu sp,sp,32
- 004002c8 <main>:
- 4002c8: 27bdffc8 addiu sp,sp,-56
- 4002cc: afbf0034 sw ra,52(sp)
- 4002d0: afbe0030 sw s8,48(sp)
- 4002d4: 03a0f021 move s8,sp
- 4002d8: 24020001 li v0,1 /*没有采用优化选项,所以对每个变量的操作均会访问内存*/
- 4002dc: afc2001c sw v0,28(s8)
- 4002e0: 24020002 li v0,2
- 4002e4: afc20020 sw v0,32(s8)
- 4002e8: 24020003 li v0,3
- 4002ec: afc20024 sw v0,36(s8)
- 4002f0: 24020004 li v0,4
- 4002f4: afc20028 sw v0,40(s8)
- 4002f8: 24020005 li v0,5
- 4002fc: afa20010 sw v0,16(sp)
- 400300: 8fc4001c lw a0,28(s8)
- 400304: 8fc50020 lw a1,32(s8)
- 400308: 8fc60024 lw a2,36(s8)
- 40030c: 8fc70028 lw a3,40(s8)
- 400310: 0c100099 jal 400264 <fun5> /* jal:jump and link*/
- 400314: 00000000 nop
- 400318: afc20018 sw v0,24(s8)
- 40031c: 00001021 move v0,zero
- 400320: 03c0e821 move sp,s8
- 400324: 8fbf0034 lw ra,52(sp)
- 400328: 8fbe0030 lw s8,48(sp)
- 40032c: 03e00008 jr ra
- 400330: 27bd0038 addiu sp,sp,56
还有一篇文章介绍mips栈使用特性: http://www.newsmth.net/nForum/#!article/Programming/2823
发信人: newmirror (yestoday once more), 信区: Programming
标 题: 总结了一下mips的栈使用惯例,也许有人感兴趣.
发信站: BBS 水木清华站 (Fri Jun 14 12:14:01 2002)
stack usage convention(栈使用惯例?)
1.stack 在内存中朝低地址的方向增长.
2.栈指针寄存器 sp ($29)指向栈的最低的word address(栈顶)
3.栈内的内容通过从sp的正的word offset来引用.
4.一个函数需要的所有的栈空间(活动记录)必须在函数开始的时候一次全部分配,还必须
双字对齐.
5.栈空间的分配是通过减去栈指针(加上一些需要保持对齐的)
6.当从过程返回的时候,sp加上它在过程被调用的时候减去的值,活动记录被回收.
7.没有子函数的函数至少需要24个字节的空间:为a0-a3(输入参数)保留4个字的,返回地
址一个字和一个
字用来双字对齐.保存a0-a3这样使得调用函数可以在被调用的函数中保留输入的参数.这
个规则一开始
引入是为了处理参数个数可变的函数,但是现在不管什么情况都保存a0-a3.
8.一个函数的活动记录按照地址升序;依次存放:
8.1:a0-a3
8.2:传递给被调用函数的参数(outgoing arguments)
8.3:从双字对齐的位置开始保存浮点寄存器.(从号最小的寄存器开始)
8.4:保存寄存器.(从号最小的开始)(其中包括返回地址寄存器ra)
8.5:函数分配给局部变量的空间(local)
9. .frame指令通常和三个参数相联系.
9.1:sp
9.2:存放fp的位置.
9.3:返回地址.
btw:
1)outgoing arguments:
Outgoing arguments are only meaningful for nonleaf routine
The Outgoing Arguments area must be large enough to hold all the arguments p
assed when calling another function, including all arguments passed in regis
ters.
2)a0-a3:incoming args
---------------------------------------------
过程格式:
一个过程主要包括三个部分:prologue,body,epilogue
prologue:
1.规定函数入口点.
2.分配栈空间.
3.保存需要的寄存器.
epilogue:
1.恢复寄存器.
2.回收空间.
3.把控制权返回给caller.