如何證明一個靜態庫沒有使用 malloc

今天我還做了另外一個有趣的事情:

有一個客戶要求我們提供的一個 libxxx.a 文件不能使用 malloc 動態分配內存。研發排查了下代碼都沒有用到 malloc。

但是客戶說他們還是可以看到 malloc 符號,但是他們把malloc的實現hook成空的也能跑。所以他們相信我們應該沒有調用malloc,但是需要我們保證我們的程序沒有使用malloc。

產品找到我老大,我老大讓我幫忙給分析下怎麼證明。我知道這個事情有經驗的應該是朋友TT 。諮詢了下 TT 說可以看導入符號表,但是Linux的沒搞過。

我問了GPT,有 nm -D 和 objdump -D 兩個命令可以用。於是我就去看了下我們的 libxxx.a 用法是 nm -D libxxx.a |grep malloc 或者 objdump -D libxxx.a | grep malloc

但是 nm 好像看不出來,nm 只能打印頭文件裏有直接用的,例如 xxx.h 和 xxx.cpp

nm 只能看到導出符號好像。在 xxx.cpp 的實現函數內調用的符號,通過 objdump 可以看到。

但是我想要進一步分析下到底是哪裏引入了 malloc。我寫了一個很簡單的 test.cpp 把 xxx.h 引入,鏈接 libxxx.a 然後執行

gcc -E -P test.cpp > test.full.cpp

在 test.full.cpp 裏就可以直接看到 malloc 函數的實現代碼,這是什麼原理呢?原理是 gcc -E 只做預處理,相當於把所有 include 都展開了,於是 malloc 無論是在哪裏定義的,都會被展開出來。

那麼下面就好辦了,通過2分註釋方式,很快排查到 #include <cmath> 居然導致了 malloc 符號的引入,cmath 和malloc 居然有關係!

怎麼解決呢?我們只用了一個函數 powf,可以直接用 GCC 內置的 __builtin_powf 函數,實際上GCC的 cmath內部也是層層判斷最後調用 GCC的某個 __builtin_powf實現。這在都用GCC的時候是安全的。

同時沒有 #include <cmath> 會導致 int32_t 之類的符號爲定義,這個簡單,使用 #include <stdint.h>

這個問題解決了後。我又進一步提供了一個證明庫如果不做任何改動,沒有使用 malloc 的證明思路:

a) 在 xxx.h 裏添加一個 test函數
b) 在 xxx.cpp 裏實現 test,在裏面調用 malloc,同時添加一個 print 使用malloc的結果,這是因爲如果不使用可能編譯就被優化掉了。
c)在 test.cpp 裏使用 test方法
d)編譯後通過 objdump -D test | grep malloc 可以看到
e)在 test.cpp 裏註釋掉對 test 的調用。
f)再次編譯後再通過 objdump -D test | grep malloc 查看,這下看不到了。
g)這就證明了,即使靜態庫實現代碼有 malloc ,只要最終的可執行程序沒有調用,通過靜態分析就可以判斷是否 malloc 被調用了。
h)這樣就可以通過對 libxxx.a 在使用前後 objdump -D 結果是否有 malloc 來證明 libxxx.a 確實沒有使用 malloc.

--end--

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章