2016-12-14 168 views
2

假設我有一個用C編寫的程序,我有兩臺相同的計算機,一臺運行Windows,另一臺運行linux。由於計算機是相同的,它們的處理器具有相同的指令集,因此編譯後的機器代碼應該相同。那麼爲什麼我需要編譯我的程序兩次?假設我不調用任何與操作系統相關的函數,或者依賴於實際操作系統的東西。爲什麼機器代碼依賴於操作系統類型?

+6

可執行格式不同。 –

+7

您無法避免調用OS函數或調用OS函數的東西。如果您希望程序具有可見效果,例如打印號碼,則不適用。 –

+0

他們爲什麼不同?它與保護模式/內核模式有關嗎? 如果處理器是相同的,他們不應該運行相同的機器碼嗎? –

回答

6

機器碼不依賴於操作系統,它對於同一個CPU是一樣的。

如果你在目標CPU模式下(比如說x86 32b)做了一個與操作系統無關的機器代碼,並將其加載到某些ROM內存中,這樣它就可用了,你可以在Windows中映射這部分ROM,在linux中(通過完全不同的OS API來映射物理內存並賦予其可執行權限),並跳到那裏..並且ROM中的機器代碼將以相同的方式運行。

那麼,爲什麼我需要編譯我的程序兩次?假設我不調用任何與操作系統相關的函數,或者依賴於實際操作系統的東西。

您不需要。但通常你需要一些入口點進入你的代碼,通常如何提供通用入口點最簡單的方法是遵循操作系統定義的ABI(應用程序二進制接口),例如在32b窗口中你從堆棧中讀取參數,在64b中你在寄存器中接收參數(如果可能的話)。如果您不會調整您的過程序言代碼以正確的方式選擇參數,那麼它將在「其他」操作系統中輸入的錯誤超出其寫入的操作。

但是機器代碼本身與CPU指令相同。這就是說,在x86上,由於歷史向後兼容性,情況會更加複雜,所以CPU可以處於16b模式,32b [受保護]模式(它們中的幾個加上不同的設置)或64b模式。 80386 CPU指令mov eax,1對於16b模式和32b模式具有不同的機器碼編碼。

但是,只要您的目標是相同的CPU模式,相同指令的機器代碼就會以相同的方式編譯。你只需要寫出不同的來源來遵循不同的ABI。

而可執行文件......每種格式都不同,它甚至不是「每個操作系統」,再次由於歷史原因,幾乎所有的x86操作系統都支持多種可執行文件格式,因此存儲在文件中的機器代碼周圍的元數據(在將機器代碼加載到內存中並將其設置爲運行時由OS使用)完全不同。

實用的例子是linux應用wine,它可以執行windows可執行文件,提供虛擬操作系統鉤子點來模擬windows操作系統,並且通過理解windows可執行文件,正確地將它們加載到內存中。這種Windows應用程序的機器碼本身運行,沒有任何補丁。

+1

另外一個注意事項。彙編程序通常會生成「目標文件」,它們又是工具鏈特定的格式,因此Microsoft Visual Studio使用不同的「.obj」文件來存儲相同的組裝機器代碼,而不是使用Linux上的gcc生成「.o」。目標文件的機器代碼部分是相同的,但允許鏈接此文件的元數據可能完全不同(加上不同格式的調試元數據等)。所以這是另一個原因,爲什麼你必須多次編譯同一個源文件,但它不是per-OS,而是每個工具鏈。 – Ped7g

+2

另外,ABI在字體大小上的差異:'long'在x86-64 Windows上是32位,而在System V x86-64 ABI中是64位。因此,對於不同的ABI編譯時,相同的C結構可能意味着不同的東西,更不用說單個變量是不同的大小(因此在機器代碼中需要不同的操作數大小,以及不同的本地堆棧佈局等) –

+2

比「每工具鏈」。它是每個工具鏈版本。中間文件的二進制兼容性並不能保證來自特定供應商的所有工具永久存在。我不確定GCC人員是否試圖在這裏提供向後兼容性,但是Microsoft明確沒有。 (實際上,這頗具諷刺意味。)交叉工具鏈的唯一作用是調試信息,因爲它具有標準化的格式。但是,當然,關於標準的最好的事情是有很多可供選擇的:COFF,ELF,CodeView,PDB,... :-) –

相關問題