最近將自己開發的SIP協議棧移植到ARM芯片下的嵌入式Linux,遇到一個奇怪問題,這篇小文簡要記錄解決過程。
相同的代碼在windows下、CentOS Linux下都正常,交叉編譯到ARM芯片的64位Linux下總是崩潰,估計堆棧、內存被破壞,用Valgrind沒辦法定位到具體的出錯位置。經過多次用最原始的printf()跟蹤,發現和md5加密函數有關,篩查了代碼死活也沒發現錯誤。如此折騰了將近一週,神經高度緊張。
後來仔細分析相關的源代碼,終於發現問題所在。
協議棧用到一個md5的函數庫,這個庫比較古老,Dos時代就存在。其中的頭文件md5.h有問題:
#ifdef __alpha
typedef unsigned int uint32;
#else
typedef unsigned long uint32;
#endif
Dos時代(或windows NT 3.1時代),alpha芯片是32位的,因此定義一個32位無符號整數,用unsigned int,而intel的286之類的芯片int類型是16位的,所以32位無符號整數需定義爲unsigned long。
到了現在的64位時代,這些就有問題了。
在windows下:int - 4字節32位,long - 4字節32位,long long - 8字節64位
在linux下:int - 4字節32位,long - 8字節64位,long long - 8字節64位
上述定義,非_alpha,32位無符號整數被定義爲typedef unsigned long uint32,在windows下沒問題,在linux下是有問題的,實際上成了64位。很多因此定義的緩衝區,在操作時就可能出錯。
也許會問,同樣Linux,爲何CentOS下沒問題呢?那是因爲Intel和ARM芯片的內存佈局不一樣,ARM會立即暴露而已。
後來將上述定義統一改爲(反正不可能16位時代了):
typedef unsigned int uint32;
重新編譯後,程序就非常穩定了。