天天看點

"double 強轉 uint64"程式飛了,損失慘重!

來自公衆号::最後一個bug

1

情景 

售後 : X工,現場出大事了,今天更新的程式跑着跑着就挂了!現在整個産線都等着這個裝置恢複,能安排個人過來支援下嗎?

bug菌 : my god !别慌,我問一下負責的A工。

bug菌 : 喂,A工,昨天更新的程式有問題,程式卡死,售後在現場你聯系一下,支援他一波,順便把程式發送給我一份,一起看看!

A工 : 啊,還有這種事,程式沒改什麼呀,行,我跟售後聯系一下。

經過一番折騰,發現由于程式測試不到位,導緻了一個強制類型轉化引發的進入異常,這裡就分享給大家。

2

bug示範 

這是一個老項目,采用stm32F4晶片為主要,由于硬體限制而客戶又不願意花大價錢改造,是以程式架構等等都沒有再大動作,由于通信上的傳輸和解析都是位元組流,一些小的需求都隻是在原來的通信架構上把4個位元組拆成2個位元組來用,然而這一次實在沒辦法沒改接受資料類型,然後把一個double類型拆成了4個uint16來使用,沒想到出問題了。

公司代碼加密,是以這裡簡單的模拟示範了一下:

"double 強轉 uint64"程式飛了,損失慘重!
"double 強轉 uint64"程式飛了,損失慘重!

A工用一個double類型取位址,然後把位址強制轉為uint64_t類型,以此類型指針取内容,當這段代碼執行完程式就跳到了異常中斷,導緻當機。

其實這段代碼對于經驗豐富的人來說,一看就覺得很變扭,但是無論如何也不至于當機呀,畢竟強制類型轉化大部分人拿來都是随便用。

3

bug解讀 

當看到A工寫的這一套代碼,bug菌其實隐隐約約就感覺這塊有些問題,但是沒敢确定,畢竟整套代碼也是前人留下的,全是邏輯沒什麼精華也沒有過細研究,最後看這段代碼的彙編才知道問題所在。

在之前bug菌也曾比較詳細的出過一篇分析此類問題的文章,可能這一塊并沒有吸引到你,不過還是一句話:"出來混都是要還的!"。

☞聽說因為代碼沒"對齊"程式就奔了?(深度剖析)

其實問題就出在LDRD這個ARM彙編指令上,LDRD指令表示從指定記憶體位址取double word,上面圖檔代碼中的LDRD R0,R1,[R2,#0x2EC],可以分解為下面兩個ldr步驟 : 

"double 強轉 uint64"程式飛了,損失慘重!

在ARM彙編指令集中LDRD和STRD是一對加載和提取指令,一般都需要使用__align(8)修飾來保證資料對象進行8直接對齊,而使用#pragma pack(8)是來指定結構體成員變量相對于第一個變量的位址的偏移量的對齊方式。

__align訓示編譯器在 n 位元組邊界上對齊變量,是一個存儲類修飾符,當然也可以以讓2位元組的對象進行4位元組對齊其與8位元組對齊是等價的,一定要記得是存儲的起始位址為8的整數倍。

對齊可以在一定程度上提高資料提取的效率,一旦起始位址沒有對齊會導緻對齊錯誤,是以上面的double浮點類型的結構體變量沒有8位元組位址對齊,當進行強制類型轉化并使用LDRD指令就導緻未對齊故障。

更專業點 

當然對于跳轉到硬體異常的故障是非常好排查的,下面這篇文章教你如何迅速的定位故障位置和故障資訊 :

☞【收藏】get這些技巧,HardFault_Handler排查隻需要幾分鐘

對于非對齊指令的執行會導緻指令用法上的故障,那麼Cortex晶片中相應的故障寄存器标志位會置位。

"double 強轉 uint64"程式飛了,損失慘重!
"double 強轉 uint64"程式飛了,損失慘重!

以上來自于Cortex技術文檔,文檔中也寫得非常的詳細。

當CPU嘗試做一個未對齊的記憶體通路,然後就會發生此錯誤。特别是對于未對齊的LDM/STM/LDRD/STRD指令,是以進入異常中斷以後查詢晶片内部故障寄存器也是可以找到問題所在的,對于使用仿真器排查是再簡單不過了,如果是離線排查就需要進行上篇文章那樣列印相關日志來定位問題。

本文到此結束!

c