天天看點

加載位址和運作位址關系,程式如何由加載位址跳入運作位址運作

本文轉自 http://blog.csdn.net/ouyang_linux007/article/details/7448505

程式從片内位址0開始,但為什麼連結位址又設0x30000000,那不就從0x30000000開始了,反彙編可以看到不是從0開始的?

韋老大回答:

1. 裸闆程式燒在FLASH上 一上電,肯定從0位址運作

2. 但是,0位址要麼對應NOR FLASH,要麼對應隻有4K的片内記憶體

3. 程式要讀寫資料,或是程式大于4K,怎麼辦?

4. 程式就要複制到SDRAM裡去執行

5. SDRAM那麼大,複制到哪個位址去?能随便選擇位址嗎

6. 不能,要複制到它的連結位址去

7. 為什麼一定要複制到它的連結位址去?

8. 因為這個連結位址是程式運作時“應該位于的地方”,比如要通路某個全局變量時,就是通路這個全局變量的連結位址

9. 既然連結位址是SDRAM的位址,那為什麼一開始程式可以從0位址運作

10. 因為一開始的程式是“位置無關碼”

獨孤君回答:

以下是我自己總結的,看對你有幫助不

加載時域與運作時域:可以這麼了解,加載時域涉及到存儲位址;運作時域涉及到連接配接位址(連接配接位址開始作用的時間是在使用僞指令ldr(adr、adrl)pc,=某符号或是某立即數時)。可執行程式在被下載下傳到相應存儲器件裡時,它的存儲位址可以通過oflash來選擇;而當運作該程式時,一開始時PC寄存器的值是指向存儲空間的起始位址(起始位址由oflash決定)的,但在遇到僞指令ldr(adr、adrl)pc,=某符号或是某立即數(位置無關相對跳轉指令B與BL不影響運作位址)後,程式運作時的絕對位址(即PC寄存器的值)就發生了改變,它是以連接配接位址(由arm-Linux-ld之-T選項設定)為基址,指令所在位置為相對位址共同組成的。總之:運作位址=-T指定的連接配接位址+相對偏移位址;存儲位址=由oflash指定某存儲器件的起始位址+由連結檔案中AT指定的加載位址。

http://blog.csdn.net/ce123_zhouwei/article/details/6990100

加載時位址就是程式放置的位址,運作位址就是程式定位的絕對位址,也即在編譯連接配接時定位的位址。

如果程式是在flash裡運作,則運作位址和加載位址是相同的。

如果程式是在ram裡運作,但程式是存儲在flash裡,則運作位址指向ram,而加載位址是指向flash。

代碼一般是燒寫在NAND裡面,比如S3C2440 如果開機從NAND啟動 其開始的4K代碼會被COPY到2440内部的4KRAM 用于對關鍵硬體的初始化 這時候内部RAM被映射為0x0位址。

如果從NOR啟動,因為NOR支援片上運作,代碼可以直接在NOR上運作 此時NOR便被映射成0x0,S3C2440 内部的4KRAM便被映射到了0x40000000處。

下面我們看看連結檔案。           

    對于.lds檔案,它定義了整個程式編譯之後的連接配接過程,決定了一個可執行程式的各個段的存儲位置。先看一下GNU官方網站上對.lds檔案形式的完整描述:

加載位址和運作位址關系,程式如何由加載位址跳入運作位址運作
SECTIONS {  
    ...  
    secname start BLOCK(align) (NOLOAD) : AT ( ldadr )  
      { contents } >region :phdr =fill  
    ...  
    }       
加載位址和運作位址關系,程式如何由加載位址跳入運作位址運作

secname和contents是必須的,其他的都是可選的。下面看看幾個常用的:

1、secname:段名

2、contents:決定哪些内容放在本段,可以是整個目标檔案,也可以是目标檔案中的某段(代碼段、資料段等)

3、start:本段連接配接(運作)的位址,如果沒有使用AT(ldadr),本段存儲的位址也是start。GNU網站上說start可以用任意一種描述位址的符号來描述。

4、AT(ldadr):定義本段存儲(加載)的位址。

看一個簡單的例子:

/* nand.lds */  
SECTIONS {   
   firtst 0x00000000 : { head.o init.o }   
   second 0x30000000 : AT(4096) { main.o }   
}      

以上,head.o放在0x00000000位址開始處,init.o放在head.o後面,他們的運作位址也是0x00000000,即連接配接和存儲位址相同(沒有AT指定);

main.o放在4096(0x1000,是AT指定的,存儲位址)開始處,但是它的運作位址在0x30000000,運作之前需要從0x1000(加載處)複制到0x30000000(運作處),

此過程也就用到了讀取Nand flash。這就是存儲位址和連接配接(運作)位址的不同,稱為加載時域和運作時域,可以在.lds連接配接腳本檔案中分别指定。

編寫好的.lds檔案,在用arm-linux-ld連接配接指令時帶-Tfilename來調用執行,如arm-linux-ld –Tnand.lds x.o y.o –o xy.o。也用-Ttext參數直接指定連接配接位址,

如arm-linux-ld –Ttext 0x30000000 x.o y.o –o xy.o。

總之:

         連接配接位址<==>運作位址

         存儲位址<==>加載位址

既然程式有了兩種位址,就涉及到一些跳轉指令的差別,下面就來具體看看這些跳轉指令。

ARM彙編中,常有兩種跳轉方法:b跳轉指令、ldr指令向PC指派。

(1)b step1 :b跳轉指令是相對跳轉,依賴目前PC的值,偏移量是通過該指令本身的bit[23:0]算出來的,這使得使用b指令的程式不依賴于要跳到的代碼的位置,隻看指令本身。

(2)ldr pc, =step1 :該指令是從記憶體中的某個位置(step1)讀出資料并賦給PC,同樣依賴目前PC的值,但是偏移量是那個位置(step1)的連接配接位址(運作時的位址),

       是以可以用它實作從Flash到RAM的程式跳轉。

繼續閱讀