ARM C函数调用堆栈入栈顺序
ARM C函数调用堆栈入栈顺序
堆栈指针是在函数一开头就确认了的,比如如下的xxx_func.cfi函数,它在函数的开头就将sp自减了0x170,这个0x170是xxx_fun.cfi函数局部变量total size + 需要入栈的reg total size
然后会设置x29(fp,栈底指针),这里看到是sp - 0x110,可以看到需要入栈的reg total size为0x60,所以fp指向了函数局部变量列表的头部,它是不包含函数里的局部变量的
00000000000441c8 <xxx_func.cfi>: 441c8: d105c3ff sub sp, sp, #0x170 441cc: a9117bfd stp x29, x30, [sp,#272] 441d0: a9126ffc stp x28, x27, [sp,#288] 441d4: a91367fa stp x26, x25, [sp,#304] 441d8: a9145ff8 stp x24, x23, [sp,#320] 441dc: a91557f6 stp x22, x21, [sp,#336] 441e0: a9164ff4 stp x20, x19, [sp,#352] 441e4: 910443fd add x29, sp, #0x110 441e8: 90000008 adrp x8, 0 <__stack_chk_guard>
当函数返回时,从stack弹出值到之前保护reg而入栈的reg里,这里可以看到出栈时顺序恰好和入栈时相反,即是后入先出,将fp、lr从stack弹出到fp、lr即实现返回:
447f4: a9564ff4 ldp x20, x19, [sp,#352] 447f8: a95557f6 ldp x22, x21, [sp,#336] 447fc: a9545ff8 ldp x24, x23, [sp,#320] 44800: a95367fa ldp x26, x25, [sp,#304] 44804: a9526ffc ldp x28, x27, [sp,#288] 44808: a9517bfd ldp x29, x30, [sp,#272] 4480c: 9105c3ff add sp, sp, #0x170 44810: d65f03c0 ret
函数调用stack变化,入栈顺序
以main()里有int a、int b两个局部变量并call了一个test_func(int i, int j),在test_func()里有define一个int c变量为例,来看下调用test_func()时的堆栈变化:
调用test_func时,test_func参数i、j先反序入栈,然后是给test_func的返回值预留空间,然后是test_func的返回地址;
接下来是在test_func函数里将需要保存的reg入栈,其中x29、x30是必须要入栈的,此时会将fp指向当前的堆栈顶部,这里x29、x30是最后才入栈的;
然后再是为test_func中的局部变量开辟堆栈空间,此时将sp指向此时的堆栈顶部:
|--------------------| |..... | |--------------------| |int a | |--------------------| |int b | |--------------------| |int j | |--------------------| |int i | |--------------------| |test_func返回值预留 | |--------------------| |test_func返回地址 | |--------------------| fp-->|x29, x30 | /*如果test_func还需要保护其它reg,将其它reg也入栈*/ |--------------------| sp-->|int c | |--------------------|
int test_func(int i, int j) { int c = i + j;
return c; } int main() { int a = 3; int b =4; test_func(3, 4); return 0; }
所以fp指针是指向的当前函数的stack底部、sp指向的是stack顶部,在fp和sp之间即是test_func的所有局部变量区间,如果test_func没有一个局部变量,fp、sp将指向同一个位置,即都指向当前函数堆栈顶部。
如下是我自己在kernel里写的一个测试函数确认的入栈顺序:
-------- |-------------------------| -------- | | |-------------------------| 0x10 | x19 | |-------------------------| init_module() | x30 | stack |-------------------------| 0x10 size: fp->| x29 | 0x30 |-------------------------| | long __stack_chk_guard | |-------------------------| 0x10 sp->| | int ret | -------- |-------------------------| -------- | x19 | |-------------------------| 0x10 | x20 | |-------------------------| | x30 | |-------------------------| 0x10 test_func() fp->| x29 | stack frame |-------------------------| size: | long __stack_chk_guard | 0x50 |-------------------------| 0x10 | int b | int a | |-------------------------| | long c | |-------------------------| 0x10 | long d | |-------------------------| | | int e | |-------------------------| 0x10 sp->| long fp | -------- |-------------------------| --------
对应C函数:
static int __init hello_init(void){ int ret; pr_info("ret's addr: %#px.\n", &ret); test_func(3, 4); ret = register_chrdev(HELLO_MAJOR,DEVICE_NAME, &hello_flops); if (ret < 0) { printk(KERN_EMERG DEVICE_NAME " can't register major number.\n"); return ret; } printk(KERN_EMERG DEVICE_NAME " initialized.\n"); return 0; }
int test_func(int a, int b) { long c = 10; long d = 10; int e = 11; unsigned long fp; asm ("mov %0, x29" : "=r" (fp)); pr_info("a's addr: %#px.\n", &a); pr_info("b's addr: %#px.\n", &b); pr_info("fp is: %#lx.\n", fp); pr_info("c's addr: %#px.\n", &c); pr_info("d's addr: %#px.\n", &d); pr_info("e's addr: %#px.\n", &e); pr_info("fp's addr: %#px.\n", &fp); c = a + b; pr_info("c: %d.\n", c); return c; }
对应汇编,下面init_module()即是hello_init()
0000000000000000 <init_module>: 0: d100c3ff sub sp, sp, #0x30 4: a9017bfd stp x29, x30, [sp,#16] 8: f90013f3 str x19, [sp,#32] c: 910043fd add x29, sp, #0x10 10: 90000008 adrp x8, 0 <__stack_chk_guard> 14: f9400108 ldr x8, [x8] 18: f90007e8 str x8, [sp,#8]
0000000000000000 <test_func>: 0: d10143ff sub sp, sp, #0x50 4: a9037bfd stp x29, x30, [sp,#48] 8: a9044ff4 stp x20, x19, [sp,#64] c: 9100c3fd add x29, sp, #0x30 10: 90000008 adrp x8, 0 <__stack_chk_guard> 14: f9400108 ldr x8, [x8] 18: 2a0103f3 mov w19, w1 1c: 2a0003f4 mov w20, w0 20: f81f83a8 str x8, [x29,#-8]
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析