歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

U-Boot第一階段關鍵代碼理解

1.u-boot程序的入口地址

  要理解程序的入口地址,自然想到的是連接文件,首先看連接文件"/board/smdk2410/u-boot.lds"

  ENTRY(_start)

  SECTIONS

  {

  . = 0x00000000;

  . = ALIGN(4);

  .text :

  {

  cpu/arm920t/start.o (.text)

  *(.text)

  }

  . = ALIGN(4);

  .rodata : { *(.rodata) }

  . = ALIGN(4);

  .data : { *(.data) }

  . = ALIGN(4);

  .got : { *(.got) }

  __u_boot_cmd_start = .;

  .u_boot_cmd : { *(.u_boot_cmd) }

  __u_boot_cmd_end = .;

  . = ALIGN(4);

  __bss_start = .;

  .bss : { *(.bss) }

  _end = .;

  }

  (1) 從ENTRY(_start)可以看出u-boot的入口函數是_start

  (2) 從. = 0x00000000也許可以看出_start的地址是0x00000000,事實並不是這樣的,這裡的0x00000000無效,在連接的時候最終會被TETX_BASE所代替的,具體請參考u-boot根目錄下的config.mk.

  (3) 網上很多說法是 _start=TEXT_BASE,我想這種說法也是正確的,實際上,不是TEXT_BASE這段代碼映射到0x0的地方,其實編譯器進行編譯,是按照鏈接文件進行的。也就是說,編譯的時候所有的地址都是相對於TEXT_BASE計算出來的。而這個地址是在連接文件裡面指定的為0x338f0000,你可以看鏈接文件,可以看反匯編裡面的地址,全是0x33f8XXXX的地址。

  1.1 cpu/arm920t/start.S部分代碼釋疑

  (1)

  [html] view plaincopy _TEXT_BASE:

  .word TEXT_BASE

  此處定義一匯編語言標簽_TEXT_BASE,它的值就是 “.word TEXT_BASE” 這句代碼所在的鏈接地址值; TEXT_BASE定義在board\smdk2410\config.mk文件中

  (即0x33F80000)。以後通過引用_TEXT_BASE即可得到TEXT_BASE的值。

  (2)

  [html] view plaincopy .globl _armboot_start

  _armboot_start:

  .word _start

  定義外部可以引用的變量_armboot_start,其值為代碼“.word _start"所在的鏈接地址值。

  圖1 u-boot.map

  編譯完成後,會生成u-boot.map 文件,如圖1所示。圖1顯示_armboot_start 所指的地址值是0x33f80044。用UltraEdit打開u-boot.bin,定位到偏移0x44處(0x44由_armboot_start 所在地址0x33f80044-TEXT_BASE得到),如圖2所示,此值就是_start 所指的鏈接地址的值 33 F8 00 00(小端模式)。

  圖2 u-boot.bin

  (3)

  [html] view plaincopy .globl _bss_start

  _bss_start:

  .word __bss_start

  __bss_start 的值定義在board/smdk2410/u-boot.lds文件中:

  [html] view plaincopy . = ALIGN(4);

  __bss_start = .;

  .bss : { *(.bss) }

  _end = .;

  __bss_start = .; 表示__bss_start 值就是當前位置的值。當前位置是多少呢?從下面一句 .bss : { *(.ss) } 知道。緊接該位置後面馬上就是放 bss 段數據了。所以,__bss_start 當然就是bss段的起始地址。_end 就是bss段的結束地址。

  bss是這個鏈接腳本的最後一個段。start.S 就是以這個段的起始地址來計算要搬運的 u-boot 代碼的大小,即這個段前面的所有數據都將被搬到TEXT_BASE處,然後跳到start_armboot 處,即C語言的入口代碼。__bss_start這個值是多少? 我編譯後的值是0x33f979d4。可以u-boot.map文件中查到,如圖3所示,bss段是從0x33f979d4開始分配的。


圖3 u-boot.map

  由圖1可知,_bss_start 所指的鏈接地址為0x33f80048, 該處即存儲了bss段的起始地址。打開u-boot.bin,定位到偏移0x48處(0x48由_bss_start所在地址0x33f80048-TEXT_BASE得到)如圖2所示,此值確為 33 F9 79 D4(小端模式)。

  2.SDRAM初始化,lowleverl_init.S

  _TEXT_BASE:

  .word TEXT_BASE

  .globl lowlevel_init

  lowlevel_init:

  /* memory control configuration */

  /* make r0 relative the current location so that it */

  /* reads SMRDATA out of FLASH rather than memory ! */

  ldr r0, =SMRDATA

  ldr r1, _TEXT_BASE

  sub r0, r0, r1

  ldr r1, =BWSCON /* Bus Width Status Controller */

  add r2, r0, #13*4

  不理解SMRDATA與_TEXT_BASE這兩個地址相減之後,到底是一個什麼值。為什麼要相減呢?

  我們的程序是存放在Flash中的,這裡面的地址稱為加載地址,當然是從0x0這個地址開始,而程序中所用的標號編譯時都是基於_TEXT_BASE地址,我們稱為鏈接或運行地址。這時加載地址和運行地址不相同,所以要求我們的代碼在還沒有搬移到TEXT_BASE(0x33f80000)這個位置以前是不能使用這些標號。如果直接調用標號,程序就飛了,只有運行在SDRAM中才可以調用標號,因為0x33f80000在SDRAM中。所以只能找到一個相對於0x0的偏移地址出來,才能得到真正的SMRDATA定義數據,也就是說此部分代碼與地址無關,是基於PC的偏移來進行的。下面來分析代碼:

  ldr r0, =SMRDATA取得標號SMRDATA的絕對地址,它是大於_TEXT_BASE;ldr r1, _TEXT_BASE 取基地址(0x33f80000)。sub r0, r0, r1相減後得到SMRDATA相對於_TEXT_BASE偏移。

  3.UBOOT啟動代碼重定位

  #ifndef CONFIG_SKIP_RELOCATE_UBOOT

  relocate: /* relocate U-Boot to RAM */

  adr r0, _start /* r0 <- current position of code */

  ldr r1, _TEXT_BASE /* test if we run from flash or RAM */

  一直迷惑r0,r1什麼時候才相等,什麼時候才不等,看看adr與ldr偽指令區別

  adr是小范圍的地址讀取偽指令,指令將基於PC相對偏移的地址值讀取到寄存器中;ldr是大范圍的讀取地址偽指令,用於加載32為立即數或一個地址到指定的寄存器中。

  _start的鏈接地址在鏈接的時候與各指令之間的偏移地址已經確定,與位置無關,該偏移地址是負值,因為_start始終是代碼段的起始地址。假設PC所指指令的鏈接地址與_start之間的偏移量為x,該值為負,所以在執行adr r0,_start時,相當於ldr r0,=PC+x(x<0),這個時候r0中的值即為代碼段的起始地址。因為PC中的地址是當前代碼的加載地址,和它所指的指令的鏈接地址基於_start(代碼的鏈接起始地址)的偏移相加後,就是代碼所存儲的起始地址,即加載起始地址。

Copyright © Linux教程網 All Rights Reserved