嵌入式linux编程arm初步接触之存储控制中的程序运行时动态加载

2019-07-13 09:15发布

        韦东山老师的开发板在讲解存储管理技术的时候大致意思如下,片内ram只有4K,可执行程序大小超过4K怎么办,在这里韦东山老师的办法是在可执行代码的首部,运行自身拷贝,把自己拷贝到内存地址足够存放存放程序的地址处,然后直接跳转过去执行,这种技术在stm32里面叫做程序自举,这种技术也叫作程序动态加载,既可以用于软件自举,操作系统引导加载,还可以用于嵌入式系统不停机运行升级。
       这种技术本身跟操作系统内核引导加载是同一种技术,linux下我们使用uBoot加载操引导操作系统内核,内核再引导加载整个操作系统,都是用了这种技术。韦东山老师使用汇编编写的引导加载代码,并且在链接的时候指定了加载地址。我决定用C语言实现,汇编代码做尽可能少的工作。实在是汇编代码编写难度太大,只能回到熟悉的C了,用另一种思路实现,也算是对自己学习的考验吧。闲话休提,总计使用了两个C文件一个汇编文件,上代码。 //这个是systm.c文件,用于初始化硬件 #define WATCH_DOG (*(volatile unsigned long *)0x53000000) #define SDRAM_BASE (volatile unsigned long *)0x30000000 #define MEM_CTL_BASE (volatile unsigned long *)0x48000000 const unsigned long memcfgval[]={ 0x22011110, 0x00000700, 0x00000700, 0x00000700, 0x00000700, 0x00000700, 0x00000700, 0x00018005, 0x00018005, 0x008C07A3, 0x000000B1, 0x00000030, 0x00000030 }; void disable_watch_dog(void) //禁用看门狗 { WATCH_DOG = 0; } void mem_setup(void) //配置内存存储设置 { int i; volatile unsigned long* ptr; ptr = MEM_CTL_BASE; for(i = 0; i < sizeof(memcfgval)/sizeof(memcfgval[0]); i++) { *ptr = memcfgval[i]; ptr++; } } void executable_code_jmp(void) //内存拷贝,实现自身从0地址拷贝到指定地址 { int i; volatile unsigned long* src; volatile unsigned long* dest; src=0; dest = SDRAM_BASE; for(i = 0; i < 1024; i++) { *dest = *src; dest++; src++; } } 这个是汇编启动代码文件statrup.S,用于设置C使用环境 .text .global _start _start: ldr sp,=1<<12 bl disable_watch_dog bl mem_setup bl executable_code_jmp ldr sp,=0x34000000 ldr pc,=(0x30000000 + main) halt_loop: b halt_loop 这个是main.c文件,常规C程序,使用韦东山的点灯 #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) #define GPF4_out (1<<(4*2)) #define GPF5_out (1<<(5*2)) #define GPF6_out (1<<(6*2)) extern void disable_watch_dog(void); extern void mem_setup(void); extern void executable_code_jmp(void); void wait(volatile unsigned long dly) { for(; dly > 0; dly--); } int main(void) { unsigned long i = 0; GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1,2,4对应的GPF4/5/6三个引脚设为输出 while(1){ wait(30000); GPFDAT = (~(i<<4)); // 根据i的值,点亮LED1,2,4 if(++i == 8) i = 0; } return 0; } 一下是makefile文件 led.bin:startup.S systm.c main.c arm-linux-gcc -c -o startup.o startup.S arm-linux-gcc -c -o systm.o systm.c arm-linux-gcc -c -o main.o main.c arm-linux-ld -Ttext 0x00000000 -g startup.o systm.o main.o -o led_elf arm-linux-objcopy -O binary -S led_elf led.bin arm-linux-objdump -D -m arm led_elf > led.dis 汇编启动代码还是按照常规,关键代码在bl executable_code_jmp           
  ldr sp,=0x34000000
  ldr pc,=(0x30000000 + main)
executable_code_jmp 实现了代码自动拷贝到内存指定地址处,然后设置堆栈,直接跳转main函数入口地址+0x30000000处。反汇编结果如下所示
   c:    eb000024     bl    a4
  10:    e3a0d30d     mov    sp, #872415232    ; 0x34000000
  14:    e59ff000     ldr    pc, [pc, #0]    ; 1c <.text+0x1c>00000018 :
  18:    eafffffe     b    18
  1c:    30000154     andcc    r0, r0, r4, asr r1
        然后下载运行,跟韦东山的代码执行效果一模一样。在这里我使用  ldr pc,=(0x30000000 + main)语句跳转到main函数地址,韦东山的代码通过链接时候指定偏移地址0x30000000,然后bl main实现。可见,只要思路行得通,软件的实现方法是多种多样的。