esp8266 SDK开发之编译流程
最近剛完成自己8266的小項(xiàng)目,已經(jīng)發(fā)布在github上,有興趣的朋友可以看一下
github地址:esp-ujn
1. 通過(guò)MQTT協(xié)議與服務(wù)器交互
2. 內(nèi)置HTTP服務(wù)器,支持通過(guò)瀏覽器進(jìn)行參數(shù)配置
編譯流程分析
我們?cè)诰幾g8266代碼時(shí)可以使用項(xiàng)目中的gen_misc.sh(Windows下為gen_misc.bat)腳本,選擇合適的參數(shù)后就會(huì)在sdk/bin/文件夾中生成可燒錄的文件,如eagle.flash.bin,eagle.irom0text.bin。 但這樣存在的問(wèn)題是每次編譯時(shí)都需要選擇一遍編譯參數(shù),所以一般會(huì)使用make命令進(jìn)行編譯,如:
make COMPILE=gcc BOOT=none APP=0 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE_MAP=4這是因?yàn)?span style="margin:0px;padding:0px;">gen_misc.sh的作用僅僅是供用戶選擇編譯參數(shù),最終的編譯過(guò)程是通過(guò)make命令依據(jù)Makefile文件中定義的若干規(guī)則來(lái)進(jìn)行的。接下來(lái)通過(guò)如下幾個(gè)方面來(lái)探討整個(gè)編譯流程
一、Makefile的組織形式
SDK中Makefile文件以樹(shù)形結(jié)構(gòu)組織。總體上分為3類(lèi):主文件,項(xiàng)目配置文件,庫(kù)配置文件。
|--sdk/ |----Makefile |----project/ |------Makefile |------user/ |--------Makefile |------json/ |--------Makefile如上圖所示
- sdk/Makefile 主文件
- sdk/project/Makefile 項(xiàng)目配置文件
- sdk/project/json/Makefile?庫(kù)配置文件
平常開(kāi)發(fā)過(guò)程中,一般我們只需要關(guān)注項(xiàng)目配置文件與庫(kù)配置文件即可。如有時(shí)為了程序的模塊化,需要將不同的功能模塊編譯成獨(dú)立的庫(kù)。這時(shí)需要修改項(xiàng)目配置文件,并創(chuàng)建對(duì)應(yīng)的庫(kù)配置文件。例如我們需要添加一個(gè)json庫(kù)。這時(shí)就需要:
需要在兩個(gè)Makefile中做出的改動(dòng)如下:
#sdk/project/json/Makefile GEN_LIBS = libjson.a #庫(kù)名#sdk/project/Makefile SUBDIRS = user \json #庫(kù)目錄 COMPONENTS_eagle.app.v6 = user/libuser.a \json/libjson.a #庫(kù)路徑二、燒錄文件的生成過(guò)程
對(duì)于Non-FOTA模式,編譯完成后會(huì)在sdk/bin/目錄下生成eagle.flash.bin與eagle.irom0text.bin。顯然這兩個(gè)文件并不是編譯器的直接產(chǎn)物,一般來(lái)說(shuō)編譯器會(huì)通過(guò)我們的代碼生成一個(gè)可執(zhí)行程序。那么這兩個(gè)文件是從何而來(lái)的呢?實(shí)際上這兩個(gè)文件是編譯后生成的可執(zhí)行文件的一部分。可執(zhí)行文件被拆解成了多個(gè)部分,然后拼湊出了這兩個(gè)文件供我們燒錄。我們的代碼經(jīng)過(guò)編譯后會(huì)生成一個(gè)elf文件,它的路徑在sdk/project/.output/eagle/debug/image/eagle.app.v6.out。這個(gè)一個(gè)標(biāo)準(zhǔn)的elf文件,可以使用readelf命令查看它的一些信息。
#readelf -h ELF 頭:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 類(lèi)別: ELF32數(shù)據(jù): 2 補(bǔ)碼,小端序 (little endian)版本: 1 (current)OS/ABI: UNIX - System VABI 版本: 0類(lèi)型: EXEC (可執(zhí)行文件)系統(tǒng)架構(gòu): Tensilica Xtensa Processor版本: 0x1入口點(diǎn)地址: 0x40100004程序頭起點(diǎn): 52 (bytes into file)Start of section headers: 549292 (bytes into file)標(biāo)志: 0x300本頭的大小: 52 (字節(jié))程序頭大小: 32 (字節(jié))Number of program headers: 5節(jié)頭大小: 40 (字節(jié))節(jié)頭數(shù)量: 19字符串表索引節(jié)頭: 16#readelf -S 共有 19 個(gè)節(jié)頭,從偏移量 0x861ac 開(kāi)始: 節(jié)頭: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al[ 0] NULL 00000000 000000 000000 00 0 0 0[ 1] .data PROGBITS 3ffe8000 0000e0 000804 00 WA 0 0 16[ 2] .rodata PROGBITS 3ffe8810 0008f0 0015d0 00 A 0 0 16[ 3] .bss NOBITS 3ffe9de0 001ec0 006f18 00 WA 0 0 16[ 4] .irom0.text PROGBITS 40210000 0092c0 038b44 00 AX 0 0 16[ 5] .text PROGBITS 40100000 001ec0 0073fc 00 AX 0 0 4[ 6] .xtensa.info NOTE 00000000 041e04 000038 00 0 0 1[ 7] .comment PROGBITS 00000000 041e3c 001bbd 00 0 0 1[ 8] .debug_frame PROGBITS 00000000 0439fc 00211c 00 0 0 4[ 9] .debug_info PROGBITS 00000000 045b18 014bec 00 0 0 1[10] .debug_abbrev PROGBITS 00000000 05a704 003f8a 00 0 0 1將可執(zhí)行文件eagle.app.v6.out轉(zhuǎn)變?yōu)榭蔁浳募倪^(guò)程定義在sdk/Makefile,也就是在主文件中。大體流程如下:
#將.text、.data、.rodata和.irom0.text節(jié)的數(shù)據(jù)轉(zhuǎn)存到文件 objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin #通過(guò)eagle.app.v6.text.bin、eagle.app.v6.data.bin和eagle.app.v6.rodata.bin生成eagle.app.flash.bin python sdk/tools/gen_appbin.py eagle.app.v6.text.bin eagle.app.v6.data.bin eagle.app.v6.rodata.bin #將最后生成的可燒錄文件放到sdk/bin/目錄下 mv eagle.app.flash.bin sdk/bin/eagle.flash.bin mv eagle.app.v6.irom0text.bin sdk/bin/eagle.irom0text.bin通過(guò)上邊readelf -S獲取到的節(jié)區(qū)表信息可以看到,實(shí)際在內(nèi)存中出現(xiàn)的節(jié)只有.text、.data、.bss、.rodata和.irom0.text。
.text + .data + .rodata? =>?eagle.flash.bin
.irom0.text?=> eagle.irom0text.bin
通過(guò)比較這幾個(gè)節(jié)的大小與燒錄文件大小的關(guān)系可以得到相同的結(jié)果(eagle.flash.bin文件中除了包含程序節(jié)數(shù)據(jù),還有少量的配置數(shù)據(jù))。.bss節(jié)雖然在內(nèi)存中出現(xiàn)但是在程序初始化時(shí)會(huì)被整個(gè)清零,所以不必出現(xiàn)在燒錄文件中。這幾個(gè)節(jié)包含的數(shù)據(jù)內(nèi)容如下:
| 節(jié)名 | 作用 |
| .text | 存放代碼 |
| .data | 存放已初始化的全局變量 |
| .bss | 存放未初始化的全局變量 |
| .rodata | 存放只讀數(shù)據(jù) |
| .irom0.text | 存放標(biāo)注有ICACHE_FLASH_ATTR的代碼或ICACHE_RODATA_ATTR的變量 |
三、Makefile的執(zhí)行過(guò)程
在前邊已經(jīng)提到,我們寫(xiě)的代碼最終會(huì)編譯為一個(gè)elf格式的可執(zhí)行文件(eagle.app.v6.out),接下我們通過(guò)具體Makefile文件中的代碼對(duì)整個(gè)編譯的執(zhí)行過(guò)程進(jìn)行分析。之前講到sdk/Makefile為主文件,也就是所有編譯時(shí)用到的邏輯都在其中定義。我們整個(gè)的編譯流程中需要按順序產(chǎn)生如下幾類(lèi)目標(biāo):二進(jìn)制目標(biāo)文件、庫(kù)文件、elf文件、燒錄文件。那么如何通過(guò)一個(gè)主Makefile來(lái)完成這些工作呢,這里需要先看一下其余兩類(lèi)起配置作用的Makefile。這兩類(lèi)Makefile的最后都會(huì)有如下代碼:
PDIR := ../$(PDIR) sinclude $(PDIR)Makefile它的作用是包含自己父文件夾中的Makefile文件,那么最后主Makefile文件中的內(nèi)容會(huì)被包含到庫(kù)配置文件與項(xiàng)目配置文件中。在項(xiàng)目配置文件中,它的作用是產(chǎn)生elf可執(zhí)行文件與燒錄文件,在庫(kù)配置文件中,它的作用是產(chǎn)生靜態(tài)鏈接庫(kù)。主Makefile中,最主要的顯式規(guī)則如下,通過(guò)這兩條規(guī)則產(chǎn)生了所有我們需要的文件。
...314 all: .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
... 324 .subdirs: 325 @set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);)
這兩條規(guī)則的目標(biāo)都是偽目標(biāo),并不會(huì)產(chǎn)生任何文件,我們所需要的所有文件都是目標(biāo)all的依賴文件。
| $(OBJS) | 二進(jìn)制目標(biāo)文件 |
| $(OLIBS) | 靜態(tài)鏈接庫(kù) |
| $(OIMAGES) | elf可執(zhí)行文件 |
| $(OBINS) | 燒錄文件 |
| $(SPECIAL_MKTARGETS) | 一直為空 |
第一個(gè)依賴文件.subdirs是一個(gè)偽目標(biāo),也就是每次編譯時(shí)都會(huì)先執(zhí)行.subdirs中定義的操作,也就是遍歷所有含Makefile文件的子文件夾,執(zhí)行make命令。通過(guò)這種方式產(chǎn)生的結(jié)果就是make工具的當(dāng)前路徑發(fā)生了改變。拿上邊列出的項(xiàng)目結(jié)構(gòu)為例,我們一次編譯過(guò)程可分為
這時(shí)如果執(zhí)行了sdk/project/json/Makefile,那么此時(shí)make工具的當(dāng)前路徑變?yōu)榱?span style="margin:0px;padding:0px;">sdk/project/json/。此時(shí)sdk/project/json/Makefile對(duì)上層Makefile進(jìn)行包含后再次構(gòu)建目標(biāo)all。一般來(lái)說(shuō)sdk/project/json/中不會(huì)再有包含Makefile的子文件夾,那么此時(shí)目標(biāo)all的第一個(gè)依賴項(xiàng).subdirs會(huì)立刻返回,然后再對(duì)其余的依賴項(xiàng)進(jìn)行構(gòu)建。
還有一點(diǎn)需要說(shuō)明的是目標(biāo)all的依賴項(xiàng)并不是全都有值的,比如$(SPECIAL_MKTARGETS)的值就一直為空,表示不存在此依賴項(xiàng)。繼續(xù)拿上邊的項(xiàng)目結(jié)構(gòu)舉例:
| make當(dāng)前路徑 | $(OBJS) | $(OLIBS) | $(OIMAGES) | $(OBINS) |
| sdk/project/ | 空 | 空 | eagle.app.v6.out | eagle.app.v6.bin |
| sdk/project/json/ | json.o | libjson.a | 空 | 空 |
根據(jù)make當(dāng)前路徑的不同,目標(biāo)all有不同的依賴項(xiàng),然后再根據(jù)主Makefile中定義隱式規(guī)則對(duì)依賴項(xiàng)進(jìn)行構(gòu)建,即完成了整個(gè)項(xiàng)目的構(gòu)建過(guò)程。
?
有的朋友可能對(duì)Makefile的語(yǔ)法不熟悉,這里推薦一個(gè)網(wǎng)站
https://www.gnu.org/s/make/manual/make.html
官方的教程很詳細(xì)
總結(jié)
以上是生活随笔為你收集整理的esp8266 SDK开发之编译流程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: buntu linux下建立stm32开
- 下一篇: Keil生成汇编文件、bin文件