日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

IMX6ULL uboot启动分析(五)

發(fā)布時(shí)間:2023/12/10 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IMX6ULL uboot启动分析(五) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  • 1、前言
  • 2、uboot段相關(guān)變量
  • 3、relocate_code函數(shù)
  • 4、relocate_vectors函數(shù)
  • 5、小結(jié)

1、前言

在前面的文章《Uboot啟動(dòng)流程分析(四)》,鏈接如下:

原文鏈接

已經(jīng)對(duì)board_init_f() 函數(shù)作出了簡(jiǎn)單的分析,該函數(shù)對(duì)一些早期的外設(shè)進(jìn)行了初始化,例如調(diào)試串口,并填充了gd_t結(jié)構(gòu)體中的成員變量,最主要的是對(duì)整個(gè)DRAM的內(nèi)存進(jìn)行了分配,以便uboot的重定位,接下來(lái),先回顧一下_main函數(shù)的大概流程,如下:

_main|board_init_f_alloc_reserve-->reserve gd and early malloc area|board_init_f_init_reserve-->initialize global data|board_init_f-->initialize ddr,timer...,and fill gd_t|relocate_code-->relocate uboot code|relocate_vectors-->relocate vectors|board_init_r-->calling board_init_r

在_main函數(shù)中,調(diào)用完了board_init_f()函數(shù)后,將DRAM的內(nèi)存分配好,填充gd_t結(jié)構(gòu)體成員變量,接下來(lái),就是調(diào)用relocate_code()函數(shù)重定位uboot代碼,調(diào)用relocate_vectors()函數(shù)重定位中斷向量表,本篇文章將簡(jiǎn)單分析uboot的大概重定位過(guò)程。

2、uboot段相關(guān)變量

在分析relocate_code函數(shù)之前,先來(lái)總結(jié)一下相關(guān)的uboot段相關(guān)變量,這些段的地址在uboot代碼重定位的時(shí)候需要用到,將uboot源碼進(jìn)行編譯后,會(huì)在源碼根目錄生成u-boot.lds鏈接文件和u-boot.map內(nèi)存映射文件,通過(guò)這兩個(gè)文件,可以尋找到uboot段的地址,一些重要的段地址如下表格所示:

在上面尋找到的變量值中,除了_start和__image_copy_start值不會(huì)該變,當(dāng)修改了uboot的源碼或改變了編譯條件,其他的變量都可能會(huì)發(fā)生改變,因此分析時(shí),一定要以實(shí)際編譯時(shí)進(jìn)行uboot分析。

3、relocate_code函數(shù)

在調(diào)用relocate_code函數(shù)之前的分析,可以參考《文章Uboot啟動(dòng)流程分析(二)》,鏈接如下所示:

原文鏈接

也就是_main函數(shù)執(zhí)行的第二部分,relocate_code函數(shù)會(huì)傳入一個(gè)參數(shù),該參數(shù)為gd->relocaddr,也就是uboot重定位的目的地址。

接下來(lái),對(duì)relocate_code函數(shù)進(jìn)行分析,該函數(shù)用于重定位uboot,函數(shù)的定義在下面的匯編文件中:

uboot/arch/arm/lib/relocate.S

relocate_code函數(shù)的定義如下所示:

/** void relocate_code(addr_moni)** This function relocates the monitor code.** NOTE:* To prevent the code below from containing references with an R_ARM_ABS32* relocation record type, we never refer to linker-defined symbols directly.* Instead, we declare literals which contain their relative location with* respect to relocate_code, and at run time, add relocate_code back to them.*/ENTRY(relocate_code)ldr r1, =__image_copy_start/* r1 <- SRC &__image_copy_start */ //r1保存源image開(kāi)始地址//r0 = gd->relocaddr,r4 = r0 - r1 = 0x8ff3b000 - 0x87800000 = 0x873b000subs r4, r0, r1 /* r4 <- relocation offset */ //r4 = gd->reloc_off,保存偏移地址beq relocate_done /* skip relocation */ //如果r0和r1相等,則不需要uboot重定位ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */ //r2保存源image結(jié)束地址copy_loop:ldmia r1!, {r10-r11} /* copy from source address [r1] */ //拷貝uboot代碼到r10和r11stmia r0!, {r10-r11} /* copy to target address [r0] */ //將uboot代碼寫(xiě)到目的地址cmp r1, r2 /* until source end address [r2] */ //判斷uboot是否拷貝完成blo copy_loop //循環(huán)拷貝/** fix .rel.dyn relocations //修復(fù).rel.dyn段*/ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ fixloop:ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */and r1, r1, #0xffcmp r1, #23 /* relative fixup? */bne fixnext/* relative fix: increase location by offset */add r0, r0, r4ldr r1, [r0]add r1, r1, r4str r1, [r0] fixnext:cmp r2, r3blo fixlooprelocate_done:ENDPROC(relocate_code)

函數(shù)傳入的參數(shù)為r0 = 0x8ff3b000,uboot重定位的目的地址,函數(shù)進(jìn)來(lái)后,將__image_copy_start的數(shù)值賦值給r1,也就是uboot復(fù)制開(kāi)始地址,從表格可以知道r1 = 0x87800000,接下來(lái)進(jìn)行一個(gè)減法操作,此時(shí)r4將保存著uboot的偏移量,也就是r4 = gd->reloc_off。

接下來(lái)會(huì)進(jìn)行一個(gè)判斷,判斷uboot重定位的目的地址是否和uboot源地址是否相等,如果相等的話,表示不需要重定位,跳到relocate_done處,繼續(xù)運(yùn)行,如果不相等的話,則是需要進(jìn)行重定位。

將__image_copy_end的值賦值給r2,也就是uboot復(fù)制結(jié)束的地址,從表格中可以知道,此時(shí)r2 = 0x87868960,接下來(lái),開(kāi)始進(jìn)行uboot代碼的拷貝,進(jìn)入到copy_loop循環(huán),從r1,也就是__image_copy_start開(kāi)始,讀取uboot代碼到r10和r11寄存器,一次就拷貝兩個(gè)32位的數(shù)據(jù),r1自動(dòng)更新,保存下一個(gè)需要拷貝的地址,然后將r10和r11里面的數(shù)據(jù),寫(xiě)到r0保存的地址,也就是uboot重定位的目的地址,r0自動(dòng)更新。

接下來(lái),比較r1和r2是否相等,也就是判斷uboot代碼是否拷貝完成,如果沒(méi)完成的話,跳轉(zhuǎn)到copy_loop處,繼續(xù)循環(huán),直到uboot拷貝完成。

函數(shù)relocate_code()的第二部分是修復(fù).rel.dyn的定位問(wèn)題,.rel.dyn段存放了.text段中需要重定位地址的集合,uboot重定位就是uboot將自身拷貝到DRAM的另外一個(gè)地址繼續(xù)運(yùn)行(DRAM的高地址),一個(gè)可執(zhí)行的bin文件,它的鏈接地址和運(yùn)行地址一定要相等,也就是鏈接到哪個(gè)地址,運(yùn)行之前就需要拷貝到哪個(gè)地址中進(jìn)行運(yùn)行,在前面的重定位后,運(yùn)行地址和鏈接地址就不一樣了,這樣在進(jìn)行尋址就會(huì)出問(wèn)題,對(duì).rel.dyn的定位問(wèn)題進(jìn)行修復(fù),就是為了解決該問(wèn)題。

下面,使用一個(gè)簡(jiǎn)單的例子進(jìn)行分析:

先在新適配的板級(jí)文件中添加測(cè)試代碼,文件如下:

uboot/board/freescale/mx6ul_comp6ul_nand/mx6ul_comp6ul_nand.c

添加的內(nèi)容如下:

static int rel_a = 0;void rel_test(void) {rel_a = 100;printf("rel_test\n"); }/* 在board_init()函數(shù)中調(diào)用rel_test()函數(shù) */ int board_init(void) {......rel_test();return 0; }

接下來(lái)對(duì)uboot源碼重新編譯,并將u-boot文件進(jìn)行反匯編分析,使用命令如下:

$ cd uboot $ make $ arm-linux-gnueabihf-objdump -D -m arm u-boot > u-boot.dis

然后,打開(kāi)u-boot.dis文件,并在文件中,找到rel_a變量、rel_test函數(shù)和board_init函數(shù)相關(guān)的匯編代碼,如下所示:

/* rel_test尋址分析 */ 8780424c <rel_test>: 8780424c: e59f300c ldr r3, [pc, #12] ; 87804260 <rel_test+0x14> 87804250: e3a02064 mov r2, #100 ; 0x64 87804254: e59f0008 ldr r0, [pc, #8] ; 87804264 <rel_test+0x18> 87804258: e5832000 str r2, [r3] 8780425c: ea00f1a9 b 87840908 <printf> 87804260: 87868994 ; <UNDEFINED> instruction: 0x87868994 87804264: 878494ce strhi r9, [r4, lr, asr #9] ... ... 878043e4: e59f2038 ldr r2, [pc, #56] ; 87804424 <board_init+0x1bc> 878043e8: e5923068 ldr r3, [r2, #104] ; 0x68 878043ec: e3833030 orr r3, r3, #48 ; 0x30 878043f0: e5823068 str r3, [r2, #104] ; 0x68 878043f4: ebffff94 bl 8780424c <rel_test> ... ... 87868994 <rel_a>: 87868994: 00000000 andeq r0, r0, r0

在0x878043f4處,也就是board_init()函數(shù)中調(diào)用了rel_test()函數(shù),在匯編代碼中,可以看到是使用了bl指令,而bl指令是相對(duì)尋址的(pc + offset),為位置無(wú)關(guān)指令,因此,可以知道,uboot中的函數(shù)調(diào)用時(shí)與絕對(duì)位置無(wú)關(guān)的。

接下來(lái),分析rel_test()函數(shù)對(duì)全局變量rel_a的調(diào)用過(guò)程,在0x8780424c處開(kāi)始,先設(shè)置r3的值為pc + 12地址處的值,由于ARM流水線的原因,當(dāng)前pc的值為當(dāng)前的地址加8,所以pc = 0x8780424c + 8 = 0x87804254,因此r3 = pc + 12 = ?0x87804254 + 12 = 0x87804260,從反匯編的代碼中可以看到0x87804260處的值為0x87868994,所以,最終r3的值為0x87868994,而且從反匯編代碼中可以看到0x87868994就是變量rel_a的地址,rel_test()函數(shù)繼續(xù)執(zhí)行,將100賦值到r2寄存器,然后通過(guò)str指令,將r2的值寫(xiě)到r3的地址處,也就是將0x87868994地址處的值賦值100,就是函數(shù)中調(diào)用的rel_a = 100;語(yǔ)句。

從上面的分析,可以知道rel_a = 100;的執(zhí)行過(guò)程為:

  • 在函數(shù)rel_test()的末尾處有一個(gè)地址為0x87804260的內(nèi)存空間,此內(nèi)存空間中保存著變量rel_a的地址;
  • 函數(shù)rel_test()想要訪問(wèn)變量rel_a,首先需要訪問(wèn)0x87804260內(nèi)存空間獲取變量rel_a的地址,而訪問(wèn)0x87804260是通過(guò)偏移來(lái)訪問(wèn)的,與位置無(wú)關(guān)的操作;
  • 通過(guò)訪問(wèn)0x87804260獲取變量rel_a的地址,然后對(duì)變量rel_a進(jìn)行賦值操作;
  • 函數(shù)rel_test()對(duì)變量rel_a的訪問(wèn)并沒(méi)有直接進(jìn)行,而是通過(guò)一個(gè)偏移地址0x87804260,找到變量rel_a真正的地址,然后才對(duì)其操作,偏移地址的專(zhuān)業(yè)術(shù)語(yǔ)為L(zhǎng)abel。

從分析中,可以知道,該偏移地址就是uboot重定位運(yùn)行是否會(huì)出錯(cuò)的原因,在上面可以知道,uboot重定位的偏移量為0x8ff3b000,將上面給出的反匯編代碼進(jìn)行重定位后,也就是加上地址偏移量0x873b000后,如下:

/* rel_test尋址分析(重定位后) */ 8ff3f24c? <rel_test>: 8ff3f24c: e59f300c ldr r3, [pc, #12] 8ff3f250: e3a02064 mov r2, #100 ; 0x64 8ff3f254: e59f0008 ldr r0, [pc, #8] 8ff3f258: e5832000 str r2, [r3] 8ff3f25c: ea00f1a9 b 87840908 <printf> 8ff3f260: 87868994 ; <UNDEFINED> instruction: 0x87868994 8ff3f264: 878494ce strhi r9, [r4, lr, asr #9] ... ... 8ff3f3e4: e59f2038 ldr r2, [pc, #56] 8ff3f3e8: e5923068 ldr r3, [r2, #104] ; 0x68 8ff3f3ec: e3833030 orr r3, r3, #48 ; 0x30 8ff3f3f0: e5823068 str r3, [r2, #104] ; 0x68 8ff3f3f4: ebffff94 bl 8780424c <rel_test> ... ... 8ffa3994 <rel_a>: 8ffa3994: 00000000 andeq r0, r0, r0

函數(shù)rel_test()假設(shè)調(diào)用后對(duì)rel_a變量進(jìn)行訪問(wèn),分析和上面一樣,先通過(guò)pc和偏移量找到0x8ff3f260,該地址是重定位后的地址,可此時(shí)該地址的值為0x87868994,也就是沒(méi)有重定位后的rel_a變量的地址,但是從上面可以知道,uboot重定位后,rel_a變量的新地址為0x8ffa3994,因此,我們可以知道,如果uboot重定位后,要想正常訪問(wèn)rel_a變量,必須要將0x8ff3f260地址中的值0x87868994加上偏移量0x873b000,才能保證uboot重定位后,能正常訪問(wèn)到rel_a變量,將.rel.dyn段進(jìn)行重定位修復(fù),就是為了解決鏈接地址和運(yùn)行地址不一致的問(wèn)題。

在uboot中,對(duì)于重定位后鏈接地址與運(yùn)行地址不一致的解決辦法就是使用位置無(wú)關(guān)碼,在uboot編譯使用ld鏈接的時(shí)候使用參數(shù)"-pie"可生成與位置無(wú)關(guān)的可執(zhí)行程序,使用該參數(shù)后,會(huì)生成一個(gè).rel.dyn段,uboot則是靠該段去修復(fù)重定位后產(chǎn)生的問(wèn)題的,在uboot的反匯編文件中,有.rel.dyn段代碼,如下:

Disassembly of section .rel.dyn:87868988 <__rel_dyn_end-0x91a0>: 87868988: 87800020 strhi r0, [r0, r0, lsr #32] 8786898c: 00000017 andeq r0, r0, r7, lsl r0 87868990: 87800024 strhi r0, [r0, r4, lsr #32] 87868994: 00000017 andeq r0, r0, r7, lsl r0 ... ... 87868f08: 87804260 strhi r4, [r0, r0, ror #4] 87868f0c: 00000017 andeq r0, r0, r7, lsl r0 87868f10: 87804264 strhi r4, [r0, r4, ror #4] 87868f14: 00000017 andeq r0, r0, r7, lsl r0 ... ...

rel.dyn段的格式如下,類(lèi)似下面的兩行就是一組:

87868f08: 87804260 strhi r4, [r0, r0, ror #4] 87868f0c: 00000017 andeq r0, r0, r7, lsl r0

也就是兩個(gè)4字節(jié)數(shù)據(jù)為一組,其中高4字節(jié)是Label地址的標(biāo)識(shí)0x17,低4字節(jié)就是Label的地址,首先會(huì)判斷Label地址標(biāo)識(shí)是否正確,也就是判斷高4字節(jié)是否為0x17,如果是的話,低4字節(jié)就是Label地址,在上面給出的兩行代碼中就是存放rel_a變量地址的Label,0x87868f0c的值為0x17,說(shuō)明低4字節(jié)是Label地址,也就是0x87804260,需要將0x87804260 + offset處的值改為重定位后的變量rel_a地址。

在對(duì).rel.dyn段以及Label的相關(guān)概念有一定的了解后,接下來(lái),我們分析函數(shù)relocate_code()的第二部分代碼,修復(fù).rel.dyn段重定位問(wèn)題,代碼如下:

/** fix .rel.dyn relocations //修復(fù).rel.dyn段*/ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ fixloop:ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */and r1, r1, #0xffcmp r1, #23 /* relative fixup? */bne fixnext/* relative fix: increase location by offset */add r0, r0, r4ldr r1, [r0]add r1, r1, r4str r1, [r0] fixnext:cmp r2, r3blo fixloop

relocate_code()函數(shù)第二部分代碼調(diào)用后,將__rel_dyn_start的值賦給r2,也就是r2中保存著.rel.dyn段的起始地址,然后將__rel_dyn_end的值賦給r3,也就是r3中保存著.rel.dyn段的結(jié)束地址。

接下來(lái),進(jìn)入到fixloop處執(zhí)行,使用ldmia指令,從.rel.dyn段起始地址開(kāi)始,每次讀取兩個(gè)4字節(jié)數(shù)據(jù)存放到r0和r1寄存器中,其中r0存放低4字節(jié)的數(shù)據(jù),也就是Label的地址,r1存放高4字節(jié)的數(shù)據(jù),也就是Label的標(biāo)識(shí),然后將r1的值與0xff相與,取r1值的低8位,并將結(jié)果保存到r1中,接下來(lái),判斷r1中的值是否等于23(0x17),如果r1不等于23的話,也就說(shuō)明不是描述Label,跳到fixnext處循環(huán)執(zhí)行,直到r2和r3相等,也就是遍歷完.rel.dyn段。

如果r1的值等于23(0x17)的話,繼續(xù)執(zhí)行,r0保存著Label地址,r4保存著uboot重定位的偏移,因此,r0 + r4就得到了重定位后的Label地址,也就是上面分析的0x87804260 + 0x873b000 = 0x8ff3f260 = r0,此時(shí)r0已經(jīng)保存著重定位后的Label地址,然后使用ldr指令,讀取r0中保存的值到r1中,也就是Label地址所保存的rel_a變量的地址,但是此時(shí),該rel_a變量的地址仍然是重定位之前的地址0x87868994,所以,此時(shí)r1 = 0x87868994,接下來(lái),使用add指令,將r1中的值加上r4,也就是加上uboot重定位偏移量,所以,此時(shí)r1 = 0x87868994 + 0x873b000 = 0x8ffa3994,這不就是前面分析的uboot重定位后的rel_a變量的地址嗎?接下來(lái)使用str指令,將r1中的值寫(xiě)入到r0保存的地址中,也就是將Label地址中的值設(shè)置為0x8ffa3994,就是重定位后rel_a變量的新的地址。

比較r2和r3的值,查看.rel.dyn段重定位修復(fù)是否完成,循環(huán)直到完成,才能執(zhí)行完relocate_code()函數(shù)。

第二部分執(zhí)行完成后,就解決了.rel.dyn段的重定位問(wèn)題,從而解決了uboot重定位后,鏈接地址和運(yùn)行地址不一致的問(wèn)題。

4、relocate_vectors函數(shù)

執(zhí)行完relocate_code函數(shù)后,接下來(lái)就是執(zhí)行relocate_vectors函數(shù),該函數(shù)用于重定位中斷向量表,該函數(shù)的定義同樣在匯編文件:

uboot/arch/arm/lib/relocate.S

函數(shù)的定義如下所示:

ENTRY(relocate_vectors)#ifdef CONFIG_CPU_V7M/** On ARMv7-M we only have to write the new vector address* to VTOR register.*/ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ldr r1, =V7M_SCB_BASEstr r0, [r1, V7M_SCB_VTOR] #else #ifdef CONFIG_HAS_VBAR/** If the ARM processor has the security extensions,* use VBAR to relocate the exception vectors.*/ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */mcr p15, 0, r0, c12, c0, 0 /* Set VBAR */ #else/** Copy the relocated exception vectors to the* correct address* CP15 c1 V bit gives us the location of the vectors:* 0x00000000 or 0xFFFF0000.*/ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */mrc p15, 0, r2, c1, c0, 0 /* V bit (bit[13]) in CP15 c1 */ands r2, r2, #(1 << 13)ldreq r1, =0x00000000 /* If V=0 */ldrne r1, =0xFFFF0000 /* If V=1 */ldmia r0!, {r2-r8,r10}stmia r1!, {r2-r8,r10}ldmia r0!, {r2-r8,r10}stmia r1!, {r2-r8,r10} #endif #endifbx lrENDPROC(relocate_vectors)

在該函數(shù)中,對(duì)于i.mx6ul芯片來(lái)說(shuō)CONFIG_CPU_V7M沒(méi)有定義,接下來(lái),判斷是否定義了CONFIG_HAS_VBAR,表示向量表偏移,在.config文件有該配置,因此,會(huì)執(zhí)行該定義相關(guān)的代碼,首先是將gd->relocaddr的值賦給r0,也就是r0里面保存重定位后uboot的首地址,中斷向量表也是從這個(gè)首地址開(kāi)始存儲(chǔ)的,接下來(lái),使用mcr指令,將r0的值寫(xiě)到CP15的VBAR寄存器中,其實(shí)就是將新的中斷向量表首地址寫(xiě)到VBAR寄存器中,設(shè)置中斷向量表偏移。

5、小結(jié)

本篇文章主要是對(duì)Uboot啟動(dòng)流程中,uboot重定位的流程進(jìn)行簡(jiǎn)單分析,核心是對(duì)relocate_code函數(shù)和relocate_vectors函數(shù)的分析。

總結(jié)

以上是生活随笔為你收集整理的IMX6ULL uboot启动分析(五)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。