2013-10-21 55 views
0

我想了解的部分代碼,其公司在RAM中的U-boot的搬遷說,下面是代碼這個搬遷的u-boot的工作原理在這裏

#ifndef CONFIG_SKIP_RELOCATE_UBOOT 
    relocate: 
    adr r0,_start  /*r0 <--- Current posistion of code8*/ 
    ldr r1,_TEXT_BASE /* test if we run from flash or Ram /* 
    cmp r0,r1 
    beq stack_setup 
    ldr r2,_armboot_start 
    ldr r3,_bss_start 
    sub r2,r3,r2 
    add r2,r0,r2 
    copy_loop: 
    ldmia r0!,{r3-r10} 
    stmia r1!,{r3-r10} 
    cmp r0,r2 
    ble cop_loop 
    #endif /*CONFIG_SKIP_RELOCATE_UBOOT/* 

現在canbody讓我知道是怎麼回事在這裏發生??我們正在測試如果我們的u-boot從RAM或Flash運行?

我在ARM平臺上。

+0

只是讀取代碼。它是比較你是從閃存中運行這些代碼還是在內存中運行它的一個副本。如果從閃存運行,然後再次嘗試,如果從內存然後繼續到下一個東西(堆棧設置) –

+0

@dwelch thaks爲響應,所以r0有FLASH位置和r1有RAM位置? –

+0

我認爲差異將會是adr vs ldr。一個人將把PC作爲數據的一部分(以及地址),另一個不是。這是關鍵的區別。 –

回答

2

這是一個很簡單的例子,

.globl _start 
_start: 

    adr r0,_start 
    ldr r1,_TEXT_BASE 

... 

_TEXT_BASE: .word _start 

在組裝時,鏈接,然後拆卸:

00008000 <_start>: 
    8000: e24f0008 sub r0, pc, #8 
    8004: e59f101c ldr r1, [pc, #28] ; 8028 <_TEXT_BASE> 
... 
00008028 <_TEXT_BASE>: 
    8028: 00008000 andeq r8, r0, r0 

還有就是你的答案。 adr指令基於假設您的pc在執行時包含0x8008。無論你身在何處,ldr都會提供一個相同的鏈接時間值。

例如,如果這個代碼實際上位於地址0x20000000處,那麼當第一條指令(adr是僞指令,在反彙編中它是8的一個子集)時,adr被執行,現在你得到一個0x20000008- 8 = 0x20000000,您將它與不匹配的0x8000進行比較。如果你在0x8000處運行代碼,那麼0x8008-8 = 0x8000和兩個匹配。

只要閱讀代碼並查找adr指令(或者做我所做的,只是嘗試它並檢查編譯器/工具的輸出,並/或在硬件上運行它,如果不顯示答案的話)。

編輯:

使用具有不同前綴的GNU工具,但是這個代碼是很簡單的幾乎不關心。 arm-none-eabi-或arm-none-linux-gnueabi-。

假設大會文件名是foo.s

arm-none-eabi-as foo.s -o foo.o 
arm-none-eabi-ld -T memmap foo.o -o foo.elf 
arm-none-eabi-objdump -D foo.elf 

我所謂MEMMAP是鏈接描述文件,你可以使用命令行-Ttext =爲0x8000在這種情況下。我喜歡將.elf擴展名用於我的交叉編譯二進制文件,而不是每個人都這樣做。

00008000 <_start>: 
    8000: e24f0008 sub r0, pc, #8 
    8004: e59f101c ldr r1, [pc, #28] ; 8028 <_TEXT_BASE> 
... 
00008028 <_TEXT_BASE>: 
    8028: 00008000 andeq r8, r0, r0 

_start是GNU工具的事情鏈接器需要/想這個標籤知道代碼的入口點。所以它不是一個真正的uboot的東西,雖然uboot可能也很在意,但它絕對是一個gnu鏈接器的東西。你可以看到linux作爲一個內核在類似的地址上啓動,但它確實是任意的,你可以設置你的系統和二進制文件。

對於這裏發生的事情,沒有什麼是ARM特有的或魔法。在任何平臺上您都可以提出正確的說明。

程序計數器在前面是兩個指令,這是一個32位的arm指令,所以程序計數器在執行指令時被假定爲這個指令的地址加上8個。由於這個adr的東西是第一個指令在_start表示這條指令在地址0x8000處被鏈接/編譯,並且從0x8008到0x8000得到的指令編碼爲r0 = pc-8;其他信息是鏈接器在標籤_TEXT_BASE之後提供_start的地址。所以另一個步驟是將該值加載到r1中。

這隻適用於假設代碼實際存在於閃存中以使處理器在地址0x8000處看到閃存中的指令的情況。 0x8000和0x8000的比較是相等的,所以程序在ram中生成它自己的副本,然後它會跳到這個副本的起始位置,這次它通過代碼的副本時,一個寄存器包含一些地址除了0x8000以外,所以比較失敗,如果你從閃存運行原始版本或者從RAM運行拷貝,這只是一個檢測器。目的是複製並運行基於RAM的副本。需要其他預防措施以確保代碼可以在兩個地址上運行(與位置無關的代碼)。

如果你碰巧知道flash地址爲0x00008000,碰巧知道的例子,RAM地址是說0x20000000你可以有,而不是比較的PC用說的0x10000或可能相與PC的價值與0xFF000000

and r0,pc,#0xFF000000 
beq stack_setup 

但我認爲這是更通用的代碼,所以使用額外的說明和準確的比較。

這種類型的技巧還是比較常見的,檢測閃存或RAM複製等。使用的確切指令以及如何讓鏈接器爲您填充事物是特定於目標的。

在這種情況下,閃存可能位於某個非零地址,而0x8000是RAM副本。我不知道。

+0

感謝@dwelch讓事情變得簡單明瞭,我知道程序集很少,x86上也是如此。我瞭解的是標籤_start,它是u-boot映像的入口點,其地址爲0008000我的平臺)。現在第一個adr r0,_start會被存儲到0008000的某個偏移處?在「board/kb9202configs.mk」中定義的_TEXT_BASE是爲了什麼目的。我也想知道你如何做這個反彙編,以便我也可以在我的平臺上做同樣的事情。 –

+0

我個人的觀點是,如果你想在這個級別上工作,那麼即使你的彙編語言技能不強,你也需要知道如何編譯,彙編和鏈接。爲了調試,如果你已經正確鏈接到目標系統,最簡單的方法是反彙編,並看看事情結束了。所以擴展我會爭論你學習如何使你的工具反彙編你所鏈接的東西......從那裏你可以重複我上面做的事情來弄清楚發生了什麼。 –

+0

感謝@dwelch對我友善,我會採納您的建議並繼續努力。謝謝您提供詳細的答案。 –