Cortex-M3 动态加载二(RWPI数据无关实现)

上一篇关于动态加载讲述的是M3下面的ropi的实现细节,这一篇则讲述RW段的实现细节以及系统加载RW段的思路,我在M3上根据这个思路可以实现elf的动态加载,当然进一步的可以优化很多东西,还可以研究将bin加载起来,这个需要一些辅助的东西实现。

言归正文,使用/acps/rwpi编译代码,解决RW段即全局变量的加载。

首先编译的时候会为每一个全局变量生成一个相对于r9寄存器的偏移量,这个偏移量会在.text段中。

如下例子:

1 static int elf_test_num = 1;

2 int elf_test_num2 = 12;

3 int main(void)

4 {       

5     elf_test_num = 2;

6     elf_test_num2 = 4; 

7     for(;;);

8 }

编译:

armcc  -c --cpu Cortex-M3 -O0 --apcs=interwork --apcs /ropi/rwpi -o main.o main.c

使用fromelf查看汇编代码

fromelf.exe -s -c main.o

生成的汇编代码如下(Cortex-M3):

 1 $t

 2 .text

 3 SystemInit

 4     0x00000000:    4770      BX       lr

 5 main

 6     0x00000002:    2002      MOVS     r0,#2

 7     0x00000004:    4904      LDR      r1,[pc,#16] ; [0x18] = 0

 8     0x00000006:    4449      ADD      r1,r1,r9

 9     0x00000008:    6008      STR      r0,[r1,#0]

10     0x0000000a:    2004      MOVS     r0,#4

11     0x0000000c:    4903      LDR      r1,[pc,#12] ; [0x1c] = 0

12     0x0000000e:    4449      ADD      r1,r1,r9

13     0x00000010:    6008      STR      r0,[r1,#0]

14     0x00000012:    bf00      NOP

15     0x00000014:    e7fe      B        {pc} ; 0x14

16 $d

17     0x00000016:    0000        ..      DCW    0

18     0x00000018:    00000000    ....    DCD    0

19     0x0000001c:    00000000    ....    DCD    0

在编译阶段相对r9偏移量还都是零,要到链接阶段才确定相对r9偏移量的大小,链接之后如下:

armlink.exe --cpu Cortex-M3 --ropi --ro_base 0 --rwpi --rw_base 0x0 --entry=main --no_startup main.o -o main.elf
使用fromelf查看汇编代码
fromelf.exe -s -c main.elf

查看最终的elf文件汇编如下:

 1 $t

 2     .text

 3 SystemInit

 4         0x00000000:    4770       BX       lr

 5 main

 6         0x00000002:    2002       MOVS     r0,#2

 7         0x00000004:    4904       LDR      r1,[pc,#16] ; [0x18] = 0x4

 8         0x00000006:    4449       ADD      r1,r1,r9

 9         0x00000008:    6008       STR      r0,[r1,#0]

10         0x0000000a:    2004       MOVS     r0,#4

11         0x0000000c:    4903       LDR      r1,[pc,#12] ; [0x1c] = 0x8

12         0x0000000e:    4449       ADD      r1,r1,r9

13         0x00000010:    6008       STR      r0,[r1,#0]

14         0x00000012:    bf00       NOP

15         0x00000014:    e7fe       B        0x14 ; main + 18

16 $d

17         0x00000016:    0000        ..      DCW    0

18         0x00000018:    00000004    ....    DCD    4

19         0x0000001c:    00000008    ....    DCD    8

此时$d对应的偏移量均已确定大小。

取出对应一句C的汇编代码如下:

1 elf_test_num = 2;

2 

3 0x00000002:    2002       MOVS     r0,#2

4 0x00000004:    4904       LDR      r1,[pc,#16] ; [0x18] = 0

5 0x00000006:    4449       ADD      r1,r1,r9

6 0x00000008:    6008       STR      r0,[r1,#0]

详细解释如下:

  1、MOVS     r0,#2

    即r0 = 2。

  2、LDR      r1,[pc,#16] ; [0x18] = 0

    即r1 = *(pc + 16)。这里实现了RW无关性,相对当前PC值取出偏移量所在的地址即pc,#16 = 0x18,再从0x18地址出取出偏移量的大小即[pc,#16] = 0x04,从上面加黑的位置查看0x00000018地址的值即为0x00000004,存放到r1寄存器。(这里的pc值应该是下一指令的pc值,并且应该是对齐32位的,具体赢查看arm指令手册。)

  3、ADD      r1,r1,r9

    即r1 = r1+r9,所以指定了在r9偏移0x00000004的地址处给到r1。

  4、STR      r0,[r1,#0]

    即*(r1 + 0) = r0,即将r0赋给r1指向的地址处,此时r1即是偏移r9基址4的地方。

综上所述,在加载elf阶段,将RW段加载到RAM当中之后,需要将r9寄存器指向此片内存的基地址,然后接下来就可以跳转到加载的elf的代码中去执行,就可以实现全局变量的加载了。具体实现思路可以如下:

 1 __global_reg(6) char *sb;   //在C中使用r9寄存器(static base register)的方法

 2 

 3 char rw_buf[100];            //rw段的加载地址,也可以让系统动态分配一段内存地址

 4 

 5 char *saved_sb;           //保存r9

 6 

 7 void load_fun(void)

 8 

 9 {

10 

11     saved_sb = sb;     //先保存r9的值

12 

13     sb = rw_buf;      //将r9指向rw段的加载地址

14 

15     entry();          //跳转执行到具体的一个elf的入口执行

16 

17     sb = saved_sb;    //从elf程序跳转回来赋回原来r9的值

18 

19 }

 

你可能感兴趣的:(数据)