做嵌入式Linux開發(fā),最熟悉的莫過于交叉編譯了。
1. 編譯器
??嵌入式開發(fā)和桌面應(yīng)用的一個很大不同就是:我們必須自己準備配置所需的工具環(huán)境。并不像Windows開發(fā)那樣裝一個VS就一切OK了,這其中最重要的就是編譯器的準備。
- 在Ubuntu上我一般是使用
sudo apt-get install arm-linux-gnu
命令進行交叉編譯工具的安裝。但大多數(shù)時候這是行不通的。因為arm架構(gòu)或者特定ARM芯片的特性(如是否支持浮點運算),導(dǎo)致后期使用時故障頻出。 - 使用開發(fā)板提供商或芯片提供商提供的開發(fā)套件,這些往往對某些特性做了優(yōu)化,是最能契合我們的芯片開發(fā)的一種方式。
- 去Linaro下載對應(yīng)架構(gòu)的編譯程序,這些開發(fā)環(huán)境適用性很好,在沒能找到官方提供的套件的時候,這是一個很好的選擇。
??當(dāng)然,下載安裝完成后需要將可執(zhí)行文件的路徑加到系統(tǒng)的 PATH
路徑中
2.交叉編譯器選項
??編譯程序分為4個步驟:
-
1.預(yù)處理,生成預(yù)編譯文件(.文件):
Gcc –E hello.c –o hello.i
-
2.編譯,生成匯編代碼(.s文件):
Gcc –S hello.i –o hello.s
-
3.匯編,生成目標(biāo)文件(.o文件):
Gcc –c hello.s –o hello.o
-
4.鏈接,生成可執(zhí)行文件:
Gcc hello.o –o hello
2.2 警告選項
??在默認情況下,警告選項是默認不打開的,后來Dock在開發(fā)的實踐過程中,返現(xiàn)使用-Wall
選項可以事先發(fā)現(xiàn)很多簡單錯誤,為后期免去很多麻煩:
- 判斷語句
if(a = b)
- 缺少
default
分支 - 類型不匹配對比
int a = 0; long b = 2; if(a == b)
- 其他Dock還未遇到的
??就是這三個簡單的錯誤,曾經(jīng)讓Dock花費很多時間去調(diào)試。錯誤應(yīng)該消滅在萌芽。
2.3 包含鏈接選項
??Gcc編譯器默認是會自動尋找包含編譯環(huán)境中的頭文件和鏈接庫,但是在使用自己的頭文件和鏈接庫時,需要自己手動指定。
- -I[path-to-include_file] 使用 -I 指定頭文件的路徑
- -L[path-to-lib] 使用-L 指定庫文件的路徑
- -lxx.so 使用-l 選項指定要鏈接的庫文件,默認 l代替lib文件,如鏈接libmath.so要使用
-lmath
- -nostartfiles 不鏈接啟動文件,即暫時不鏈接
main
函數(shù) - -nostdlib 不鏈接標(biāo)準庫文件,在裸機程序中比較常用,如uboot中就會使用到這個選項,因為鏈接標(biāo)準庫的話,程序就會變得很大。
- -static 靜態(tài)鏈接,這樣就不會使用動態(tài)庫,但后邊有時需要制定 libxx.a靜態(tài)庫文件,同時文件體積會變得很大
2.4 objcpy objdump
??雖然說能夠編譯出程序并且能夠運行就已經(jīng)夠了,但是這兩個程序使我們做嵌入式程序所不能忽視的。
2.4.1 ELF格式
ELF(Executable and Linking Format)是unix-like系統(tǒng)下的一種文件格式,它是一種對象文件的格式,用于定義不同類型的對象文件(Object files)中都放了什么東西、以及都以什么樣的格式去放這些東西。即是在程序的頭部加上了一段信息:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x42dfe0
Start of program headers: 64 (bytes into file)
Start of section headers: 67460488 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 8
Size of section headers: 64 (bytes)
Number of section headers: 40
Section header string table index: 37
對應(yīng)的結(jié)構(gòu)體為:
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* 魔數(shù)和相關(guān)信息 */
Elf32_Half e_type; /* 目標(biāo)文件類型 */
Elf32_Half e_machine; /* 硬件體系 */
Elf32_Word e_version; /* 目標(biāo)文件版本 */
Elf32_Addr e_entry; /* 程序進入點 */
Elf32_Off e_phoff; /* 程序頭部偏移量 */
Elf32_Off e_shoff; /* 節(jié)頭部偏移量 */
Elf32_Word e_flags; /* 處理器特定標(biāo)志 */
Elf32_Half e_ehsize; /* ELF頭部長度 */
Elf32_Half e_phentsize; /* 程序頭部中一個條目的長度 */
Elf32_Half e_phnum; /* 程序頭部條目個數(shù) */
Elf32_Half e_shentsize; /* 節(jié)頭部中一個條目的長度 */
Elf32_Half e_shnum; /* 節(jié)頭部條目個數(shù) */
Elf32_Half e_shstrndx;
??但是在uboot等環(huán)境中,是無法識別這些信息的。裸機程序總是從頭一條一條指令的進行執(zhí)行。所以在有些情況下我們需要去掉這些信息。那就用到了objcopy命令:
objcopy用于將object的部分獲全部內(nèi)容拷貝到另一個object,從而可以實現(xiàn)格式的變換。
如 arm-linux-gnu-objcopy -O binary boot.elf boot.bin
就常用來將elf轉(zhuǎn)換為RAW
格式,從而在裸機上運行。
2.4.2 objdump
??objdumpb即是常用的反匯編程序,Dock常用的兩條命令為:
-
arm--linux-objdump -d boot.elf
將 elf反匯編 -
arm--linux-objdump -d -b binary -m arm boot.bin
將 bin反匯編