單片機
返回首頁

S3c2440代碼重定位詳解5---代碼重定位與位置無關碼

2021-09-10 來源:eefocus

一個程序,由代碼段、只讀數據段、數據段、bss段等組成。


程序一開始可以燒在Nor Flash上面,運行時代碼段仍可以在Nor Flash運行,但對于數據段,就必須把數據段移到SDRAM中,因為只要在SDRAM里面,數據段的變量才能被寫操作,把程序從一個位置移動到另一個位置,把這個過程就稱為重定位。


先梳理下把整個程序復制到SDRAM需要哪些技術細節:


把程序從Flash復制到運行地址,鏈接腳本中就要指定運行地址(Runtime addr)為SDRAM地址;

編譯鏈接生成的bin文件,需要在SDRAM地址上運行,但上電后卻必須先在0地址運行,這就要求重定位之前的代碼與位置無關(是位置無關碼寫成);

參考Uboot修改鏈接腳本:

SECTIONS

{

    . = 0x30000000;


    . = ALIGN(4);

    .text      :

    {

      *(.text)

    }


    . = ALIGN(4);

    .rodata : { *(.rodata) }


    . = ALIGN(4);

    .data : { *(.data) }


    . = ALIGN(4);

    __bss_start = .;

    .bss : { *(.bss) *(.COMMON) }

    _end = .;

}


現在我們寫的這個鏈接腳本,稱為一體式鏈接腳本,對比前面的分體式鏈接腳本區別在于代碼段和數據段的存放位置是否是分開的。


例如現在的一體式鏈接腳本的代碼段后面依次就是只讀數據段、數據段、bss段,都是連續在一起的。


分體式鏈接腳本則是代碼段、只讀數據段,中間相關很遠之后才是數據段、bss段。

我們以后的代碼更多的采用一體式鏈接腳本,原因如下:


分體式鏈接腳本適合單片機,單片機自帶有flash,不需要再將代碼復制到內存占用空間。而我們的嵌入式系統內存非常大,沒必要節省這點空間,并且有些嵌入式系統沒有Nor Flash等可以直接運行代碼的Flash,就需要從Nand Flash或者SD卡復制整個代碼到內存;

JTAG等調試器一般只支持一體式鏈接腳本;

修改start.S段


    /* 重定位text, rodata, data段整個程序 */

    mov r1, #0

    ldr r2, =_start         /* 第1條指令運行時的地址 */

    ldr r3, =__bss_start    /* bss段的起始地址 */


cpy:

    ldr r4, [r1]

    str r4, [r2]

    add r1, r1, #4

    add r2, r2, #4

    cmp r2, r3

    ble cpy



    /* 清除BSS段 */

    ldr r1, =__bss_start

    ldr r2, =_end

    mov r3, #0

clean:

    str r3, [r1]

    add r1, r1, #4

    cmp r1, r2

    ble clean


    bl main  


halt:

    b halt


將修改后的代碼重新編譯燒寫在Nor Flash上,上電運行。

對本代碼的啟動情況進行分析:

在這里插入圖片描述

在生成的bin文件里,代碼保存的位置是0x30000000。隨后燒寫到NOR Flash的0地址,但代碼的結構沒有變化。之后再重定位到SDRAM


查看反匯編:


3000005c:   eb000106    bl  30000478  


30000060:   e3a01000    mov r1, #0  ; 0x0

30000064:   e59f204c    ldr r2, [pc, #76]   ; 300000b8 <.text+0xb8>

30000068:   e59f304c    ldr r3, [pc, #76]   ; 300000bc <.text+0xbc>


這里的bl 30000478不是跳轉到30000478,這個時候sdram并未初始化;

為了驗證,我們做另一個實驗,修改連接腳本sdram.lds, 鏈接地址改為0x32000478,編譯,查看反匯編:


3000005c:   eb000106    bl  30000478  


30000060:   e3a01000    mov r1, #0  ; 0x0

30000064:   e59f204c    ldr r2, [pc, #76]   ; 300000b8 <.text+0xb8>

30000068:   e59f304c    ldr r3, [pc, #76]   ; 300000bc <.text+0xbc>


可以看到現在變成了bl 30000478,但兩個的機器碼eb000106都是一樣的,機器碼一樣,執行的內容肯定都是一樣的。

因此這里并不是跳轉到顯示的地址,而是跳轉到: pc + offset,這個由鏈接器決定。


假設程序從0x30000000執行,當前指令地址:0x3000005c ,那么就是跳到0x30000478;如果程序從0運行,當前指令地址:0x5c 調到:0x00000478


跳轉到某個地址并不是由bl指令所決定,而是由當前pc值決定。反匯編顯示這個值只是為了方便讀代碼。


重點:

反匯編文件里, B或BL 某個值,只是起到方便查看的作用,并不是真的跳轉。


怎么寫位置無關碼?


1、使用相對跳轉命令 b或bl;

2、重定位之前,不可使用絕對地址,不可訪問全局變量/靜態變量,也不可訪問有初始值的數組(因為初始值放在rodata里,使用絕對地址來訪問);

3、重定位之后,使用ldr pc = xxx,跳轉到/runtime地址;


寫位置無關碼,其實就是不使用絕對地址,判斷有沒有使用絕對地址,除了前面的幾個規則,最根本的辦法看反匯編。


因此,前面的例子程序使用bl命令相對跳轉,程序仍在NOR/sram執行,要想讓main函數在SDRAM執行,需要修改代碼


 //bl main  /*bl相對跳轉,程序仍在NOR/sram執行*/

 ldr pc, =main/*絕對跳轉,跳到SDRAM*/

進入單片機查看更多內容>>
相關視頻
  • TI 新一代 C2000? 微控制器:全方位助力伺服及馬達驅動應用

  • MSP430電容觸摸技術 - 防水Demo演示

  • 直播回放: Microchip Timberwolf? 音頻處理器在線研討會

  • 新唐 8051單片機教程

  • 基于靈動MM32W0系列MCU的指夾血氧儀控制及OTA升級應用方案分享

  • 基于靈動MM32SPIN系列MCU的無感FOC便攜冰箱應用方案分享

    相關電子頭條文章
萝卜大香蕉