程序本身如何知道自身大小?這是雞生蛋還是蛋生雞的問(wèn)題
本文轉(zhuǎn)載自微信公眾號(hào)「魚(yú)鷹談單片機(jī)」,作者魚(yú)鷹Osprey。轉(zhuǎn)載本文請(qǐng)聯(lián)系魚(yú)鷹談單片機(jī)公眾號(hào)。
有些情況下,我們可能需要知道程序本身占用的空間大小,一般來(lái)說(shuō),我們可以從編譯結(jié)果中看到我們的程序到底有多大(不包含 ZI-data 部分):
還可以通過(guò)生成的bin文件大小來(lái)查看,這個(gè) bin 文件就是不需要經(jīng)過(guò)任何轉(zhuǎn)化直接燒錄到 flash 的數(shù)據(jù),當(dāng)然它也不包含 ZI-data,因?yàn)樗跏蓟?0,只需要在程序開(kāi)始時(shí)清零即可(該工作由庫(kù)函數(shù)自動(dòng)幫你完成),沒(méi)必要保存到 flash中浪費(fèi)空間。
Bin 文件生成方法(fromelf --bin !L --output hello.bin):
我們可以看一看這些數(shù)據(jù)的空間分布:
一般來(lái)說(shuō),const 聲明的函數(shù)將放在 RO-data 區(qū)。全局(或局部靜態(tài))未進(jìn)行初始化(或初始化為0)的變量放在 ZI-data 區(qū),當(dāng)然棧(stack)也會(huì)放在 ZI-data。
MDK的編譯器為我們提供了一些內(nèi)置變量,這些變量是由編譯鏈接之后自動(dòng)生成的,我們可以直接在程序中獲取,那么有哪些變量,又該如何獲取呢?
據(jù)魚(yú)鷹了解,MDK 內(nèi)置了如下變量(有些變量在有些情況下表示相同值):
- Image$$ER_IROM1$$Base;
- Image$$ER_IROM1$$Limit;
- Image$$ER_IROM1$$Length; // 獲取總大小
- Load$$LR$$LR_IROM1$$Limit; // 這個(gè)和上面的效果一樣
- Image$$ER_IROM1$$RO$$Limit; // 這個(gè)和上面的效果一樣
- Image$$RW_IRAM1$$Base;
- Image$$RW_IRAM1$$Limit;
- Image$$RW_IRAM1$$Length;
- Image$$RW_IRAM1$$ZI$$Base;
- Image$$RW_IRAM1$$ZI$$Limit;
- Image$$RW_IRAM1$$ZI$$Length;
Image$$ER_IROM1$$Length 對(duì)應(yīng)于 Code + RO Data 的大小,而 base 和 limit 為這段空間的起始和結(jié)束地址。
Image$$RW_IRAM1$$Length 對(duì)應(yīng)于 RW-Data 的大小,而 base 和 limit 為這段空間的起始和結(jié)束地址。
Image$$RW_IRAM1$$ZI$$Length 對(duì)應(yīng)于 ZI-Data(包括STACK) 的大小,而 base 和 limit 為這段空間的起始和結(jié)束地址。
那么我們?cè)撊绾问褂眠@些變量呢?下面魚(yú)鷹提供C語(yǔ)言和匯編兩個(gè)版本:
- // C語(yǔ)言
- extern int Image$$ER_IROM1$$Base;
- unsigned int base = (uint32_t)&Image$$ER_IROM1$$Base
- ; 匯編
- IMPORT |Image$$ER_IROM1$$RO$$Base|
- IMPORT |Image$$ER_IROM1$$RO$$Limit|
- IMPORT |Image$$RW_IRAM1$$RW$$Base|
- IMPORT |Image$$RW_IRAM1$$RW$$Limit|
- IMPORT |Image$$RW_IRAM1$$ZI$$Base|
- IMPORT |Image$$RW_IRAM1$$ZI$$Limit|
首先使用 extern 關(guān)鍵聲明這個(gè)外部變量,int 類(lèi)型。
但是你通過(guò)它的使用方式你會(huì)發(fā)現(xiàn),這個(gè)變量是不可以直接使用的,需要把對(duì)它進(jìn)行取地址,而它的地址才是你想要的數(shù)據(jù)。
事實(shí)上,這些內(nèi)置變量本身是不占用空間的的,和用戶聲明的變量是不同的。
我們可以這樣理解,這些變量存放在某個(gè)地址空間,這個(gè)地址就是它要表示的值(含義),但因?yàn)樗奶厥庑裕运徽加每臻g,只能采用取地址的方式獲取它代表的值。
通過(guò)這些內(nèi)置變量,原本我們計(jì)算 Code + RO-data + RW-Data 的值就可以得到 bin 文件的大小,但當(dāng)你查看 bin 文件大小之后,你會(huì)發(fā)現(xiàn) bin 文件小于該值,這是怎么回事?
通過(guò)分析 map 文件我們可以看到如下信息:
你會(huì)發(fā)現(xiàn),實(shí)際的bin文件包含的 RW 數(shù)據(jù)大小并不是 372,而是 56,也就是說(shuō),有一部分?jǐn)?shù)據(jù)并沒(méi)有被包含進(jìn) bin 文件用于拷貝(可能和 RW 的數(shù)據(jù)有部分初始值為 0 有關(guān)而被壓縮了)。
具體原因,魚(yú)鷹也沒(méi)搞懂,但是按照之前的變量來(lái)看,我們無(wú)法準(zhǔn)確獲得 bin 文件的大小,只能說(shuō)獲取到一個(gè)比 bin 文件大小稍大的數(shù)字。
原本以為魚(yú)鷹不可能獲得準(zhǔn)確的 bin 文件大小了,一個(gè)偶然的map文件查看,讓魚(yú)鷹看到了這么個(gè)變量:
好奇的魚(yú)鷹對(duì)它進(jìn)行了比較深入的研究,發(fā)現(xiàn)我需要的bin文件(程序)大小就隱藏在這里。
通過(guò)分析,魚(yú)鷹發(fā)現(xiàn)這個(gè)地址包含的8個(gè)數(shù)據(jù)含義如下:
通過(guò)圖中數(shù)據(jù),減去flash 的基地址,我們就可以獲取到 0x2FE8,即我們 bin 文件實(shí)際大小。
而另外兩個(gè)函數(shù)地址,原本魚(yú)鷹并不知道這些值是干什么用的,還是通過(guò)分析 map 文件,才最終確認(rèn)是兩個(gè)函數(shù)的地址,至于到底干什么用的,魚(yú)鷹就不是很清楚的,不過(guò)看名字也知道應(yīng)該和變量初始化有關(guān)系。
以上就是魚(yú)鷹分享的關(guān)于程序本身獲取自身大小的知識(shí)點(diǎn),至于你用這些數(shù)據(jù)干啥用那就是你的事情了。
原本魚(yú)鷹是準(zhǔn)備獲取到bin的大小后通過(guò)指定地址的方式在bin文件最后放一些數(shù)據(jù)的,但是這就真的變成雞生蛋蛋生雞的問(wèn)題了,看來(lái)通過(guò)內(nèi)置變量的方式是不行了,不知道各位道友有沒(méi)有好的方法讓編譯器自動(dòng)在 bin 文件的后面添加想要的數(shù)據(jù)呢(非第三方工具)?