打印
[开发工具]

C语言在ARM中函数调用时,栈是如何变化的?

[复制链接]
102|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
kmzuaz|  楼主 | 2024-12-30 12:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Arm指令集介绍
崇尚简单粗暴的介绍方式,我们直接来看各个寄存器的大体用法,详细用法可百度,不,谷歌。
1.    r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。---如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。2.    r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。r11 是栈帧指针 fp。3.    r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。4.    寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。5.    寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复6.    寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。
演示代码
假如现在你已经掌握了 arm 指令的用法,即便没有掌握也没关系,“书到用时回头翻”。这里以一段简单的 c 语言为例:
代码语言:javascript
复制


#include .h= ;int (,int b{    int c 0= a ;    ;main)= ;    int j 5= (i)return ;编译一下,然后反汇编:
$ arm-linux-gnueabi-gcc main.c -o main $ arm-linux-gnueabi-objdump -D -D main
代码语言:javascript
复制


<fun:   :       e52db004        push    }            (str fp[sp-])   :       e28db000        add     fp, #10408, sp20   1040c, , #1610410, , #20; 10414, #10418, , #8:       e51b2010        ldr     r2[fp-]   :       e51b3014        ldr     r3[fp-]  0xffffffec   :       e0823003        add     r3, r3   :       e50b3008        str     r3[fp-]   1042c, , #810430, r3   :       e24bd000        sub     sp, #10438{fp; , ]4:       e12fff1e        bx      lr<main:   :       e92d4800        push    , lr10444, sp4   :       e24dd008        sub     sp, #:       e3a03004        mov     r34   :       e50b300c        str     r3[fp-]   :       e3a03005        mov     r35   :       e50b3008        str     r3[fp-]   1045c, , #810460, , #121046410400 >   :       e1a02000        mov     r2:       e59f3010        ldr     r3[pc16; <main0x4410470, ]   :       e3a03000        mov     r30   :       e1a00003        mov     r0:       e24bd004        sub     sp, #10480{fp}   :       , r2, lsr #如何能让读者接受吸收的更快,我一直觉得按照学习效率来讲的话顺序应该是视频,图文,文字。反正我是比较喜欢视频类的教学。这里给大家画下栈变化的过程是什么样子的。这里的图是结合上面的代码来画的,希望有助于读者的理解。1.程序在内存分布区域



2.全局变量m赋值



3.保存进入main之前的栈底, fp-sp之间是当前函数栈



4.函数main的栈已经准备好了



5.i入栈



6.j入栈



7.准备函数fun的调用, 形参反向入栈 先形参b入栈



8.形参a入栈



9.留空一个地址作为fun返回值, 待后面返回时填入



10.fun返回地址入栈, 通常是main函数当前pc指针的下一个



11.main函数的栈底地址入栈



12.pc指针跳转fun代码



13.c入栈



14.可以看到函数fun的数据 形参a,b 在上一层函数的栈中. 一部分在自己的栈上. 此步取值到加法器中进行加法运算,再赋值给c



15.c赋给返回值,填入上面的留空位置



16.栈底恢复上一层



17.lr赋值给pc, 实现了跳转



18.返回值赋值给全局变量m



19.前面函数调用的形参已经无用,回滚sp



20.函数返回,清理main的栈空间





使用特权

评论回复
沙发
gejigeji521| | 2024-12-30 19:24 | 只看该作者
现在学单片机不用考虑这个了吧

使用特权

评论回复
板凳
dongnanxibei| | 2024-12-30 22:05 | 只看该作者
从来没有考虑过这个问题。

使用特权

评论回复
地板
和下土| | 2024-12-30 23:32 | 只看该作者
被调用的函数在返回之前不需要恢复这些寄存器的值,除非调用的函数自己需要。

使用特权

评论回复
5
和下土| | 2024-12-30 23:33 | 只看该作者
我们在 C 代码中嵌入汇编指令时,实际上是直接操作这些寄存器。

使用特权

评论回复
6
申小林一号| | 2024-12-31 16:34 | 只看该作者
感谢分享,学习一下

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

3139

帖子

0

粉丝