【Linux】制作U-Boot烧写镜像到SD卡的过程(中篇:LDS文件)
上一篇文章,講述了制作U-Boot燒寫鏡像到SD卡的過程,其中運用make的方式來進行將.s文件編譯成.bin文件,那make是什么意思?它主要實現了什么?
先講一下,如果不采用make的方式該怎樣實現這個過程。
準備工作
先準備兩個.s文件,myboot.s和mylowlevel_init.s。為了使用講解一下鏈接過程,本文故意將gpio_out和led2_on兩個過程寫在兩個文件中。
myboot.s:
b reset //8種異常的處理,都是跳轉到reset b reset b reset b reset b reset b reset b resetreset:bl gpio_outbl led2_onmov r0, r1 //5句無用代碼mov r1, r2mov r2, r3mov r3, r4mov r4, r5 1: //標號b 1b //死循環gpio_out:ldr r11, =0xE0200280 //獲得寄存器地址ldr r12, =0x00001111 //配置成輸出狀態str r12, [r11] //將r12寄存器的值放回r11ldr r11, =0xE0200284ldr r12, =0xFstr r12, [r11]mov pc, lrmylowlevel_init.s:
.globl led2_on led1_on:ldr r11, =0xE0200284ldr r12, [r11]bic r12, r12, #(1<<1) //將r12的第二位清零,回寫到r12str r12, [r11]mov pc, lr下面就先將.s文件匯編成.o文件,利用arm-linux-gcc命令。
arm-linux-gcc -c mystart.s arm-linux-gcc -c mylowlevel_init.s那么兩個.o文件怎么鏈接成一個可執行文件呢?
.o鏈接成可執行文件
LDS文件
當對一個.c文件進行處理,經過預編譯、編譯、匯編后,最終會生成.o文件。最后一步,將所有的.o文件進行鏈接,從而生成最終的可執行文件。
那么如何進行鏈接呢?
匯編后的.o文件會由好多段(section)組成,其中最基本的段就是:bss段,data段和text段。這些段的含義如下:
- bss段:bss段(bss segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域,屬于靜態內存分配;
- data段:數據段(data segment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域,屬于靜態內存分配;
- text段:代碼段(code segment/text segment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,并且內存區域通常屬于只讀(某些架構也允許代碼段為可寫,即允許修改程序)。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。
在鏈接的過程中,當然不會喜歡將這些段直接拼接,而是希望它們按種類進行分別拼接,也就是說:
對于標準C程序而言,鏈接是固定的。它是ld調用一個缺省的鏈接腳本來完成的。因此對于一般的應用開發者,幾乎感覺不到ld以及鏈接腳本的存在。
但是如果在一些特殊情況下,主要是底層非操作系統程序。里面很多代碼,特別是匯編代碼。必須要鏈接到指定的位置。而且這個時候的程序入口不一定也不是main了。這種情況在bootloader、Linux內核以及裸機程序下比較普遍,這時就要手工編寫lds文件了。
總而言之,創建.o可執行文件的最后一步就是鏈接。它是由ld或者是用gcc間接調用ld來完成的。它主要任務和把外部庫和應用程序目標代碼的各個段放到正確位置。
那么,本文就先創建一個簡單的LDS文件(myboot.lds)來進行鏈接:
SECTIONS {. = 0xD0020010 //鏈接完的可執行文件開始運行的地址(即定位器位置).text : {mystart.o //mystart.o的.text最前面* (.text) //剩余文件的.text都放在后面,先后順序不管}.data : {* (.data) //所有文件的.data放在一起}.bss_start = .; //.bss_start在當前位置.bss : { //可能在其他的文件中用到* (.bss)}.bss_end = .; //.bss_end在當前位置 }想要了解更多的內容,可以參考博文:Linux下的lds鏈接腳本詳解。
鏈接
鏈接部分使用arm-linux-ld指令:
arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o查看文件
查看一下mystart.o文件:
arm-linux-objdump -S mystart.o運行結果如圖所示:
可以看到,由于未鏈接所以地址從0開始。前面8句都是跳轉到地址20,地址20就是reset語句。但是,可以看到所有的匯編代碼相同,但是機器指令卻是不同的。
根據機器指令,如何計算出匯編代碼呢?
根據ARM的手冊,可以查看這些機器指令的含義,這里就不展開了。ea000006就表示向后跳轉6條指令;而pc指針等于當前地址+8,即指向后兩條指令處。總共是向后跳轉8條指令,也就是reset處。這樣就可以理解了。
另外,還看到led2_on的匯編代碼,由于還未進行鏈接不知道跳轉到哪里去,所以顯示的地址為0。
除此之外,還看到gpio_out的代碼有所變化,r11變成了fp,r12變成了ip,這其實就是ARM內核將r11、r12寄存器增加了新的功能,但是在一般情形下,也可以當做普通寄存器,不需要管。
還有,由于ARM的每條指令為4個字節,但是立即數0xE0200280直接就已經是4個字節了。因此,ARM就將這一條指令變成了兩條指令,一條指令將0xE0200280放在一個新的地址上,另一條指令通過相對指針偏轉指令來進行讀取。
40: e59fb014 ldr fp, [pc, #20] 5c: e0200280 .word 0xe0200280順便也查看一下mylowlevel_init.o文件
arm-linux-objdump -S mylowlevel_init.o運行結果如圖所示:
就不再進行分析了。
最后看一下u-boot可執行文件:
arm-linux-objdump -S myboot運行結果如圖所示:
可以看到,起始地址和led2_on的地址都沒有問題。
后續工作
得到了可執行文件,但是由于myboot的文件較大,需要生成純二進制.bin文件。使用objcopy命令:
arm-linux-objcopy -O binary myboot myboot.bin然后,添加HeaderInfo信息:
./mkv210 u-boot.bin u-boot.16k最后燒寫到SD卡中:
sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1總結起來:
arm-linux-gcc -c mystart.s arm-linux-gcc -c mylowlevel_init.s //.s文件生成.o文件arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o //.o文件生成可執行文件arm-linux-objcopy -O binary myboot myboot.bin //只保留二進制文件./mkv210 u-boot.bin u-boot.16k //添加HeaderInfosudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1 //燒寫到SD卡可以看到,如果講需求改成點亮LED3的話,只需要修改mylowlevel_init.s即可。但是這些命令都還要再次輸入一遍,這是很麻煩的事情!
有沒有什么簡單的辦法呢?這就是make的功能了,請看下一篇博文。
總結
以上是生活随笔為你收集整理的【Linux】制作U-Boot烧写镜像到SD卡的过程(中篇:LDS文件)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java中 方法引用、Stream流、及
- 下一篇: (zhuan)富文本 Attribut