1. 程式人生 > >深入解析用rpmbuild 打rpm包時引起的打包前後文件大小變化的問題

深入解析用rpmbuild 打rpm包時引起的打包前後文件大小變化的問題

源出處

緣起

你能看到的只是表面很少的一部分——冰山理論。

在程式設計排查問題時,我們經常遇到的情形就是,看似一個很小的點,深究起來卻發現其背後隱藏著更深層次的技術邏輯。昨天的上線經歷讓我深有體會。

昨日上線,一切如舊。雲淡風輕,茶香四溢。

焚香,沐浴,更衣,食素三日之後總該一切無憂了吧。

用 RPM 包在線上解包完成,輕泯了一口茶水,正準備手起鍵落重啟服務。電光火石之間,感覺命運之神輕拍了一下我的肩膀:“還差點什麼吧?” “額,好像是的,重啟之前最好再得對比下檔案內容,確保傳輸過程沒問題”。例行公事而已,簡直有些畫蛇添足。

然而,對比檔案內容之後,卻發現 RPM 解包後得到的可執行檔案和打包前檔案的 md5 不一樣。

前行

RPM 打包沒報錯,傳輸沒報錯,解包沒報錯。

看看檔案大小,打包前可執行程式14M,但包中解出來的卻只有900多 K,相差甚多。

這是打包前:

這是解包後:

處理過程沒報錯,但是檔案內容不一樣,一定是沒看好,傳錯RPM包了。重新檢查 spec 檔案,重新打包,重新傳輸,每一步都小心翼翼。

解包之前先對比下RPM包的md5 吧。很好,這次傳對了,兩個RPM 包一樣,剛才一定是傳錯檔案了。

解包,品茗,手起鍵落,準備重啟。又一次電光火石,感覺肩膀又被命運之神拍了一下。“好吧,再給你個面子”,對比一下可執行檔案。

歷史總是驚人的相似,解包後還是912 k。打包前14M,解包後大小縮減了這麼多。

再來:打包沒報錯,傳輸沒報錯,解包沒報錯。但解出來程式卻變小了,一定是幻覺。又對比了兩次,幻覺依舊。

你信或不信,事實就在那裡。各個過程都沒錯,可是就變小了。思考邏輯與事實的衝突,恐怕是最折磨人的吧。

再啟航

過程沒錯,但檔案卻大小變化了,心中默唸多次之後,突然想到:誰說檔案變小了一定是錯誤?

對啊,變小不一定是錯誤。那一定是誰在某個過程中做了什麼手腳。上面已經驗證,傳輸前後RPM包內容一樣,那隻能是RPM在打包或解包時做了什麼操作了,並且rpm 在做這個操作時沒有報告。

決定從就從打包過程開始排查。打包輸出資訊中有這麼幾行:

strip,objdump,我們好像在哪見過。依稀記得strip 好像是用來去掉可執行檔案中除錯資訊的,objdump 是用來處理可執行檔案的。正好我程式中含有除錯資訊,那麼會不會是rpm 呼叫的strip 把除錯資訊自作主張的去掉了呢?畢竟rpm 包通常是release 版本,除錯資訊的作用不大,自動刪除也是有可能的。

曙光初現。用file 命令看一下解包後的檔案資訊:

”not stripped“ ,檔案沒有被strip。欲哭無淚,不禁想起了一句話—— 前途是光明的,道路是曲折的。

除錯資訊還在,那變小的原因是什麼?你到底少了什麼?!

調整心情,從頭再來,一條路不行,再嘗試另外一條路。建立包時的輸出資訊中,除了strip,還有objdump。這兩個都是處理二進位制可執行檔案的。建包和解包都沒有報錯,但檔案變小了,那少的只能是程式執行時無用的資訊,無用的資訊又只能是除錯資訊和無用的符號之類的,而file 顯示額外資訊還在,沒被strip 掉。還在,還在。

又默唸了多次之後,突然又靈光一閃:誰說strip資訊還在就等於除錯資訊沒變化呢?

strip 資訊還在,並不代表除錯資訊沒變化。思維定勢害死人啊。那就看看打包前和解包後兩個可執行檔案中的除錯資訊數量有沒有區別。

抱著試試看的心理,用如下命令把兩個可執行檔案中的除錯資訊寫到了檔案中:

objcopy --only-keep-debug rubik_superfst info.dbg

這是打包前程式生成的除錯資訊大小:

這是解包後程序生成除錯資訊大小:

果然不一樣!已經隱約見到勝利燈塔上閃現的微光了。file 命令的障眼法,還是被我給瞧破了。

除錯資訊大小不一樣,那麼RPM 到底去除了哪些資訊呢?看看objcopy 還有什麼使用方法。

man objcopy

發現 objcopy 的引數中有好幾個 strip 開頭的選項:

既然是除錯資訊大小不一樣,我們就先在打包前的程式上試試去除除錯資訊的選項 —strip-debug,即 -g 選項。

objcopy -g rubik_superfst

看看打包前的程式在執行這一命令後發生了哪些變化:

一次無意的嘗試,竟獲得了意想不到的結果。在未打包程式上執行完 objcopy -g 得到的檔案,竟然和RPM 包解包後變小的程式完全一樣。這說明了什麼?這說明了 RPM 做的操作就是執行了 objcopy -g 命令!

既然除錯資訊還是被 strip 了,那麼就 google 一下如何保留除錯資訊吧。只要在 spec 檔案中加入:

%define __strip /bin/true

萬事大吉。再對比打包前和解包後的可執行檔案,md5 完全一樣。

一切如舊。雲淡風輕,茶香四溢。

RPM,終究你還是沒逃出我的掌心。食素、焚香、沐浴、更衣,終究是有用的。

那麼,file 命令為什麼對已經去掉了除錯資訊的程式還會顯示 “not stripped” 呢?file 命令的 man page 中並沒有對 strip 進行明確的說明。發現上面 objcopy 命令有好幾個strip 選項,那就試試吧。

夢想還是要有的,萬一實現了呢?嘗試的勇氣還是要有的,萬一有發現新大陸呢?

先看 strip-all 選項,這個看上去是去除了所有無用資訊,應該對 file 有效:

果然有效!但是 strip-debug 對file 命令就無效,用gdb檢視時,strip-all 也刪除了除錯資訊的符號,看來strip-all 中應該還刪除了除除錯符號外的其他符號,而 file 命令關注的就是這些內容。感興趣的同學可以繼續研究。本篇就不做擴充套件了。

結語

此次除錯,小有心得:

  • 別看現在鬧得歡,小心將來拉清單。別高興的太早,碼農前進的路上從來不缺的就是坑。
  • 排除掉所有不可能的因素,剩下的即使再不可思議,也是真實答案。
  • 吾愛吾師,但吾更愛真理。這個spec 檔案是根據前輩使用的檔案改進的,本來覺得萬無一失,沒想到還是會有一些問題。
  • 世事洞明皆學問。檔案大小變化看似是一個不起眼的小問題,但抽絲剝繭深究起來還真的學到不少東西。
  • bug 一定有線索。要重視程式的輸出或log,及早發現bug 的線索在排查問題時能節約不少時間。
  • 排查問題時一定要深度思考,不要病急亂投醫,東試一下,西試一下。想清楚當前問題的癥結在哪,從癥結出發一步步前進,直至找到問題的最根本原因。
  • 跳出思維定勢,跳出思維定勢,跳出思維定勢。換個角度考慮問題會有不一樣的收穫。