博客專欄

EEPW首頁 > 博客 > 程序數(shù)據(jù)諱莫如深,如何才能驗(yàn)明正身

程序數(shù)據(jù)諱莫如深,如何才能驗(yàn)明正身

發(fā)布人:三德子 時(shí)間:2022-04-25 來源:工程師 發(fā)布文章

猶記得,在英特爾吊打AMD、蘋果橫踢安卓手機(jī)商的黃金年代里,每每這兩家巨頭推陳出新之際,總會(huì)被一眾吃瓜群眾冠上“擠牙膏”的標(biāo)簽冷嘲熱諷一番。 

似乎這哥倆明明可以在新品上驚艷絕倫,閃瞎望眼欲穿的粉絲們的雙眼,卻愣是氣沉丹田,硬憋不放,控制節(jié)奏,就這么胃口,反復(fù)搜刮大家的錢包。 

這還真是,周瑜打黃蓋,一個(gè)愿打,一個(gè)真的不愿挨! 

因?yàn)?,?qiáng)大如英特爾,也得遵守它自家的摩爾定律,傲嬌如蘋果,也要考慮整個(gè)產(chǎn)業(yè)鏈的協(xié)同。不可能動(dòng)不動(dòng)甩出幾條街,像喬老爺子那樣拉風(fēng)地搞個(gè)One more thing”! 

技術(shù)文的寫手,其實(shí)也有類似的困擾。 

如果掰開了、揉碎了把一個(gè)問題說透吧,文章篇幅就像勤快婆娘的裹腳布一樣,雖然不臭但是太長。不說透吧,又不免給人虎頭蛇尾的既視感。 

比如,山人之前就寫過一篇文章《海可枯石可爛 程序存儲(chǔ)的空間也會(huì)變》,有一個(gè)有趣的評(píng)論說“是不是在轉(zhuǎn)載文章的時(shí)候沒有轉(zhuǎn)全?”。 

轉(zhuǎn)載?巧了不是,這不是巧了嗎不是?

這不巧了么不是.jpeg 

山人最初有些啞然失笑,然后便是失語,接著,便張著空洞的大嘴巴迷思起來: 

技術(shù)文章到底應(yīng)該怎么寫?要寫出所有該寫的細(xì)節(jié),就不能限制字?jǐn)?shù),要考慮受眾的閱讀心理,就不能寫得過長。 

思來想去,斟酌再三,既然飯要一口一口吃,水要一口一口喝,文章吶,當(dāng)然也要一篇一篇地寫。一篇寫不完就再寫一篇唄,地也久,天也長,總能把該填的坑都填上。 

就像鹿鼎記里,多倫色瞇瞇地對(duì)著建寧公主“表白”:韋大人的屁股,奴才也是愿意擦的。。。 

所以,今天就書接上文,就如何驗(yàn)證程序數(shù)據(jù)的一致性,補(bǔ)充一些必要的細(xì)節(jié)。

1

要想思路不斷檔,必要的背景還是要講一件。 

首先,這個(gè)世界上從來都不存在永遠(yuǎn)不變的東西,如果有,那就是時(shí)間還不夠長。 

記得新冠疫情初初爆發(fā)后美股第一次熔斷時(shí),巴菲特老爺子出來喊話:韭菜們不要慌,我八十九了,到現(xiàn)在只見過兩次熔斷。 

后來的事情也頗具喜劇效果,接下來兩周內(nèi),美股接連四次熔斷,手里攥著大把現(xiàn)金等著抄底的老爺子終于明白了: 

我還是太年輕了。。。

巴菲特.jpeg 

其次,雖說人心不古,這年頭,社會(huì)人各個(gè)變臉比翻書還要快,比起來,電子產(chǎn)品要可靠得多,但是,也保不齊會(huì)出現(xiàn)產(chǎn)品內(nèi)部程度代碼突變的意外。 

之前那篇“??煽菔蔂€...”的文章里,拿飛思卡爾的S19文件為例,說明了可以通過Bootloader提取S19文件中的程序代碼,并給代碼數(shù)據(jù)加上CRC32的驗(yàn)證信息。 

可以用一個(gè)PC端的軟件把程序數(shù)據(jù)的地址、內(nèi)容、長度解析出來。PC端下載第一臺(tái)產(chǎn)品的程序,Bootloader在解析并存儲(chǔ)程序數(shù)據(jù)的過程中,同時(shí)對(duì)程序數(shù)據(jù)做CRC32運(yùn)算,。。。程序存好了,完整性標(biāo)識(shí)-CRC32運(yùn)算結(jié)果也存好了。

在產(chǎn)品運(yùn)行階段,上電后,在應(yīng)用程序的初始化階段,讀取存儲(chǔ)在數(shù)據(jù)Flash中的校驗(yàn)信息,根據(jù)校驗(yàn)信息中的分段尺寸,讀取各個(gè)分段中的Flash數(shù)據(jù),進(jìn)行CRC32校驗(yàn),并將計(jì)算結(jié)果和校驗(yàn)信息中的CRC32校驗(yàn)值進(jìn)行比對(duì)。。。 

這里面,有幾個(gè)比較關(guān)鍵的細(xì)節(jié),前文并沒有給出詳細(xì)的解釋。 

比如,MCU上電后驗(yàn)證程序的一致性時(shí),要根據(jù)校驗(yàn)信息中的分段尺寸讀取各個(gè)分段中的Flash數(shù)據(jù)。 

為什么代碼是分段的,而不是分布在一大段連續(xù)的地址空間中? 

再比如,PC端軟件怎么在S19文件中把程序數(shù)據(jù)的地址、內(nèi)容、長度解析出來? 

各位看官不要慌,當(dāng)哩個(gè)當(dāng),當(dāng)哩個(gè)當(dāng),且容小弟慢慢講!

2

先說說程序代碼為啥不是一大段連續(xù)的數(shù)據(jù),卻分成了幾段。 

MCU內(nèi)部程序Flash中,程序并不像一般人所理解的那樣,一股腦地連續(xù)存放在一個(gè)有頭有尾、地址線性遞增的“大”段內(nèi),而是分成了好幾個(gè)分段進(jìn)行存儲(chǔ),專業(yè)的詞匯叫SEGMENT。 

真的,代碼們不是小燕子和五阿哥、紫薇和爾康,即使??煽?,石可爛,也要手牽著手,肩并著肩! 

從地址上來說,這幾個(gè)SEGMENT可以連續(xù),也可以不連續(xù)。另外,在一個(gè)SEGMENT內(nèi),代碼數(shù)據(jù)可能填滿了該分段的空間,也可能只占據(jù)該分段空間的一部分。 

為啥會(huì)這樣呢?把代碼存在一大段(而不是多個(gè)分段)地址空間里,燒寫、讀取、校驗(yàn)不都是更方便嗎? 

講真,山人也困惑于這個(gè)問題好久?,F(xiàn)在的MCU技術(shù)完全可以做到一大段連續(xù)存儲(chǔ)和讀取,干嘛還分段呢? 

岳不群當(dāng)年練葵花寶典,抹淚揮刀自宮,那是為了練就絕世武功,可MCU如此自苦,所為何來? 

帶著探究的目的,秉持鉆研的精神,山人小小研究了一番,并形成了自己的一點(diǎn)思考,不敢藏私,與諸君分享,當(dāng)然可能也說的不對(duì),敬請(qǐng)海涵哈~~ 

飛思卡爾MCU程序Flash的分段以16KB為單位,這也許是一個(gè)歷史的原因。 

憶往昔崢嶸歲月稠,恰同學(xué)少年,風(fēng)華正茂,正熬鷹走狗,揮斥方遒,卻得學(xué)那計(jì)算機(jī)系統(tǒng)的大部頭。 

當(dāng)時(shí),教科書上還是以8086為原型講解的(暴露年齡了?沒錯(cuò),請(qǐng)叫我大叔!)。說為了讓CPU能夠?qū)ぶ返?M,8086把1M地址分成16K個(gè)字節(jié)為一段,總共64個(gè)段。也許,飛思卡爾MCU里的分段機(jī)制和8086里面的分頁機(jī)制是一脈相承的吧。

8086.jpg 

總之,這可能就是一個(gè)歷史包袱,導(dǎo)致了目前這種別扭的局面。 

外插一句話,現(xiàn)在人們?yōu)槭裁茨敲醋放?/span>RISC-V,主要原因還不是因?yàn)樗鼪]有那么多歷史包袱,不會(huì)為了向下兼容幾十年前的老古董而設(shè)計(jì)蹩腳的尋址方式、指令等嗎。 

好了,只要程序代碼不小于16KB,那它肯定要分成幾個(gè)SEGMENT進(jìn)行存儲(chǔ)的,所以,當(dāng)您在第一臺(tái)產(chǎn)品中通過Bootloader下載代碼的時(shí)候,需要判斷出代碼存在了哪些分段,以及在每個(gè)分段里的代碼尺寸。 

還有,您千萬不要以為,前幾個(gè)段會(huì)秉承著節(jié)約光榮、浪費(fèi)可恥”的革命精神,存滿數(shù)據(jù),實(shí)際上,那種巧合基本上是不存在的。 

因?yàn)?,為了保證代碼的執(zhí)行效率,在存儲(chǔ)代碼時(shí),不會(huì)把一個(gè)函數(shù)的代碼拆開了放到兩個(gè)段里面,所以,最有可能的情況就是,這個(gè)段還剩下100個(gè)字節(jié)的時(shí)候,如果要存儲(chǔ)的下一個(gè)函數(shù)的長度超過了100個(gè)字節(jié),這個(gè)段就收工大吉了,這個(gè)大函數(shù)得放到下一個(gè)段中。 

3

行文至此,嵌入式軟件開發(fā)人員對(duì)MCU端的疑問應(yīng)該不多了,那么,PC端呢? 

PC端軟件怎么在S19文件中把程序數(shù)據(jù)的地址、內(nèi)容、長度解析出來? 

山人把當(dāng)年搞bootloader時(shí)下載的代碼S19文件里的兩行數(shù)據(jù)拿出來,各位看官就明白了。

 

S12341200102040810204080003FE00000402000007FFF014000000880000008BFFF0240AE

S123414000000980000009BFFF034000000A8000000ABFFF044000000B8000000BBFFF05D9

 

S1表示程序數(shù)據(jù),地址長度為2個(gè)字節(jié)(對(duì)應(yīng)于4個(gè)ASCII字符),0x23表示該行剩下的數(shù)據(jù)長度。第一行里,0x4120為首地址,剩下的一直到0xAE之前都是程序數(shù)據(jù),0xAE是校驗(yàn)和。第二行里,0x4140為首地址,剩下的一直到0xD9之前都是程序數(shù)據(jù),0xD9是校驗(yàn)和。 

在山人這個(gè)S19文件里,每一行的程序數(shù)據(jù)有0x20個(gè)字節(jié),正好是0x23-2(地址長度)-1(校驗(yàn)和長度)。 

具體代碼實(shí)現(xiàn)就不用說了吧,這點(diǎn)小活簡直就是張飛吃豆芽-小菜一碟。 

寫在最后

現(xiàn)在的IDE應(yīng)用程序編譯、鏈接后會(huì)生成程序數(shù)據(jù)文件并非固定一種,山人不才,知道的hexelf、bins19格式,也許還有其它的格式。 

每一種格式都有自己的歷史淵源和用武之地,山人今天分享的方法里用到的是S19文件格式,其實(shí)也適用于其它格式。 

現(xiàn)在,自己動(dòng)手寫bootloader的人已經(jīng)很少了,后浪們也大多不用知道,自然也不知道其內(nèi)部的機(jī)理了。 

也許有一天,山人會(huì)寫一篇懷舊文,好好講一講bootloader,順便講一講我們那個(gè)年代。 

流水帶走了光陰的故事,也帶走了多愁善感的青春。。。


文:馬步

*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



關(guān)鍵詞: 程序Flash

技術(shù)專區(qū)

關(guān)閉