位置無關碼


ARM下的位置無關和相關碼

為什么需要位置無關碼?

  見 :

  U-BOOT詳解(什么是《編譯地址》?什么是《運行地址》?)  http://bbs.21ic.com/forum.php?mod=viewthread&tid=857037&typeid=114 

     ARM位置無關代碼設計規范  http://wenku.baidu.com/view/5ef25b890b4c2e3f562763a8.html

位置無關可執行文件PIE包括位置無關代碼PIC和位置無關數據PID兩部分。
通常情況下,將bootloader程序下載到ROM的0x0地址進行啟動(比如固化到NorFlash中)。然而在很多的設計中,比如將bootloader固化在NAND中,在系統復位后S3C2440A中NAND控制器自動讀取NAND中存儲的前4K的代碼到s3c2440a中稱之為
steppingstone的RAM中,steppingstone中的代碼用進行一些非核心的硬件初始化,再將NAND中剩下的bootloader代碼拷貝到RAM中運行。一般境況下兩者的地址並不相同,程序在SDRAM中的地址重定位過程必須由程序員來完成。這樣就有了位置無關代碼的概念,指代碼不在連接時制定的運行地址空間,也可以執行,它一段加載到任意地址空間都能執行的特殊代碼。這樣在steppingstone設計的代碼要用位置無關設計。 

 

位置無關代碼可以用於以下場合:
1. 程序在運行期間動態加載到內存; 
2. 程序在不同場合與不同程序組合后加載到內存(共享的動態鏈接庫);  3. 在運行期間不同地址相互之間的映射(如bootloader)

 

怎么實現位置無關碼?

{

  1. 位置無關的函數跳轉

  2. 位置無關的常量訪問

}

位置無關代碼,即該段代碼無論放在內存的哪個地址,都能正確運行。究其原因,是因為代碼里沒有使用絕對地址,都是相對地址。

而位置相關碼,即它的地址與代碼處於的位置相關,是絕對地址,如:mov PC ,#0xff;ldr  pc,=0xffff等。

如果你的這段代碼需要實現位置無關,那么你就不能使用絕對尋址指令,否則的話就是位置有關了。

 

一、位置無關的寫法:

(1) B指令

 

B指令接受一個相對地址,因此在匯編里用B跳轉到一個標號時,實際編譯的結果是一個相對跳轉。相對地址有個范圍限制,即目標不能太遠,一般目標放在同一個文件里是肯定可以的。 Offset must IN 32Mbit

_start:

    b  _reset

_reset:

      ...

(2) BL

BL用於調用函數,也是一個相對跳轉,same as B instrction

(3) ADR

獲取標號的地址,在編譯時會使用PC+偏移的方式得到該位置的地址。例如,當TEXT_BASE是0時

SMRDATA可能被放在0x100的位置,當TEXT_BASE為0x30000000時放在0x30000100的位置。使用ADR

總能獲取正確的位置,與程序的加載地址無關。

    ADR R0, SMRDATA

SMRDATA:

    .word  0x22111120 

    .word  0x00002F50 

    .word  0x00000700 

(相應的, LDR Rn, =LABEL是位置相關的)

(4) LDR

當加標號時,LDR可以用於偽指令,也可以真指令。

真指令: (標號前不加=號,表示取標號處的值)

    LDR R0,  SDRDATA

實際被編譯為LDR R0, [PC, #NN],其中NN是目標的相對距離

偽指令: (標號前加=號,取標號的地址)

    LDR R0, = SDRDATA

實際編譯的時候的時候,會在某位置存處SDRDATA的值,然后用一個LDR取出來。

顯然,用LDR時,加不加=號有很大區別。

無=號:取該標號處的值,位置無關

有=號:取該標號的地址,位置相關

(5)  bl/b調用的c語言函數里面也不要使用全局變量, 因為c里面的全局變量的地址是根據鏈接地址生成的.

  見例7.

==================================================================================================================

舉例分析

例1:中斷向量跳轉

_start:    

    b       reset

    ldr    pc, _undefined_instruction

    ldr    pc, _software_interrupt

    ldr    pc, _prefetch_abort

    ldr    pc, _data_abort

    ldr    pc, _not_used

    ldr    pc, _irq

    ldr    pc, _fiq

 

_undefined_instruction:    .word undefined_instruction

_software_interrupt:    .word software_interrupt

_prefetch_abort:    .word prefetch_abort

_data_abort:        .word data_abort

_not_used:        .word not_used

_irq:            .word irq

_fiq:            .word fiq

其中,

ldr pc, _irq,由於沒加=號,表示取值_irq處的值放在pc里 (位置無關)

_irq:  .word irq ,表示_irq存放的值是irq的絕對地址(位置有關)

例2:

bl  main ; 位置無關

ldr pc, =main; 把main的地址放在pc,位置相關

例3: 靜態變量

_MAGIC_NUM:

    .word 0x12345678

取值

    LDR  R0, _MAGIC_NUM  ; 位置無關

例4: 存放標號絕對地址(絕對地址是編譯的時候已經固定)

_OS_Running_p:

    .word  OS_Runing

則_OS_Running_p存放的是標號OS_Running的絕對地址

 

例5: 顯式LDR和隱式LDR

以給某C中的變量的g_num賦值為例

(1) 使用偽指令LDR,即為隱式

    LDR  R0, =g_num    @取g_num的地址到R0

    MOV R1, #10

            STR  R1, R0

(2) 顯式賦值

先定義一個變量p_g_num,用於保存g_num的地址

p_g_num:

    .word   g_num   @ g_num的絕對地址

然后賦值

    LDR R0, p_g_num

    MOV R1, #10

    STR R1,  R0

顯然,兩者其實一樣,偽指令被展開后其實就是(2)的樣子。

不同點在於:在多次引用的時候,如果使用偽指令,則會有多個臨時定義。所以,

在多次引用的時候應該使用顯式定義。

 

例6: 使用LinkScript中的變量

這種情形和例5相同

1) LinkScript中定義了兩個位置

{

    __bss_start = .;

    .bss : { *(.bss) }

    _end = .;

}

2) 定義兩個變量,用於存處這兩個位置

.globl _bss_start

_bss_start:

    .word __bss_start

 

.globl _bss_end

_bss_end:

    .word _end

3) 使用這兩個位置

    ldr    r0, _bss_start        /* find start of bss segment        */

   ldr    r1, _bss_end        /* stop here                        */

 

例7. bl/b調用的c語言函數里面也不要使用全局變量

in head.s and *.c :

Reset:                  
    ldr sp, =4096           @ 設置棧指針,以下都是C函數,調用前需要設好棧
    bl  disable_watch_dog   @ 關閉WATCHDOG,否則CPU會不斷重啟
    bl  clock_init          @ 設置MPLL,改變FCLK、HCLK、PCLK
    bl  memsetup            @ 設置存儲控制器以使用SDRAM
    bl  nand_init           @ 初始化NAND Flash
 
        

#define
WTCON (*(volatile unsigned long *)0x53000000) void disable_watch_dog(void) { WTCON = 0; // 關閉WATCHDOG很簡單,往這個寄存器寫0即可 } #define CLKDIVN (*(volatile unsigned long *)0x4c000014) void clock_init(void) { CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1 .. .. } void nand_init(void) { S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000; /* 設置時序 */ s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); .... } 以上函數都沒有使用到全局變量, 一旦使用了全局變量,就不能作為位置無關碼的一部分了. 比如bootloader啟動的第一階段, copy2ram的前面部分.必須是位置無關碼
 
        

 

 

 


免責聲明!

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



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