博客專欄

        EEPW首頁 > 博客 > 程序數據諱莫如深,如何才能驗明正身

        程序數據諱莫如深,如何才能驗明正身

        發布人:三德子 時間:2022-04-25 來源:工程師 發布文章

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

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

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

        因為,強大如英特爾,也得遵守它自家的摩爾定律,傲嬌如蘋果,也要考慮整個產業鏈的協同。不可能動不動甩出幾條街,像喬老爺子那樣拉風地搞個One more thing”! 

        技術文的寫手,其實也有類似的困擾。 

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

        比如,山人之前就寫過一篇文章《海可枯石可爛 程序存儲的空間也會變》,有一個有趣的評論說“是不是在轉載文章的時候沒有轉全?”。 

        轉載?巧了不是,這不是巧了嗎不是?

        這不巧了么不是.jpeg 

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

        技術文章到底應該怎么寫?要寫出所有該寫的細節,就不能限制字數,要考慮受眾的閱讀心理,就不能寫得過長。 

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

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

        所以,今天就書接上文,就如何驗證程序數據的一致性,補充一些必要的細節。

        1

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

        首先,這個世界上從來都不存在永遠不變的東西,如果有,那就是時間還不夠長。 

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

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

        我還是太年輕了。。。

        巴菲特.jpeg 

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

        之前那篇“海可枯石可爛...”的文章里,拿飛思卡爾的S19文件為例,說明了可以通過Bootloader提取S19文件中的程序代碼,并給代碼數據加上CRC32的驗證信息。 

        可以用一個PC端的軟件把程序數據的地址、內容、長度解析出來。PC端下載第一臺產品的程序,Bootloader在解析并存儲程序數據的過程中,同時對程序數據做CRC32運算,。。。程序存好了,完整性標識-CRC32運算結果也存好了。

        在產品運行階段,上電后,在應用程序的初始化階段,讀取存儲在數據Flash中的校驗信息,根據校驗信息中的分段尺寸,讀取各個分段中的Flash數據,進行CRC32校驗,并將計算結果和校驗信息中的CRC32校驗值進行比對。。。 

        這里面,有幾個比較關鍵的細節,前文并沒有給出詳細的解釋。 

        比如,MCU上電后驗證程序的一致性時,要根據校驗信息中的分段尺寸讀取各個分段中的Flash數據。 

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

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

        各位看官不要慌,當哩個當,當哩個當,且容小弟慢慢講!

        2

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

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

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

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

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

        講真,山人也困惑于這個問題好久。現在的MCU技術完全可以做到一大段連續存儲和讀取,干嘛還分段呢? 

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

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

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

        憶往昔崢嶸歲月稠,恰同學少年,風華正茂,正熬鷹走狗,揮斥方遒,卻得學那計算機系統的大部頭。 

        當時,教科書上還是以8086為原型講解的(暴露年齡了?沒錯,請叫我大叔!)。說為了讓CPU能夠尋址到1M,8086把1M地址分成16K個字節為一段,總共64個段。也許,飛思卡爾MCU里的分段機制和8086里面的分頁機制是一脈相承的吧。

        8086.jpg 

        總之,這可能就是一個歷史包袱,導致了目前這種別扭的局面。 

        外插一句話,現在人們為什么那么追捧RISC-V,主要原因還不是因為它沒有那么多歷史包袱,不會為了向下兼容幾十年前的老古董而設計蹩腳的尋址方式、指令等嗎。 

        好了,只要程序代碼不小于16KB,那它肯定要分成幾個SEGMENT進行存儲的,所以,當您在第一臺產品中通過Bootloader下載代碼的時候,需要判斷出代碼存在了哪些分段,以及在每個分段里的代碼尺寸。 

        還有,您千萬不要以為,前幾個段會秉承著節約光榮、浪費可恥”的革命精神,存滿數據,實際上,那種巧合基本上是不存在的。 

        因為,為了保證代碼的執行效率,在存儲代碼時,不會把一個函數的代碼拆開了放到兩個段里面,所以,最有可能的情況就是,這個段還剩下100個字節的時候,如果要存儲的下一個函數的長度超過了100個字節,這個段就收工大吉了,這個大函數得放到下一個段中。 

        3

        行文至此,嵌入式軟件開發人員對MCU端的疑問應該不多了,那么,PC端呢? 

        PC端軟件怎么在S19文件中把程序數據的地址、內容、長度解析出來? 

        山人把當年搞bootloader時下載的代碼S19文件里的兩行數據拿出來,各位看官就明白了。

         

        S12341200102040810204080003FE00000402000007FFF014000000880000008BFFF0240AE

        S123414000000980000009BFFF034000000A8000000ABFFF044000000B8000000BBFFF05D9

         

        S1表示程序數據,地址長度為2個字節(對應于4個ASCII字符),0x23表示該行剩下的數據長度。第一行里,0x4120為首地址,剩下的一直到0xAE之前都是程序數據,0xAE是校驗和。第二行里,0x4140為首地址,剩下的一直到0xD9之前都是程序數據,0xD9是校驗和。 

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

        具體代碼實現就不用說了吧,這點小活簡直就是張飛吃豆芽-小菜一碟。 

        寫在最后

        現在的IDE應用程序編譯、鏈接后生成程序數據文件并非固定一種,山人不才,知道的hexelfbins19格式,也許還有其它的格式。 

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

        現在,自己動手寫bootloader的人已經很少了,后浪們也大多不用知道,自然也不知道其內部的機理了。 

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

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


        文:馬步

        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: 程序Flash

        技術專區

        關閉
        主站蜘蛛池模板: 登封市| 麟游县| 汾阳市| 庐江县| 巴青县| 仪陇县| 白沙| 定西市| 禹城市| 婺源县| 静安区| 绍兴市| 永吉县| 白银市| 班玛县| 南城县| 冷水江市| 灵丘县| 罗田县| 逊克县| 东方市| 宁强县| 沽源县| 慈溪市| 喜德县| 盐池县| 平定县| 温州市| 衢州市| 民权县| 安福县| 益阳市| 武冈市| 文昌市| 桐梓县| 上思县| 芒康县| 齐河县| 读书| 佛坪县| 肇庆市|