/ sfoolish_post / C 语言之函数栈结构分析

C 语言之函数栈结构分析

2013-09-04 posted in [嵌入式]

本文通过一个简单的代码实力,结合反汇编结果,及运行打印结果,来分析 C 语言的函数栈结构。

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int backtrace_test3 (void **buf, int size)
{
    int *sp;
    int *fp;
    int *p;
    int i = 0;

    asm volatile("mov %0, sp" :"=r"(sp)::"memory");
    printf("-%s:sp-----0x%8x\n", __func__, (unsigned int)sp);
    asm volatile("mov %0, fp" :"=r"(fp)::"memory");
    printf("-%s:fp-----0x%8x\n", __func__, (unsigned int)fp);

    asm volatile("mov %0, lr" :"=r"(p)::"memory");
    printf("--lr-----0x%8x\n",(unsigned int) p);
    asm volatile("mov %0, r4" :"=r"(p)::"memory");
    printf("--r4-----0x%8x\n\n",(unsigned int) p);

    p = sp;

    for(i = 0; i < 0x30; i++, p++)
        printf("%p----0x%8x\n", p, (unsigned int)*p);

    return size;
}

void backtrace_test2(void)
{
    int *sp;
    int *fp;
    void *array[5];
    int ret = -1;
    
    asm volatile("mov %0, sp" :"=r"(sp)::"memory");
    printf("-%s:sp-----0x%8x\n", __func__, (unsigned int)sp);
    asm volatile("mov %0, fp" :"=r"(fp)::"memory");
    printf("-%s:fp-----0x%8x\n", __func__, (unsigned int)fp);

    ret = backtrace_test3(array, 5);
}

void backtrace_test1(void)
{
    int *sp;
    int *fp;

    asm volatile("mov %0, sp" :"=r"(sp)::"memory");
    printf("-%s:sp-----0x%8x\n", __func__, (unsigned int)sp);
    asm volatile("mov %0, fp" :"=r"(fp)::"memory");
    printf("-%s:fp-----0x%8x\n", __func__, (unsigned int)fp);

    backtrace_test2();
}

int main()
{
    backtrace_test1();

    return 0;
}

不带优化选项测试

反汇编结果及简要说明

编译和反汇编命令:

arm-linux-uclibcgnueabi-gcc backtrace_test.c -o backtrace_test
arm-linux-uclibcgnueabi-objdump -d backtrace_test

汇编代码:

000083f4 <backtrace_test3>:
    83f4:       e92d4800        push    {fp, lr}
    83f8:       e28db004        add     fp, sp, #4      ; 0x4
    83fc:       e24dd018        sub     sp, sp, #24     ; 0x18
    8400:       e50b0018        str     r0, [fp, #-24]
    8404:       e50b101c        str     r1, [fp, #-28]
    8408:       e3a03000        mov     r3, #0  ; 0x0
    840c:       e50b3008        str     r3, [fp, #-8]
    8410:       e1a0300d        mov     r3, sp
    8414:       e50b3014        str     r3, [fp, #-20]
    8418:       e3083600        movw    r3, #34304      ; 0x8600
    841c:       e3403000        movt    r3, #0  ; 0x0
    8420:       e51b2014        ldr     r2, [fp, #-20]
    8424:       e1a00003        mov     r0, r3
    8428:       e3081698        movw    r1, #34456      ; 0x8698
    842c:       e3401000        movt    r1, #0  ; 0x0
    8430:       ebffffc4        bl      8348 <_init+0x24>
    8434:       e1a0300b        mov     r3, fp
    8438:       e50b3010        str     r3, [fp, #-16]
    843c:       e3083618        movw    r3, #34328      ; 0x8618
    8440:       e3403000        movt    r3, #0  ; 0x0
    8444:       e51b2010        ldr     r2, [fp, #-16]
    8448:       e1a00003        mov     r0, r3
    844c:       e3081698        movw    r1, #34456      ; 0x8698
    8450:       e3401000        movt    r1, #0  ; 0x0
    8454:       ebffffbb        bl      8348 <_init+0x24>
    8458:       e1a0300e        mov     r3, lr
    845c:       e50b300c        str     r3, [fp, #-12]
    8460:       e3083630        movw    r3, #34352      ; 0x8630
    8464:       e3403000        movt    r3, #0  ; 0x0
    8468:       e51b200c        ldr     r2, [fp, #-12]
    846c:       e1a00003        mov     r0, r3
    8470:       e1a01002        mov     r1, r2
    8474:       ebffffb3        bl      8348 <_init+0x24>
    8478:       e1a03004        mov     r3, r4
    847c:       e50b300c        str     r3, [fp, #-12]
    8480:       e3083648        movw    r3, #34376      ; 0x8648
    8484:       e3403000        movt    r3, #0  ; 0x0
    8488:       e51b200c        ldr     r2, [fp, #-12]
    848c:       e1a00003        mov     r0, r3
    8490:       e1a01002        mov     r1, r2
    8494:       ebffffab        bl      8348 <_init+0x24>
    8498:       e51b3014        ldr     r3, [fp, #-20]
    849c:       e50b300c        str     r3, [fp, #-12]
    84a0:       e3a03000        mov     r3, #0  ; 0x0
    84a4:       e50b3008        str     r3, [fp, #-8]
    84a8:       ea00000c        b       84e0 <backtrace_test3+0xec>
    84ac:       e3083660        movw    r3, #34400      ; 0x8660
    84b0:       e3403000        movt    r3, #0  ; 0x0
    84b4:       e51b200c        ldr     r2, [fp, #-12]
    84b8:       e5922000        ldr     r2, [r2]
    84bc:       e1a00003        mov     r0, r3
    84c0:       e51b100c        ldr     r1, [fp, #-12]
    84c4:       ebffff9f        bl      8348 <_init+0x24>
    84c8:       e51b3008        ldr     r3, [fp, #-8]
    84cc:       e2833001        add     r3, r3, #1      ; 0x1
    84d0:       e50b3008        str     r3, [fp, #-8]
    84d4:       e51b300c        ldr     r3, [fp, #-12]
    84d8:       e2833004        add     r3, r3, #4      ; 0x4
    84dc:       e50b300c        str     r3, [fp, #-12]
    84e0:       e51b3008        ldr     r3, [fp, #-8]
    84e4:       e353002f        cmp     r3, #47 ; 0x2f
    84e8:       daffffef        ble     84ac <backtrace_test3+0xb8>
    84ec:       e51b301c        ldr     r3, [fp, #-28]
    84f0:       e1a00003        mov     r0, r3
    84f4:       e24bd004        sub     sp, fp, #4      ; 0x4
    84f8:       e8bd8800        pop     {fp, pc}

000084fc <backtrace_test2>:
    84fc:       e92d4800        push    {fp, lr}
    8500:       e28db004        add     fp, sp, #4      ; 0x4
    8504:       e24dd020        sub     sp, sp, #32     ; 0x20
    8508:       e3e03000        mvn     r3, #0  ; 0x0
    850c:       e50b3008        str     r3, [fp, #-8]
    8510:       e1a0300d        mov     r3, sp
    8514:       e50b3010        str     r3, [fp, #-16]
    8518:       e3083600        movw    r3, #34304      ; 0x8600
    851c:       e3403000        movt    r3, #0  ; 0x0
    8520:       e51b2010        ldr     r2, [fp, #-16]
    8524:       e1a00003        mov     r0, r3
    8528:       e3081688        movw    r1, #34440      ; 0x8688
    852c:       e3401000        movt    r1, #0  ; 0x0
    8530:       ebffff84        bl      8348 <_init+0x24>
    8534:       e1a0300b        mov     r3, fp
    8538:       e50b300c        str     r3, [fp, #-12]
    853c:       e3083618        movw    r3, #34328      ; 0x8618
    8540:       e3403000        movt    r3, #0  ; 0x0
    8544:       e51b200c        ldr     r2, [fp, #-12]
    8548:       e1a00003        mov     r0, r3
    854c:       e3081688        movw    r1, #34440      ; 0x8688
    8550:       e3401000        movt    r1, #0  ; 0x0
    8554:       ebffff7b        bl      8348 <_init+0x24>
    8558:       e24b3024        sub     r3, fp, #36     ; 0x24
    855c:       e1a00003        mov     r0, r3
    8560:       e3a01005        mov     r1, #5  ; 0x5
    8564:       ebffffa2        bl      83f4 <backtrace_test3>
    8568:       e1a03000        mov     r3, r0
    856c:       e50b3008        str     r3, [fp, #-8]
    8570:       e24bd004        sub     sp, fp, #4      ; 0x4
    8574:       e8bd8800        pop     {fp, pc}

00008578 <backtrace_test1>:
    8578:       e92d4800        push    {fp, lr}
    857c:       e28db004        add     fp, sp, #4      ; 0x4
    8580:       e24dd008        sub     sp, sp, #8      ; 0x8
    8584:       e1a0300d        mov     r3, sp
    8588:       e50b300c        str     r3, [fp, #-12]
    858c:       e3083600        movw    r3, #34304      ; 0x8600
    8590:       e3403000        movt    r3, #0  ; 0x0
    8594:       e51b200c        ldr     r2, [fp, #-12]
    8598:       e1a00003        mov     r0, r3
    859c:       e3081678        movw    r1, #34424      ; 0x8678
    85a0:       e3401000        movt    r1, #0  ; 0x0
    85a4:       ebffff67        bl      8348 <_init+0x24>
    85a8:       e1a0300b        mov     r3, fp
    85ac:       e50b3008        str     r3, [fp, #-8]
    85b0:       e3083618        movw    r3, #34328      ; 0x8618
    85b4:       e3403000        movt    r3, #0  ; 0x0
    85b8:       e51b2008        ldr     r2, [fp, #-8]
    85bc:       e1a00003        mov     r0, r3
    85c0:       e3081678        movw    r1, #34424      ; 0x8678
    85c4:       e3401000        movt    r1, #0  ; 0x0
    85c8:       ebffff5e        bl      8348 <_init+0x24>
    85cc:       ebffffca        bl      84fc <backtrace_test2>
    85d0:       e24bd004        sub     sp, fp, #4      ; 0x4
    85d4:       e8bd8800        pop     {fp, pc}

000085d8 <main>:
    85d8:       e92d4800        push    {fp, lr}
    85dc:       e28db004        add     fp, sp, #4      ; 0x4
    85e0:       ebffffe4        bl      8578 <backtrace_test1>
    85e4:       e3a03000        mov     r3, #0  ; 0x0
    85e8:       e1a00003        mov     r0, r3
    85ec:       e8bd8800        pop     {fp, pc}

backtrace_test3 为例简要分析汇编代码:

000083f4 <backtrace_test3>:
    push    {fp, lr}        ; 将 fp, lr 压栈,相当于 *(--sp) = lr; *(--sp) = fp; 
    add     fp, sp, #4      ; 设置帧指针(FP)地址,FP是本函数调用栈的局部基指针;
    sub     sp, sp, #24     ; 设置堆栈指针(SP)地址,空出足够的局部变量空间;
    str     r0, [fp, #-24]  ; 函数的参数相当于一个局部变量,相当于初始化 buf;
    ... ...
    mov     r0, r3          ; r0 为函数返回值,本行作用为设置函数返回值;
    sub     sp, fp, #4      ; 恢复 sp 指针到上一次值;
    pop     {fp, pc}        ; 弹栈,相当于*(sp++) = pc; *(sp++) = fp;

测试打印与说明

-backtrace_test1:sp-----0xbe886e08
-backtrace_test1:fp-----0xbe886e14
-backtrace_test2:sp-----0xbe886de0
-backtrace_test2:fp-----0xbe886e04
-backtrace_test3:sp-----0xbe886dc0
-backtrace_test3:fp-----0xbe886ddc
--lr-----0x    8458
--r4-----0x40057ed0

0xbe886dc0----0x       5    ; backtrace_test3:sp   arg size
0xbe886dc4----0xbe886de0    ;                      arg **buf
0xbe886dc8----0xbe886dc0    ;                      arg *sp
0xbe886dcc----0xbe886ddc    ;                      arg *fp
0xbe886dd0----0xbe886dd0    ;                      arg *p
0xbe886dd4----0x       5    ;                      arg i
0xbe886dd8----0xbe886e04    ;                      backtrace_test2:fp
0xbe886ddc----0x    8568    ; backtrace_test3:fp   lr back to test1       push    {fp, lr}
0xbe886de0----0xbe886e14    ; backtrace_test2:sp   arg *array[0]
0xbe886de4----0x4002b250    ;                      arg *array[1]
0xbe886de8----0x    8618    ;                      arg *array[2]
0xbe886dec----0xbe886dfc    ;                      arg *array[3]
0xbe886df0----0xbe886e14    ;                      arg *array[4]
0xbe886df4----0xbe886de0    ;                      arg *sp
0xbe886df8----0xbe886e04    ;                      arg *fp
0xbe886dfc----0xffffffff    ;                      arg ret
0xbe886e00----0xbe886e14    ;                      backtrace_test1:fp
0xbe886e04----0x    85d0    ; backtrace_test2:fp   lr back to test1      push    {fp, lr}
0xbe886e08----0xbe886e08    ; backtrace_test1:sp   arg *sp
0xbe886e0c----0xbe886e14    ;                      arg *fp
0xbe886e10----0xbe886e1c    ;                      main:fp
0xbe886e14----0x    85e4    ; backtrace_test1:fp   lr back to main       push    {fp, lr}
0xbe886e18----0x       0
0xbe886e1c----0x4004a9c4
0xbe886e20----0x       0
0xbe886e24----0x       0
0xbe886e28----0x       0

从上面的栈信息我们看可以看到,lr 保存的值为,本次函数返回后将继续执行的指令起始地址。

带 -O2 优化选项测试

反汇编结果及简要说明

编译和反汇编命令:

arm-linux-uclibcgnueabi-gcc backtrace_test.c -O2 -o backtrace_test
arm-linux-uclibcgnueabi-objdump -d backtrace_test

汇编代码:

000083f4 <backtrace_test3>:
    83f4:       e92d4070        push    {r4, r5, r6, lr}
    83f8:       e1a06001        mov     r6, r1
    83fc:       e1a0500d        mov     r5, sp
    8400:       e59f105c        ldr     r1, [pc, #92]   ; 8464 <backtrace_test3+0x70>
    8404:       e1a02005        mov     r2, r5
    8408:       e59f0058        ldr     r0, [pc, #88]   ; 8468 <backtrace_test3+0x74>
    840c:       ebffffcd        bl      8348 <_init+0x24>
    8410:       e1a0200b        mov     r2, fp
    8414:       e59f1048        ldr     r1, [pc, #72]   ; 8464 <backtrace_test3+0x70>
    8418:       e59f004c        ldr     r0, [pc, #76]   ; 846c <backtrace_test3+0x78>
    841c:       ebffffc9        bl      8348 <_init+0x24>
    8420:       e1a0100e        mov     r1, lr
    8424:       e59f0044        ldr     r0, [pc, #68]   ; 8470 <backtrace_test3+0x7c>
    8428:       ebffffc6        bl      8348 <_init+0x24>
    842c:       e1a01004        mov     r1, r4
    8430:       e59f003c        ldr     r0, [pc, #60]   ; 8474 <backtrace_test3+0x80>
    8434:       e1a04005        mov     r4, r5
    8438:       ebffffc2        bl      8348 <_init+0x24>
    843c:       e28550c0        add     r5, r5, #192    ; 0xc0
    8440:       e1a01004        mov     r1, r4
    8444:       e5942000        ldr     r2, [r4]
    8448:       e59f0028        ldr     r0, [pc, #40]   ; 8478 <backtrace_test3+0x84>
    844c:       e2844004        add     r4, r4, #4      ; 0x4
    8450:       ebffffbc        bl      8348 <_init+0x24>
    8454:       e1540005        cmp     r4, r5
    8458:       1afffff8        bne     8440 <backtrace_test3+0x4c>
    845c:       e1a00006        mov     r0, r6
    8460:       e8bd8070        pop     {r4, r5, r6, pc}
    8464:       00008524        .word   0x00008524
    8468:       00008554        .word   0x00008554
    846c:       00008568        .word   0x00008568
    8470:       0000857c        .word   0x0000857c
    8474:       0000858c        .word   0x0000858c
    8478:       000085a0        .word   0x000085a0

0000847c <backtrace_test2>:
    847c:       e92d4010        push    {r4, lr}
    8480:       e24dd018        sub     sp, sp, #24     ; 0x18
    8484:       e1a0200d        mov     r2, sp
    8488:       e59f4034        ldr     r4, [pc, #52]   ; 84c4 <backtrace_test2+0x48>
    848c:       e3080554        movw    r0, #34132      ; 0x8554
    8490:       e3400000        movt    r0, #0  ; 0x0
    8494:       e1a01004        mov     r1, r4
    8498:       ebffffaa        bl      8348 <_init+0x24>
    849c:       e1a0200b        mov     r2, fp
    84a0:       e3080568        movw    r0, #34152      ; 0x8568
    84a4:       e1a01004        mov     r1, r4
    84a8:       e3400000        movt    r0, #0  ; 0x0
    84ac:       ebffffa5        bl      8348 <_init+0x24>
    84b0:       e28d0004        add     r0, sp, #4      ; 0x4
    84b4:       e3a01005        mov     r1, #5  ; 0x5
    84b8:       ebffffcd        bl      83f4 <backtrace_test3>
    84bc:       e28dd018        add     sp, sp, #24     ; 0x18
    84c0:       e8bd8010        pop     {r4, pc}
    84c4:       00008534        .word   0x00008534

000084c8 <backtrace_test1>:
    84c8:       e92d4010        push    {r4, lr}
    84cc:       e1a0200d        mov     r2, sp
    84d0:       e59f4028        ldr     r4, [pc, #40]   ; 8500 <backtrace_test1+0x38>
    84d4:       e3080554        movw    r0, #34132      ; 0x8554
    84d8:       e3400000        movt    r0, #0  ; 0x0
    84dc:       e1a01004        mov     r1, r4
    84e0:       ebffff98        bl      8348 <_init+0x24>
    84e4:       e1a0200b        mov     r2, fp
    84e8:       e3080568        movw    r0, #34152      ; 0x8568
    84ec:       e1a01004        mov     r1, r4
    84f0:       e3400000        movt    r0, #0  ; 0x0
    84f4:       ebffff93        bl      8348 <_init+0x24>
    84f8:       e8bd4010        pop     {r4, lr}
    84fc:       eaffffde        b       847c <backtrace_test2>
    8500:       00008544        .word   0x00008544

00008504 <main>:
    8504:       e92d4010        push    {r4, lr}
    8508:       ebffffee        bl      84c8 <backtrace_test1>
    850c:       e3a00000        mov     r0, #0  ; 0x0
    8510:       e8bd8010        pop     {r4, pc}

测试打印与说明

-backtrace_test1:sp-----0xbe87ce10
-backtrace_test1:fp-----0x       0
-backtrace_test2:sp-----0xbe87cdf8
-backtrace_test2:fp-----0x       0
-backtrace_test3:sp-----0xbe87cde8
-backtrace_test3:fp-----0x       0
--lr-----0x    8420
--r4-----0x    8534

0xbe87cde8----0x    8534    ; -backtrace_test3:sp   reg r4 backup for test2
0xbe87cdec----0xbe87cea4    ;                       reg r5 backup for test2
0xbe87cdf0----0x       1    ;                       reg r6 backup for test2
0xbe87cdf4----0x    84bc    ;                       reg lr back to test2
0xbe87cdf8----0x       0    ; -backtrace_test2:sp   for 8 bytes align padding
0xbe87cdfc----0x    84f8    ;                       arg arrary[0]
0xbe87ce00----0x    8568    ;                       arg arrary[1]
0xbe87ce04----0x    8544    ;                       arg arrary[2]
0xbe87ce08----0x       0    ;                       arg arrary[3]
0xbe87ce0c----0xbe87ce10    ;                       arg arrary[4]
0xbe87ce10----0x40057ed0    ; -backtrace_test1:sp   reg r4 backup for main
0xbe87ce14----0x    850c    ;                       reg lr back to main
0xbe87ce18----0x40057ed0
0xbe87ce1c----0x4004a9c4
0xbe87ce20----0x       0

优化后的代码,不使用 fp 寄存器,指令也更加紧凑,很多局部变量都直接使用寄存器。在函数的调用过程中,如果要对默写寄存器进行修改,则在进入函数的时候将这些寄存器压栈,函数返回时弹栈。

sfoolish

About

sfoolish

爱生活,爱代码,爱折腾。。。