2013-10-19 50 views
5

我理解的編譯過程的方式:鏈接工作到底如何?

1)預處理:你的所有宏被替換爲它們的實際值,所有評論被刪除,替換等你的#include語句與文件的文字文本你」包括在內。

2)編輯:不會深入太深這裏,但結果是你在任何架構的組件文件。

3)彙編:獲取彙編文件並將其轉換爲二進制指令,即機器代碼。

4)鏈接:這是我困惑的地方。此時你有一個可執行文件。但是如果你真的運行了可執行文件會發生什麼?您可能包含* .h文件的問題是否包含函數原型?所以如果你真的從這些文件中調用其中一個函數,它將不會有定義,並且程序會崩潰?

如果是這樣的話,究竟鏈接做什麼,在底下?它如何找到與你包含的.h文件關聯的.c文件,以及它如何將它注入到機器代碼中?這個文件是否需要再次完成整個編譯過程?

現在,我明白,有兩種類型的鏈接,動態和靜態。在爲每個創建的可執行文件實際重新編譯庫的源時是否是靜態的?我不太瞭解動態鏈接如何工作。那麼你編譯的一個可執行庫是由你使用它的所有進程共享的嗎?這到底如何?它不會在嘗試訪問它的進程的地址空間之外嗎?另外,對於動態鏈接,您是否還需要在某個時刻編譯庫?它是否只是不停地坐在那裏等待使用?什麼時候編譯?

你可以通過以上並清除了所有的誤解,錯誤的假設存在,並替換你的正確的解釋?

+0

可能的重複[在實際中如何C++鏈接工作?](http://stackoverflow.com/questions/12122446/how-does-c-linking-work-in-practice) –

回答

11

At this point you have an executable.

號在這一點上,你有目標文件,這是不是在他們自己的,可執行的。

But if you actually run that executable what happens?

事情是這樣的:

h2co3-macbook:~ h2co3$ clang -Wall -o quirk.o quirk.c -c 
h2co3-macbook:~ h2co3$ chmod +x quirk.o 
h2co3-macbook:~ h2co3$ ./quirk.o 
-bash: ./quirk.o: Malformed Mach-o file 

告訴你這是不是一個可執行文件。

Is the problem that you may have included *.h files, and those only contain function prototypes?

其實很接近。翻譯單元(.c文件)(通常)被轉換爲代表其功能的彙編/機器代碼。如果它調用一個函數,那麼文件中將會有該函數的引用,但沒有定義。

So if you actually call one of the functions from those files, it won't have a definition and your program will crash?

正如我所說,它甚至不會運行。讓我重複一遍:目標文件是不可執行。

what exactly does linking do, under the hood? How does it find the .c file associated with the .h that you included [...]

事實並非如此。它會查找由.c文件生成的其他對象文件,並最終查找庫(其實質上只是其他對象文件的集合)。

它找到它們是因爲你告訴它要尋找什麼。假設你有它由該相互調用的函數的兩個.c文件的一個項目,這是不行的:

gcc -c file1.c -o file1.o 
gcc -c file2.c -o file2.o 
gcc -o my_prog file1.o 

它會失敗,並鏈接錯誤:鏈接器將無法找到的定義在file2.c(和file2.o)中實施的功能。但是,這將工作:

gcc -c file1.c -o file1.o 
gcc -c file2.c -o file2.o 
gcc -o my_prog file1.o file2.o 

[...] and how does it inject that into your machine code?

對象文件包含存根引用(通常在函數入口點地址或明確的,人類可讀的名字的形式)來調用它們的功能。然後,鏈接器查看每個庫和目標文件,找到引用(如果找不到函數定義,則拋出錯誤),然後用實際的「調用此函數」機器代碼指令替換存根引用。 (是的,這在很大程度上簡化了,但沒有你問一個具體的架構和具體的編譯器/連接器,這是很難說更準確...)

Is static when you actually recompile the source of the library for every executable you create?

號靜態鏈接意味着機器代碼庫的對象文件實際上被複制/合併到最終的可執行文件中。動態鏈接意味着庫被加載到內存中一次,然後當啓動可執行文件時,操作系統解析上述存根函數引用。庫中的機器代碼不會被複制到最終的可執行文件中。 (所以這裏,工具鏈中的鏈接程序只做部分工作。)

以下內容可以幫助您實現啓示:如果靜態鏈接可執行文件,它將是自包含的。它可以在任何地方運行(無論如何都是在兼容的架構上)。如果您動態鏈接它,則只有在該計算機上安裝了該程序引用的所有庫時纔會在機器上運行。

So you compile one executable library that is shared by all of your processes that use it? How is that possible, exactly? Wouldn't it be outside of the address space of the processes trying to access it?

OS的動態鏈接器/裝載器組件負責所有的這一點。

Also, for dynamic linking, don't you still need to compile the library at some juncture in time?

正如我已經提到:是的,它已經編譯。然後它會在某個點(通常是第一次使用時)加載到內存中。

When is it compiled?

有一段時間才能使用。通常情況下,編譯一個庫,然後將其安裝到系統上的某個位置,以便操作系統和編譯器/鏈接器知道它的存在,然後開始編譯(使用該庫的程序,即鏈接)。不早。

+0

明智的答案!謝謝。當然,如果你願意的話,我現在會跟進一些問題: - 你說如果它調用一個函數,會有一個對函數的引用。因此,目標文件實際上只包含某種可以在鏈接上解析的符號?這不是一個真正的機器指令?就像它只是說「printf」或類似的東西?因爲它不可能事先知道在內存中哪個函數將會是... – ordinary

+0

- 在動態鏈接範例中,從您的解釋看來,最終的可執行文件似乎有未解析的符號。所以當你將程序加載到內存中時,這是否意味着在程序實際加載之前,操作系統有一箇中間步驟,在該步驟中,它會查看所有未解析的符號併爲這些函數提供文本地址?如果你調用一個不存在的函數會發生什麼?失敗點在哪裏? – ordinary

+1

@普通是的,它包含存根符號(「符號」 - 這正是他們所稱的)。這是這些文件實際上不可執行的部分原因。 – 2013-10-19 09:39:04