一個多年的老隱患---ARM BL指令的分析

前兩天在俺們的EVB上加了點東西,增加了一下的BL語句進行初始化:

        BL       C2g_GsmStartZac
        LDR     pc, =INT_Initialize                  ;goto main in RAM

接着就出了一個奇怪的問題,使用RVDS,將PC置於代碼燒錄的其實地址0x40004,然後F5開跑,可以正常開跑。但是如果使用板子自帶的boot進行啓動,則跑不起來。

使用RVDS從boot開始跟蹤,發現原來boot跳到主程序的過程中,跳入的地址爲0x40040004,之所以這麼做主要是爲了和操作flash的代碼進行統一,因爲0-32K的Flash空間在系統運行的過程中被映射爲內部IRAM,如果要操作這段Flash,則需要從0x4000000開始,通過地址繞轉來巧妙的繞過這個問題。系統用了多年沒出問題,其實是沒在這個地方調用過IRAM裏的函數。

本來,如果BL跳轉的地址C2g_GsmStartZac,如果也是在Flash中,則不會有任何問題,反正地址都是繞轉。另外,如果跳轉到地址如果超出了BL的能力範圍32M,編譯器(鏈接器)本來也會想辦法解決這個問題的,即將C2g_GsmStartZac的地址保存在Flash中的某個位置,然後通過LDR把地址加載到寄存器,再通過寄存器跳轉。

出現前面說的問題的關鍵在於,這個地址正好位於0-32k的空間內,而且由於scatter loader文件中的鏈接地址又是0x40000開始的,導致編譯器認爲正好在BL跳轉的32M範圍內,直接編譯成了通過偏移條狀,即BL跳轉是在當前PC的基礎上加上偏移量來跳轉的,因此就有了問題。

通過反彙編知道,使用0x40000進行鏈接,生成的代碼如下:

 

        0x00040060:    fbff0269    i...    BLX      C2g_GsmStartZac  ; 0xa0e

 

根據文檔《ARM Instruction》對BLX的介紹如下:

 

 其中的0-23位爲有符號的偏移,指令fbff0269中對應的爲0-22位爲0x7f0269, 第23爲1,表示負數,即負的0xFD97, 在左移兩位爲負的0x3F65c,加上H<<1, H爲1, 運算結果爲負的0x3F65A. 用PC值0x40060-3f65a=0xA06.和0xa0e差8,正好符合BLX指令下面的註釋。但是當鏈接地址爲0x40000開始,boot跳轉卻是到0x4000000時,此處的PC變成了0x4000060了,運算結果變成了0x4000060-3f65a=3FC0A06,訪問的是繞轉的Flash的內容,當然就有問題了。

 

實際上如果scatter loader寫爲0x4000000則,鏈接器處理的結果是這樣的,使用了臨時變量。

 

    0x40000060:    eb02bc1c    ....    BL       $Ven$AT$L$$C2g_GsmStartZac  ; 0x400af0d8
...........................
    $Ven$AT$L$$C2g_GsmStartZac
    $a
        0x400af0d8:    e59fc000    ....    LDR      r12,0x400af0e0
        0x400af0dc:    e12fff1c    ../.    BX       r12
    $d
    $f
        0x400af0e0:    00000a0f    ....    DCD    2575 ;即0xA0F, thumb

 

 但是由於系統已經做好了,改變scatter loader的地址還要牽扯到MMU的配置,Flash的寫入cache等諸多問題,最後的解決方法就是使用不會產生歧義的調用方法,即直接使用pc跳轉

 

        mov     lr, pc
        LDR     pc, =C2g_GsmStartZac
        LDR     pc, =INT_Initialize                  ;goto main in RAM


這樣在什麼位置跳轉都沒有問題了,不過先得把pc保存到lr中(上面的第一句),不然調用後就回不來了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章