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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM