arm裸机学习预备知识五:解析C程序内部机制

注:以下内容学习于韦东山老师arm裸机第一期视频教程

以JZ2440点灯的C程序为例来解析C程序的内部机制

    一.JZ2440点灯C程序

        1.1 启动代码start.S   

        .text
        .global _start

        _start:
            ldr sp, =4096

            /* main只需要一个参数,通过r0传递 */
            mov r0, #4
            bl main

            mov r0,#5
            bl main

        halt:
            b halt


        1.2 main函数

                
        int main(int which)
        {
            unsigned int *gpfcon = (unsigned int *)0x56000050;
            unsigned int *gpfdat = (unsigned int *)0x56000054;
            if (which == 4)
                *gpfcon = 0x100;                
            else if (which == 5)
                *gpfcon = 0x400;
            
            *gpfdat = 0x0;

            return 0;
        }

        1.3 Makefile

        all:
            arm-linux-gcc -c -o main.o main.c
            arm-linux-gcc -c -o led_on.o start.S
            arm-linux-ld -Ttext 0 led_on.o main.o -o led_on.elf
            arm-linux-objcopy -O  binary -S led_on.elf led_on.bin
            arm-linux-objdump -D led_on.elf > led_on.dis
            
        clean:
            rm *.o *.elf *.bin

    二.分析C程序内部机制

        2.1 问题1: 为什么要设置栈?

                    因为C函数要使用

                    栈要指向一块可读可写的内存,并且不与代码冲突

              问题2:栈的作用?

                    a. 保存局部变量

                    b. 保存lr等寄存器(加入在main函数中调用函数,返回地址要保存在lr寄存器中,会将之前的lr寄存器覆盖,因此要保存之前的Lr寄存器)

                    c. 保存调用者给被调用者传递的参数(通过r0-r3寄存器传递)

                    d. 保存被调用者的返回值给调用者(通过r0-r3寄存器传递)

                    e. 在函数中r4-r11可能被使用,因此需要在函数的入口保存他们,在函数的出口恢复他们。

            

        2.1 分析反汇编文件led_on.dis

            nand启动时会将nand的前4K拷贝到内存中,因此下面的代码会在内存的前4K中

            
            
            led_on.elf:     file format elf32-littlearm

            Disassembly of section .text:

            00000000 <_start>:
                                /* 设置栈,sp = 4096,在内存的顶部,我们的代码很小,在0地址处,与栈不会冲突 */
               0:    e3a0da01     mov    sp, #4096    ; 0x1000
                                /* r0 = 4 */
               4:    e3a00004     mov    r0, #4    ; 0x4
                                /* 调用main函数 */
               8:    eb000002     bl    18 <main>
                                /* r0 = 5 */
               c:    e3a00005     mov    r0, #5    ; 0x5
                                /* 调用main函数 */
              10:    eb000000     bl    18 <main>

            00000014 <halt>:
              14:    eafffffe     b    14 <halt>

            00000018 <main>:
                                /* ip = sp */
              18:    e1a0c00d     mov    ip, sp
                                /* 将fp,ip,lr,pc寄存器的值存放到sp处,同时更新sp的值
                                 * 寄存器高标号放在高地址 */
                                 * pc = 10 + 8 = 0x18 */
                                 * 因此         PC = 0x18   存放在4092~4095
                                 * 然后再次减  lr = 8      存放在4088~4091
                                 * 然后再次减  ip = 4096   存放在4084~4097
                                 * 然后再次减  fp = 未定义 存放在4080~4083
                                 * 最后更新sp的值 sp = 4080
                                 */
              1c:    e92dd800     stmdb    sp!, {fp, ip, lr, pc}
                                /* fp = ip - 4 = 4092 */
              20:    e24cb004     sub    fp, ip, #4    ; 0x4
                                /* sp = sp - 12 */
                                /* 12用来存放局部变量 */
              24:    e24dd00c     sub    sp, sp, #12    ; 0xc
                                /* 把r0存放到fp-16=4092-16=4076处 */
              28:    e50b0010     str    r0, [fp, #-16]
                                /* r3 = 0x56000000 */
              2c:    e3a03456     mov    r3, #1442840576    ; 0x56000000
                                /* r3 = 0x56000000 + 0x50 = 0x56000050 */
              30:    e2833050     add    r3, r3, #80    ; 0x50
                                /* r3存入fp-20  = 4092 - 20 = 4072地址处 */
                                /* 局部变量gpfcon保存在了栈中 */
              34:    e50b3014     str    r3, [fp, #-20]
                                /* r3 = 0x56000000 */
              38:    e3a03456     mov    r3, #1442840576    ; 0x56000000
                                /* r3 = r3 + 0x54 = 0x56000054 */
              3c:    e2833054     add    r3, r3, #84    ; 0x54
                                /* r3存入fp-24  = 4092 - 24 = 4068地址处 */
                                /* 局部变量gpfdat保存在了栈中 */
              40:    e50b3018     str    r3, [fp, #-24]
                                /* 从sp-16 = 4076的地址取值,即寄存器r0存放的参数值,存放到r3寄存器 */
              44:    e51b3010     ldr    r3, [fp, #-16]
                                /* 比较r3与4 */
              48:    e3530004     cmp    r3, #4    ; 0x4
                                /* 执行完调到60地址 */
              4c:    1a000003     bne    60 <main+0x48>
                                /* r3 == 4时执行下面的代码 */
                                /* 从fp-20=4072的地址取值,得到了gpfcon变量的值,也就是0x56000050这个地址 */
              50:    e51b2014     ldr    r2, [fp, #-20]
                                /* r3 = 0x100 */
              54:    e3a03c01     mov    r3, #256    ; 0x100
                                /* 将0x100写入0x56000050这个地址 */
              58:    e5823000     str    r3, [r2]
              5c:    ea000005     b    78 <main+0x60>
                                /* 从sp-16 = 4076的地址取值,即寄存器r0存放的参数值,存放到r3寄存器 */
              60:    e51b3010     ldr    r3, [fp, #-16]
                                /* 比较r3与4 */
              64:    e3530005     cmp    r3, #5    ; 0x5
                                /* 执行完调到78地址 */
              68:    1a000002     bne    78 <main+0x60>
                                /* r3 == 5时执行下面的代码 */
                                /* 从fp-20=4072的地址取值,得到了gpfcon变量的值,也就是0x56000050这个地址 */
              6c:    e51b2014     ldr    r2, [fp, #-20]
                                /* r3 = 0x400 */
              70:    e3a03b01     mov    r3, #1024    ; 0x400
                                /* 将0x400写入0x56000050这个地址 */
              74:    e5823000     str    r3, [r2]
                                /* 从4068的地址取值放到r3寄存器,得到了gpfdat的值,也就是0x560000054这个地址 */
              78:    e51b3018     ldr    r3, [fp, #-24]
                                /* r2 = 0 */
              7c:    e3a02000     mov    r2, #0    ; 0x0
                                /* 将0写入0x560000054这个地址 */
              80:    e5832000     str    r2, [r3]
                                /* r3 = 0 */
              84:    e3a03000     mov    r3, #0    ; 0x0
                                /* r0 = r3,r0保存返回值0 */
              88:    e1a00003     mov    r0, r3
                                /* sp = fp - 12 = 4092 - 12 = 4080 */
                                /* 释放了局部变量,恢复栈 */
              8c:    e24bd00c     sub    sp, fp, #12    ; 0xc
                                /*
                                 * 从栈中恢复寄存器,将sp的值加载到多个寄存器中
                                 *  因此            fp存放4080~4083的值,等于原来保存的fp
                                 *  然后再次增加    sp存放4084~4087的值,等于原来保存的ip,sp = 4096(程序刚开始ip = sp,保存了栈的值)
                                 *    然后再次增加    pc存放4088~4091的值,等于原来保存的lr = 8
                                 *                    pc = lr = 8,因此程序就会返回0x8的地址
                                 *    最后再次增加    sp' = sp + 4 = 4096 ,由于sp后面无!号,sp修改后的地址并不存入sp中
                                 */
              90:    e89da800     ldmia    sp, {fp, sp, pc}
            Disassembly of section .comment:

            /* comment表示注释 */
            00000000 <.comment>:
               0:    43434700     cmpmi    r3, #0    ; 0x0
               4:    4728203a     undefined
               8:    2029554e     eorcs    r5, r9, lr, asr #10
               c:    2e342e33     mrccs    14, 1, r2, cr4, cr3, {1}
              10:    Address 0x10 is out of bounds.




    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值