程序數據諱莫如深,如何才能驗明正身
猶記得,在英特爾吊打AMD、蘋(píng)果橫踢安卓手機商的黃金年代里,每每這兩家巨頭推陳出新之際,總會(huì )被一眾吃瓜群眾冠上“擠牙膏”的標簽冷嘲熱諷一番。
似乎這哥倆明明可以在新品上驚艷絕倫,閃瞎望眼欲穿的粉絲們的雙眼,卻愣是氣沉丹田,硬憋不放,控制節奏,就這么吊住胃口,反復搜刮大家的錢(qián)包。
這還真是,周瑜打黃蓋,一個(gè)愿打,一個(gè)真的不愿挨!
因為,強大如英特爾,也得遵守它自家的摩爾定律,傲嬌如蘋(píng)果,也要考慮整個(gè)產(chǎn)業(yè)鏈的協(xié)同。不可能動(dòng)不動(dòng)甩出幾條街,像喬老爺子那樣拉風(fēng)地搞個(gè)“One more thing”!
技術(shù)文的寫(xiě)手,其實(shí)也有類(lèi)似的困擾。
如果掰開(kāi)了、揉碎了把一個(gè)問(wèn)題說(shuō)透吧,文章篇幅就像勤快婆娘的裹腳布一樣,雖然不臭但是太長(cháng)。不說(shuō)透吧,又不免給人虎頭蛇尾的既視感。
比如,山人之前就寫(xiě)過(guò)一篇文章《??煽菔蔂€ 程序存儲的空間也會(huì )變》,有一個(gè)有趣的評論說(shuō)“是不是在轉載文章的時(shí)候沒(méi)有轉全?”。
轉載?巧了不是,這不是巧了嗎不是?
山人最初有些啞然失笑,然后便是失語(yǔ),接著(zhù),便張著(zhù)空洞的大嘴巴迷思起來(lái):
技術(shù)文章到底應該怎么寫(xiě)?要寫(xiě)出所有該寫(xiě)的細節,就不能限制字數,要考慮受眾的閱讀心理,就不能寫(xiě)得過(guò)長(cháng)。
思來(lái)想去,斟酌再三,既然飯要一口一口吃,水要一口一口喝,文章吶,當然也要一篇一篇地寫(xiě)。一篇寫(xiě)不完就再寫(xiě)一篇唄,地也久,天也長(cháng),總能把該填的坑都填上。
就像鹿鼎記里,多倫色瞇瞇地對著(zhù)建寧公主“表白”:韋大人的屁股,奴才也是愿意擦的。。。
所以,今天就書(shū)接上文,就如何驗證程序數據的一致性,補充一些必要的細節。
1
要想思路不斷檔,必要的背景還是要講一件。
首先,這個(gè)世界上從來(lái)都不存在永遠不變的東西,如果有,那就是時(shí)間還不夠長(cháng)。
記得新冠疫情初初爆發(fā)后美股第一次熔斷時(shí),巴菲特老爺子出來(lái)喊話(huà):韭菜們不要慌,我八十九了,到現在只見(jiàn)過(guò)兩次熔斷。
后來(lái)的事情也頗具喜劇效果,接下來(lái)兩周內,美股接連四次熔斷,手里攥著(zhù)大把現金等著(zhù)抄底的老爺子終于明白了:
我還是太年輕了。。。
其次,雖說(shuō)人心不古,這年頭,社會(huì )人各個(gè)變臉比翻書(shū)還要快,比起來(lái),電子產(chǎn)品要可靠得多,但是,也保不齊會(huì )出現產(chǎn)品內部程度代碼突變的意外。
之前那篇“??煽菔蔂€...”的文章里,拿飛思卡爾的S19文件為例,說(shuō)明了可以通過(guò)Bootloader提取S19文件中的程序代碼,并給代碼數據加上CRC32的驗證信息。
“可以用一個(gè)PC端的軟件把程序數據的地址、內容、長(cháng)度解析出來(lái)。從PC端下載第一臺產(chǎn)品的程序,Bootloader在解析并存儲程序數據的過(guò)程中,同時(shí)對程序數據做CRC32運算,。。。程序存好了,完整性標識-CRC32運算結果也存好了。
在產(chǎn)品運行階段,上電后,在應用程序的初始化階段,讀取存儲在數據Flash中的校驗信息,根據校驗信息中的分段尺寸,讀取各個(gè)分段中的Flash數據,進(jìn)行CRC32校驗,并將計算結果和校驗信息中的CRC32校驗值進(jìn)行比對。。。”
這里面,有幾個(gè)比較關(guān)鍵的細節,前文并沒(méi)有給出詳細的解釋。
比如,MCU上電后驗證程序的一致性時(shí),要根據校驗信息中的分段尺寸讀取各個(gè)分段中的Flash數據。
為什么代碼是分段的,而不是分布在一大段連續的地址空間中?
再比如,PC端軟件怎么在S19文件中把程序數據的地址、內容、長(cháng)度解析出來(lái)?
各位看官不要慌,當哩個(gè)當,當哩個(gè)當,且容小弟慢慢講!
2
先說(shuō)說(shuō)程序代碼為啥不是一大段連續的數據,卻分成了幾段。
在MCU內部程序Flash中,程序并不像一般人所理解的那樣,一股腦地連續存放在一個(gè)有頭有尾、地址線(xiàn)性遞增的“大”段內,而是分成了好幾個(gè)分段進(jìn)行存儲,專(zhuān)業(yè)的詞匯叫SEGMENT。
真的,代碼們不是小燕子和五阿哥、紫薇和爾康,即使??煽?,石可爛,也要手牽著(zhù)手,肩并著(zhù)肩!
從地址上來(lái)說(shuō),這幾個(gè)SEGMENT可以連續,也可以不連續。另外,在一個(gè)SEGMENT內,代碼數據可能填滿(mǎn)了該分段的空間,也可能只占據該分段空間的一部分。
為啥會(huì )這樣呢?把代碼存在一大段(而不是多個(gè)分段)地址空間里,燒寫(xiě)、讀取、校驗不都是更方便嗎?
講真,山人也困惑于這個(gè)問(wèn)題好久?,F在的MCU技術(shù)完全可以做到一大段連續存儲和讀取,干嘛還分段呢?
岳不群當年練葵花寶典,抹淚揮刀自宮,那是為了練就絕世武功,可MCU如此自苦,所為何來(lái)?
帶著(zhù)探究的目的,秉持鉆研的精神,山人小小研究了一番,并形成了自己的一點(diǎn)思考,不敢藏私,與諸君分享,當然可能也說(shuō)的不對,敬請海涵哈~~
飛思卡爾MCU程序Flash的分段以16KB為單位,這也許是一個(gè)歷史的原因。
憶往昔崢嶸歲月稠,恰同學(xué)少年,風(fēng)華正茂,正熬鷹走狗,揮斥方遒,卻得學(xué)那計算機系統的大部頭。
當時(shí),教科書(shū)上還是以8086為原型講解的(暴露年齡了?沒(méi)錯,請叫我大叔?。?。說(shuō)為了讓CPU能夠尋址到1M,8086把1M地址分成16K個(gè)字節為一段,總共64個(gè)段。也許,飛思卡爾MCU里的分段機制和8086里面的分頁(yè)機制是一脈相承的吧。
總之,這可能就是一個(gè)歷史包袱,導致了目前這種別扭的局面。
外插一句話(huà),現在人們?yōu)槭裁茨敲醋放?/span>RISC-V,主要原因還不是因為它沒(méi)有那么多歷史包袱,不會(huì )為了向下兼容幾十年前的老古董而設計蹩腳的尋址方式、指令等嗎。
好了,只要程序代碼不小于16KB,那它肯定要分成幾個(gè)SEGMENT進(jìn)行存儲的,所以,當您在第一臺產(chǎn)品中通過(guò)Bootloader下載代碼的時(shí)候,需要判斷出代碼存在了哪些分段,以及在每個(gè)分段里的代碼尺寸。
還有,您千萬(wàn)不要以為,前幾個(gè)段會(huì )秉承著(zhù)“節約光榮、浪費可恥”的革命精神,存滿(mǎn)數據,實(shí)際上,那種巧合基本上是不存在的。
因為,為了保證代碼的執行效率,在存儲代碼時(shí),不會(huì )把一個(gè)函數的代碼拆開(kāi)了放到兩個(gè)段里面,所以,最有可能的情況就是,這個(gè)段還剩下100個(gè)字節的時(shí)候,如果要存儲的下一個(gè)函數的長(cháng)度超過(guò)了100個(gè)字節,這個(gè)段就收工大吉了,這個(gè)大函數得放到下一個(gè)段中。
3
行文至此,嵌入式軟件開(kāi)發(fā)人員對MCU端的疑問(wèn)應該不多了,那么,PC端呢?
PC端軟件怎么在S19文件中把程序數據的地址、內容、長(cháng)度解析出來(lái)?
山人把當年搞bootloader時(shí)下載的代碼S19文件里的兩行數據拿出來(lái),各位看官就明白了。
S12341200102040810204080003FE00000402000007FFF014000000880000008BFFF0240AE
S123414000000980000009BFFF034000000A8000000ABFFF044000000B8000000BBFFF05D9
S1表示程序數據,地址長(cháng)度為2個(gè)字節(對應于4個(gè)ASCII字符),0x23表示該行剩下的數據長(cháng)度。第一行里,0x4120為首地址,剩下的一直到0xAE之前都是程序數據,0xAE是校驗和。第二行里,0x4140為首地址,剩下的一直到0xD9之前都是程序數據,0xD9是校驗和。
在山人這個(gè)S19文件里,每一行的程序數據有0x20個(gè)字節,正好是0x23-2(地址長(cháng)度)-1(校驗和長(cháng)度)。
具體代碼實(shí)現就不用說(shuō)了吧,這點(diǎn)小活簡(jiǎn)直就是張飛吃豆芽-小菜一碟。
寫(xiě)在最后
現在的IDE把應用程序編譯、鏈接后會(huì )生成的程序數據文件并非固定一種,山人不才,知道的有hex、elf、bin、s19格式,也許還有其它的格式。
每一種格式都有自己的歷史淵源和用武之地,山人今天分享的方法里用到的是S19文件格式,其實(shí)也適用于其它格式。
現在,自己動(dòng)手寫(xiě)bootloader的人已經(jīng)很少了,后浪們也大多不用知道,自然也不知道其內部的機理了。
也許有一天,山人會(huì )寫(xiě)一篇懷舊文,好好講一講bootloader,順便講一講我們那個(gè)年代。
流水帶走了光陰的故事,也帶走了多愁善感的青春。。。
文:馬步
*博客內容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權請聯(lián)系工作人員刪除。