|
obswyrkkhd364019982856.gif (519.05 KB, 下載次數(shù): 2)
下載附件
保存到相冊
obswyrkkhd364019982856.gif
2024-9-19 12:04 上傳
引言友情提示本文接近三千字,預(yù)計閱讀時間為10-15分鐘。建議您在空閑時細(xì)細(xì)閱讀,享受閱讀的樂趣。
難以復(fù)現(xiàn)的Bug之痛你是否曾為那些難以復(fù)現(xiàn)的Bug而頭疼不已?本文將揭秘一種通過堆棧分析來定位并解決這類問題的神奇方法。
作為一名開發(fā)人員,在開發(fā)過程中會碰到各式各樣的問題,如果能通過一些操作復(fù)現(xiàn)問題的,通過對目標(biāo)板進行調(diào)試還能夠逐步分析。
但是,如果由于某些原因不能對目標(biāo)板進行調(diào)試,這種情況分析可就比較復(fù)雜了。
這不,前一陣子就碰到了一個問題,在調(diào)試過程中,怎樣都無法復(fù)現(xiàn)問題。直到后來才發(fā)現(xiàn)一個現(xiàn)象,只有在對目標(biāo)板上電時才有一定幾率復(fù)現(xiàn)問題,即頻繁的對目標(biāo)板進行斷電-上電測試。
降臣:“你難道不好奇你的功力是從何而來嘛?”
李星云:“不好奇”
降臣:“你難道不好奇為何換心之后 無法壓制這突增的內(nèi)力嗎?”
李星云:“不好奇”
降臣:“你難道不好奇為何你只能活半年?”
李星云:“不好奇”
降臣:“你好奇”
李星云:“不好奇”
降臣:“你好奇”
李星云:“不好奇”
如何對這種問題進行分析,你難道不好奇嘛?
寄存器(SP、LR)詳解這里介紹兩個寄存器。SP(Stack Pointer)寄存器和LR(Link Register)寄存器是非常重要的寄存器。
SP寄存器:堆棧的指路明燈SP寄存器用于指向當(dāng)前堆棧的棧頂位置。在函數(shù)調(diào)用時,SP寄存器會調(diào)整以反映堆棧的變化,確保數(shù)據(jù)正確地存儲和取出。
LR寄存器:函數(shù)調(diào)用與異常處理的橋梁LR寄存器有兩個作用。函數(shù)調(diào)用:當(dāng)一個函數(shù)調(diào)用另一個函數(shù)時,LR寄存器保存當(dāng)前函數(shù)的返回地址。異常處理:當(dāng)發(fā)生異常時,LR寄存器保存異常處理程序的返回地址。[/ol]問題分析與解決流程揭秘保存現(xiàn)場在處理難以復(fù)現(xiàn)的Bug時,保存現(xiàn)場數(shù)據(jù)是至關(guān)重要的一步。這就像是在犯罪現(xiàn)場拍照留證一樣,能夠為我們后續(xù)的分析提供寶貴的線索。
當(dāng)上電出現(xiàn)死機問題后,插上Jlink,使用J-Link commander軟件連接目標(biāo)板,然后通過命令將芯片RAM中的區(qū)域保存成二進制文件存放到電腦本地。
如果應(yīng)用程序的版本號不確定,也可以將ROM中的程序保存到本地,操作方法同保存RAM,后面會說到如何保存。
接下來,我們會分析RAM中的數(shù)據(jù)。
幸運的情況下,可以直接找到最后一次被調(diào)用的函數(shù),然后分析某個函數(shù)的功能,即可找到問題。而有的情況下就可能還需要根據(jù)函數(shù)的前后調(diào)用關(guān)系分析出問題所在。
分析堆棧數(shù)據(jù)我之前也不知道如何分析堆棧數(shù)據(jù),第一次分析的時候就感覺進了一個迷宮,繞著繞著就把自己繞進去了。
你可以想象一下,拿著一份二進制文件,去分析函數(shù)的調(diào)用關(guān)系,想想就腦殼疼...
分析堆棧時需要知道下面幾個知識點,才能正確分析,我接下來會解釋一下。
堆棧結(jié)構(gòu)堆棧結(jié)構(gòu)主要有四種類型,分別是滿遞減堆棧、滿遞增堆棧、空遞減堆棧和空遞增堆棧。
遞增/遞減是指棧向高地址還是低地址增長,滿是指棧指針(SP)總是指向堆棧中的最后一個元素,即最后壓入的數(shù)據(jù)?帐侵笚V羔槪⊿P)總是指向下一個將要放入數(shù)據(jù)的空位置。
常用的可能還是滿遞減堆棧比較多一點。
入棧順序因為我們要分析棧中的數(shù)據(jù),所以我們通過匯編查看依次有哪些數(shù)據(jù)入棧,然后分析出當(dāng)前的LR寄存器中的值。例如下方是一個入棧的匯編指令。我們需要知道入棧的順序,是從右往左入棧的還是從左往右入棧的。
0x08000644 B570 PUSH {r4-r6,lr}案例J-Link Commande工具首先需要安裝J-Link軟件,去官網(wǎng)https://www.segger.com/downloads/jlink/下載,這里是一個套件,安裝后會有若干個獨立的小軟件。
我們需要使用的是J-Link Commande軟件。打開軟件之后,可以輸入?來查看支持的命令,如下圖:
542w5p5lps264019982957.png (38.36 KB, 下載次數(shù): 2)
下載附件
保存到相冊
542w5p5lps264019982957.png
2024-9-19 12:04 上傳
感興趣的可以研究這些命令,會對自己有所幫助的。
分析;拘畔⑦@里需要分析的是棧屬于上面說的四種結(jié)構(gòu)的哪一種,以及數(shù)據(jù)入棧的順序是如何的。
這里我們需要將ARM仿真器連接目標(biāo)板進行調(diào)試,通過單步調(diào)試定位到PUSH匯編指令中,如下圖所示:
essdyejcdhy64019983057.png (249.22 KB, 下載次數(shù): 1)
下載附件
保存到相冊
essdyejcdhy64019983057.png
2024-9-19 12:04 上傳
當(dāng)未執(zhí)行 0x08000E0C B510 PUSH {r4,1r} 入棧操作時,當(dāng)前的棧指針SP指向0x20000658地址,并且該地址中值不為空。從而說明當(dāng)前的SP指向的是最后一個入棧的元素,即可判定為滿堆棧。
隨后我們單步執(zhí)行,讓其執(zhí)行入棧操作,再來看堆棧中的數(shù)據(jù),如下圖所示:
jggomit5w4564019983157.png (263.33 KB, 下載次數(shù): 1)
下載附件
保存到相冊
jggomit5w4564019983157.png
2024-9-19 12:04 上傳
此時我們看到SP的地址為0x20000650,執(zhí)行了入棧操作,SP的地址減小了,從而判定堆棧的生長方向是遞減的。根據(jù)上述兩個判斷從而能得出堆棧結(jié)構(gòu)為滿遞減堆棧。
接下來我們要判斷數(shù)據(jù)入棧的順序,這個匯編是將r4以及l(fā)r寄存器中的值入棧。分析入棧后的數(shù)據(jù)得知,第一個入棧的數(shù)據(jù)為0x08000DE1,剛好是lr寄存器中的值。我在圖片中也標(biāo)注了入棧數(shù)據(jù)對應(yīng)的寄存器。從而可以得出結(jié)論,入棧的順序是從右往左的,先入棧lr后入棧r4。
保存RAM數(shù)據(jù)到本地需要執(zhí)行如下幾個步驟,即可連接到目標(biāo)板。
當(dāng)系統(tǒng)死機后,需要將目標(biāo)板連接到ARM仿真器。使用管理員的身份打開J-Link Commande工具(否則后面保存數(shù)據(jù)會提示寫入文件失敗)[/ol]connect命令準(zhǔn)備連接目標(biāo)板選擇目標(biāo)芯片型號選擇調(diào)試接口JTAGSWDcJTAG選擇接口速度連接成功提示SaveBin命令保存棧數(shù)據(jù)[/ol]SaveBin c:/ram.bin 0x20000000 0x5000我這里是將整個RAM區(qū)域的數(shù)據(jù)保存起來了這里用到了一個SaveBin的命令,命令的原型如下:
SaveBin SaveBin , , Save target memory range into binary file.
如下圖所示,是我連接目標(biāo)板到保存RAM數(shù)據(jù)的所有操作,此時C盤根目錄就會出現(xiàn)一個ram.bin的文件。
cztu23sh2kq64019983257.png (61.73 KB, 下載次數(shù): 3)
下載附件
保存到相冊
cztu23sh2kq64019983257.png
2024-9-19 12:04 上傳
分析棧數(shù)據(jù)當(dāng)我們使用jlink連接目標(biāo)板后,輸入命令h可以看到一些關(guān)鍵信息,如下圖:
3kzy4girek064019983357.png (11.08 KB, 下載次數(shù): 1)
下載附件
保存到相冊
3kzy4girek064019983357.png
2024-9-19 12:04 上傳
可以看到SP(R13)= 20000654, PC = 08000E1E, IPSR = 000 (NoException),那我們先去看pc指向的地址是屬于哪一個函數(shù)的。我們可以直接在匯編窗口中輸入地址然后直接跳轉(zhuǎn)過去,如下圖所示:
leqhbvksk4v64019983457.png (47.19 KB, 下載次數(shù): 1)
下載附件
保存到相冊
leqhbvksk4v64019983457.png
2024-9-19 12:04 上傳
通過定位得知,這個地址是在InitC函數(shù)內(nèi),如下圖:
uresdldn0gz64019983557.png (73.64 KB, 下載次數(shù): 1)
下載附件
保存到相冊
uresdldn0gz64019983557.png
2024-9-19 12:04 上傳
而且這個函數(shù)剛執(zhí)行的時候,執(zhí)行了一次PUSH操作入棧了三個元素,根據(jù)之前的分析,入棧的順序是從右往左,所以第一個入棧的數(shù)據(jù)就是LR,又因為當(dāng)前的SP指針指向的地址為20000654,然后去查ram.bin數(shù)據(jù),如下圖:
treqjzxpe0l64019983657.png (8.59 KB, 下載次數(shù): 2)
下載附件
保存到相冊
treqjzxpe0l64019983657.png
2024-9-19 12:04 上傳
從而可以推斷出LR的值為0x08000E13,這里是PC+1的值,所以函數(shù)返回的實際地址為0x08000E12。然后再在跳轉(zhuǎn)到這個地址,根據(jù)上面圖片,發(fā)現(xiàn)是一個出棧三個元素的指令,同樣找到LR實際地址0x08000E02然后在跳轉(zhuǎn)到這個地址,反發(fā)仍然是一個出棧三個元素的出棧指令,同樣找到LR實際地址為0x08001046,再繼續(xù)跳轉(zhuǎn)到這個地址,發(fā)現(xiàn)是一個延時函數(shù)了如下圖:
liaot5jinsl64019983757.png (37.49 KB, 下載次數(shù): 2)
下載附件
保存到相冊
liaot5jinsl64019983757.png
2024-9-19 12:04 上傳
至此,就是通過分析棧數(shù)據(jù)分析函數(shù)的調(diào)用關(guān)系,這里寫了一個簡單的測試歷程,通過按下按鍵會執(zhí)行幾層函數(shù)調(diào)用最后進入一個死循環(huán),從而模擬死機的情況。
怎么樣,看著是不是感覺特簡單,但是在實際的開發(fā)過程中,真實情況可能比這復(fù)雜百倍。
結(jié)語為了寫這篇文章真的下了血本,我買了一個STM32的小開發(fā)板以及一個ARM仿真器,這對于原本就不富裕的我來說無疑是雪上加霜。
感謝您的耐心閱讀!如果您覺得本文有幫助,請不要吝嗇您的點贊、分享和收藏!
猜你喜歡:
WiFi6+藍牙+星閃,三合一開發(fā)板,真香!
Github上熱門 C 語言項目匯總!
嵌入式,可測試性軟件設(shè)計!
一些低功耗軟件設(shè)計的要點!
嵌入式 C 保護結(jié)構(gòu)體的方式
實用 | 10分鐘教你通過網(wǎng)頁點燈
談?wù)勄度胧杰浖募嫒菪裕?/strong> |
|