我只是花了半天的時間來計算x86-64代碼中的一次微妙崩潰,所以這是對別人的單挑 - 我沒有在別處看到過這種情況。在x86-64中返回指針的未聲明函數導致無效指針擴展
如果您在沒有正確聲明的情況下使用libc函數,那麼gcc 會認爲它返回int。例如。 setlocale()假定爲int setlocale(),在EAX中返回一個32位的int值。
嘗試將此返回值轉換爲指針,無論是通過隱式轉換還是顯式轉換,都將強制通過符號擴展從32位轉換爲64位,即使被調用函數返回了有效的64位指針值RAX!例如。
char *p = setlocale(0, 0); // bear with me for a second
被編譯到
1c: b8 00 00 00 00 mov $0x0,%eax
21: e8 00 00 00 00 callq 26 <hard_locale+0x26>
26: 48 98 cltq ; <--- eax is expanded in rax
GCC甚至試圖告訴你這一點:
warning: initialization makes pointer from integer without a cast
如果添加一個明確的轉換,警告變化,這說明這個問題:
warning: cast to pointer from integer of different size
如果你幸運的話,什麼都不會發生,但如果它發生在內存的指針返回最多一個較大的值,它會被搞砸了,如下:
function returns in RAX: 0x07ffff7b9705e
cltq considers EAX with negative sign: 0xf7b9705e
now RAX is: 0xfffffffff7b9705e
,你的指針是無效的。
修復和解決方案:
總是用正確的函數聲明
-Wall -Werror應在x86-64的編譯器拖欠。
嗯,是的,如果你沒有聲明你使用的函數,你會有一個糟糕的時間。我認爲這是衆所周知的,因爲需要始終啓用盡可能多的編譯器警告。我不認爲拼出很多程序集的基本類型錯誤是非常有啓發性的...... – 2013-03-11 13:30:09
這是C的預期行爲。解決方案是修復由編譯器引發的任何警告,其中包括「call未申報的功能「。 – 2013-03-11 13:32:20
這個問題應該歸咎於那些決定將int保留在32位的編譯器編寫者, 「int」爲64位(平臺的自然數據大小)的64位數據模型可以很好地處理這種情況。 – ecatmur 2013-03-11 14:42:14