標籤:
當我們寫連結器指令碼的時候,我們會設定程式碼片段的起始連結地址為0x30008000(S3C2440)。如果對連結地址理解錯誤的話,可能會出現像我這樣的問題。
Q:gboot的連結器指令碼裡寫的其實連結地址是記憶體中的0x30008000,再用交叉工具反組譯碼得到的start.S前幾行代碼地址都變成了0x30008000+,不太對啊。ARM啟動流程裡面講處理器將NAND中的代碼複製了4K到墊腳石中,墊腳石的地址是0x0,也正是因為這樣,我們設定的異常向量表才有意義,處理器才能準確的找到處理異常的標號。但是把連結地址改成了0x30008000還怎麼找?
A:首先要理解連結地址。我認為,連結地址就是為程式假想了一個起點,之後的代碼都是在這個假想的起點基礎上展開的。我們只有根據這個連結地址,才能找到後續的一些指令並執行。但是這個連結地址不是實際的指令的地址,我們先對gboot.elf進行反組譯碼看看
30008000 <_start>:30008000: ea000014 b 30008058 <reset>30008004: e59ff014 ldr pc, [pc, #20] ; 30008020 <_undefined_instruction>30008008: e59ff014 ldr pc, [pc, #20] ; 30008024 <_software_intrrupt>3000800c: e59ff014 ldr pc, [pc, #20] ; 30008028 <_prefetch_abort>30008010: e59ff014 ldr pc, [pc, #20] ; 3000802c <_data_abort>30008014: e59ff014 ldr pc, [pc, #20] ; 30008030 <_not_used>30008018: e59ff014 ldr pc, [pc, #20] ; 30008034 <_irq>3000801c: e59ff014 ldr pc, [pc, #20] ; 30008038 <_fiq>
用b reset舉例,雖然reset標號的地址已經到了0x30008000,但是我們的代碼此時實際上是在墊腳石中啟動並執行,也就是說PC指標還是指在0x0的位置。這樣0x0和0x30008000就形成了一個映射的關係,即0x4相當於0x30008004,0x8相當於0x30008008......,都是因為我們將連結起始地址設成了0x30008000,這也就是我們所說的相對跳轉。
Q:為什麼連結起始地址要寫成0x30008000?
A:照理說這個地址是隨意的,只要在S3C2440的定址空間內都可以。但是後期我們要執行C語言程式,而對堆棧等的初始化都是對SDRAM進行的,即在0x30000000之後的64M空間中。所以為了C語言能夠成功運行,直接將代碼的連結地址設到記憶體中,這樣方面之後的絕對跳轉,即
ldr pc, =main
對連結地址的理解