找回密码
 注册
关于网站域名变更的通知
查看: 371|回复: 1
打印 上一主题 下一主题

armv7指令集 C函数调用 堆栈指针SP的变化

[复制链接]
  • TA的每日心情

    2019-11-19 15:32
  • 签到天数: 1 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2021-2-2 17:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    + Y+ A) `& r2 {
    首先说明一下,ARM中函数调用不同的编译器可能差别很大,即使都是arm-linux的交叉编译器,也有差别,有的编译器把r7寄存器作为栈帧寄存器(fp),有的把r11寄存器作为栈帧指针(fp),例如arm-linux-gnueabihf-gcc用的r7和arm-linux-gnueabi-gcc用的r11,另外在函数执行开头的处理也不一样
    . \. H) n& J# ^( |0 P8 A% x- P( t% |* Z) Y0 r. n/ ]3 G
    1. arm-linux-gnueabihf-gcc编译器先给函数中变量分配栈空间,然后将fp和sp指向栈顶8 x3 }! C2 B: x$ z9 ]. ]9 Q
    ! s9 u' C3 {" |$ h
    2. arm-linux-gnueabi-gcc编译器先让fp和sp指向栈顶(栈底),然后再给函数中的变量分配栈空间,之后sp指向新的栈顶,而fp指向的老的栈顶变成了函数的栈底。( |) ^7 u4 M+ q! T' A

    2 ]5 I" o% T1 |5 e# v2 w8 K, c个人感觉第二种比较好理解,但是总而言之,每次函数开始执行都需要保存fp指针到栈空间,这里的被保存fp指针是上一个函数的数据(一般是栈底数据),然后立即将本函数的栈底数据保存到fp指针, 函数调用结束,根据fp的数据恢复sp指针,从而释放结束函数的栈空间。
    / b' M* k8 o. @5 s1 p1 Y
    - \& a% j! @, K( I6 V: A; r这么看来,函数调用需要入栈的数据只有前一个函数的fp指针,然后跳转的时候会将跳转指令bl的下一条指令地址保存到 lr寄存器,方便函数调用结束后返回。当然如果函数入参很多,寄存器不够用,这些参数同样需要入栈。( r$ O- G* T. b; @( ^4 L# b
    & K' T- s: p! F9 S' R+ a" p1 f+ M
    下面贴出两种编译器的C代码以及反汇编:1 G- v3 e2 t3 i$ t2 h6 `

    2 |7 L0 F7 c! m8 b下面我们看C程序:; x+ r5 |; r- R8 X

    * P0 c. Y* `2 B
    • int m = 8;
    • int fun(int a,int b)
    • {
    •     int c = 0;
    •     c = a + b;
    •     return c;
    • }
    • int main()
    • {
    •     int i = 4;
    •     int j = 5;
    •     m = fun(i, j);
    •     return 0;
    • }
      + D- m+ n5 ]+ P
    - w! p3 t: y4 }

    # `8 D" l8 ]+ }' }* P0 M: Z2 _使用的编译器是arm-linux-gnueabihf-gcc,针对的芯片是I.Mx6ull,对应的反汇编代码:
    4 H5 S! q/ P. P: d+ w5 }; T) k& g: `4 J- e5 T( X6 B
    • Disassembly of section .text:
    • 00010094 <fun>:
    •    10094:        b480              push        {r7}
    •    10096:        b085              sub        sp, #20
    •    10098:        af00              add        r7, sp, #0
    •    1009a:        6078              str        r0, [r7, #4]
    •    1009c:        6039              str        r1, [r7, #0]
    •    1009e:        2300              movs        r3, #0
    •    100a0:        60fb              str        r3, [r7, #12]
    •    100a2:        687a              ldr        r2, [r7, #4]
    •    100a4:        683b              ldr        r3, [r7, #0]
    •    100a6:        4413              add        r3, r2
    •    100a8:        60fb              str        r3, [r7, #12]
    •    100aa:        68fb              ldr        r3, [r7, #12]
    •    100ac:        4618              mov        r0, r3
    •    100ae:        3714              adds        r7, #20
    •    100b0:        46bd              mov        sp, r7
    •    100b2:        f85d 7b04         ldr.w        r7, [sp], #4
    •    100b6:        4770              bx        lr
    • 000100b8 <main>:
    •    100b8:        b580              push        {r7, lr}
    •    100ba:        b082              sub        sp, #8
    •    100bc:        af00              add        r7, sp, #0
    •    100be:        2304              movs        r3, #4
    •    100c0:        607b              str        r3, [r7, #4]
    •    100c2:        2305              movs        r3, #5
    •    100c4:        603b              str        r3, [r7, #0]
    •    100c6:        6839              ldr        r1, [r7, #0]
    •    100c8:        6878              ldr        r0, [r7, #4]
    •    100ca:        f7ff ffe3         bl        10094 <fun>
    •    100ce:        4602              mov        r2, r0
    •    100d0:        f240 03e4         movw        r3, #228        ; 0xe4 全局变量m的地址
    •    100d4:        f2c0 0302         movt        r3, #2
    •    100d8:        601a              str        r2, [r3, #0]
    •    100da:        2300              movs        r3, #0
    •    100dc:        4618              mov        r0, r3
    •    100de:        3708              adds        r7, #8
    •    100e0:        46bd              mov        sp, r7
    •    100e2:        bd80              pop        {r7, pc}
    • Disassembly of section .data:
    • 000200e4 <m>:
    •    200e4:        00000008         andeq        r0, r0, r8 /* 这里00000008是全局变量m的值,被解读成andeq指令了 */
      5 n4 T( P7 J. C4 t  I- D  C) ^
        - F7 w: [6 m# _$ V
    5 D9 g$ Z" e. }
    使用arm-linux-gnueabi-gcc编译器对应的反汇编代码:: l$ y8 U% g( c4 R3 ]3 e* d8 u  f

    7 V. `6 h# K( W  B) T6 x7 Q
    • 00010400 <fun>:
    •    10400:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
    •    10404:       e28db000        add     fp, sp, #0
    •    10408:       e24dd014        sub     sp, sp, #20
    •    1040c:       e50b0010        str     r0, [fp, #-16]
    •    10410:       e50b1014        str     r1, [fp, #-20]  ; 0xffffffec
    •    10414:       e3a03000        mov     r3, #0
    •    10418:       e50b3008        str     r3, [fp, #-8]
    •    1041c:       e51b2010        ldr     r2, [fp, #-16]
    •    10420:       e51b3014        ldr     r3, [fp, #-20]  ; 0xffffffec
    •    10424:       e0823003        add     r3, r2, r3
    •    10428:       e50b3008        str     r3, [fp, #-8]
    •    1042c:       e51b3008        ldr     r3, [fp, #-8]
    •    10430:       e1a00003        mov     r0, r3
    •    10434:       e24bd000        sub     sp, fp, #0
    •    10438:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
    •    1043c:       e12fff1e        bx      lr
    • 00010440 <main>:
    •    10440:       e92d4800        push    {fp, lr}
    •    10444:       e28db004        add     fp, sp, #4
    •    10448:       e24dd008        sub     sp, sp, #8
    •    1044c:       e3a03004        mov     r3, #4
    •    10450:       e50b300c        str     r3, [fp, #-12]
    •    10454:       e3a03005        mov     r3, #5
    •    10458:       e50b3008        str     r3, [fp, #-8]
    •    1045c:       e51b1008        ldr     r1, [fp, #-8]
    •    10460:       e51b000c        ldr     r0, [fp, #-12]
    •    10464:       ebffffe5        bl      10400 <fun>
    •    10468:       e1a02000        mov     r2, r0
    •    1046c:       e59f3010        ldr     r3, [pc, #16]   ; 10484 <main+0x44> 全局变量m的地址
    •    10470:       e5832000        str     r2, [r3]
    •    10474:       e3a03000        mov     r3, #0
    •    10478:       e1a00003        mov     r0, r3
    •    1047c:       e24bd004        sub     sp, fp, #4
    •    10480:       e8bd8800        pop     {fp, pc}
    •    10484:       00021024        andeq   r1, r2, r4, lsr #32
        Y4 X& H. e5 L* ^5 W5 j5 W

    6 Y3 Q# G" v  a- t' G+ K" m1 ?0 E& ^/ p4 _% u0 o! a1 v
      个人感觉参考第二份反汇编代码比较好理解堆栈的变化过程,其实函数调用就两个要点:3 n& \/ a  k; X3 G3 e0 K  _( }

    + v, ~; ~* c( g2 I4 g) q1. 要保存好函数自己的栈底地址,保证函数结束栈空间能够正常释放,但只有一个fp指针,所以使用fp指针存储本函数的栈底地址之前,要将上一个函数的栈底地址入栈也就是,现将fp入栈,然后再使用fp保存新的栈底指针。
    7 _5 V& G$ u( Z3 V, `0 R* _+ u/ W+ X5 `6 W+ {7 K6 s' i
    2. 使用bl指令,保证函数调用结束能跳回。将bl指令下一条地址保存到lr寄存器中。

    该用户从未签到

    2#
    发表于 2021-2-2 18:06 | 只看该作者
    学习                              
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 19:52 , Processed in 0.171875 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表