C编译器、链接器、加载器详解
一、概述
C語言的編譯鏈接過程要把我們編寫的一個c程序(源代碼)轉換成可以在硬件上運行的程序(可執行代碼),需要進行編譯和鏈接。編譯就是把文本形式源代碼翻譯為機器語言形式的目標文件的過程。鏈接是把目標文件、操作系統的啟動代碼和用到的庫文件進行組織形成最終生成可加載、可執行代碼的過程。過程圖解如下:?
?
二、編譯過程
編譯過程又可以分成兩個階段:編譯和匯編。
2.1編譯
編譯是指編譯器讀取源程序(字符流),對之進行詞法和語法的分析,將高級語言指令轉換為功能等效的匯編代碼。
源文件的編譯過程包含兩個主要階段:
第一個階段是預處理階段,在正式的編譯階段之前進行。預處理階段將根據已放置在文件中的預處理指令來修改源文件的內容。
主要是以下幾方面的處理:
頭文件的目的主要是為了使某些定義可以供多個不同的C源程序使用,這涉及到頭文件的定位即搜索路徑問題。頭文件搜索規則如下:
?
第二個階段編譯、優化階段,編譯程序所要作得工作就是通過詞法分析和語法分析,在確認所有的指令都符合語法規則之后,將其翻譯成等價的中間代碼表示或匯編代碼。?
?
2.2匯編
匯編實際上指匯編器(as)把匯編語言代碼翻譯成目標機器指令的過程。目標文件中所存放的也就是與源程序等效的目標的機器語言代碼。目標文件由段組成。通常一個目標文件中至少有兩個段:
- 代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可執行的,但一般卻不可寫。
- 數據段:主要存放程序中要用到的各種全局變量或靜態的數據。一般數據段都是可讀,可寫,可執行的。
?
2.3目標文件(Executable and Linkable Format)
?
2.4 靜態庫與動態庫
靜態庫(static library)就是將相關的目標模塊打包形成的單獨的文件。使用ar命令。
靜態庫的優點在于:
- 程序員不需要顯式的指定所有需要鏈接的目標模塊,因為指定是一個耗時且容易出錯的過程;
- 鏈接時,連接程序只從靜態庫中拷貝被程序引用的目標模塊,這樣就減小了可執行文件在磁盤和內存中的大小。
動態庫(dynamic library)是一種特殊的目標模塊,它可以在運行時被加載到任意的內存地址,或者是與任意的程序進行鏈接。
動態庫的優點在于:
- 更新動態庫,無需重新鏈接;對于大系統,重新鏈接是一個非常耗時的過程;
- 運行中可供多個程序使用,內存中只需要有一份,節省內存。
?
三、鏈接過程
鏈接器主要是將有關的目標文件彼此相連接生成可加載、可執行的目標文件。鏈接器的核心工作就是符號表解析和重定位。
3.1 鏈接的時機:
3.2 鏈接的作用(軟件復用):
3.3 靜態庫搜索路徑(由靜態鏈接器負責)
3.4 動態庫搜索路徑(由動態鏈接器負責)
3.5 靜態鏈接(編譯時)
鏈接器將函數的代碼從其所在地(目標文件或靜態鏈接庫中)拷貝到最終的可執行程序中。這樣該程序在被執行時這些代碼將被裝入到該進程的虛擬地址空間中。靜態鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關函數的代碼。
為創建可執行文件,鏈接器必須要完成的主要任務:
關于符號表和符號解析以及重定位的分析后續學習。
3.6 動態鏈接(加載、運行時)
在此種方式下,函數的定義在動態鏈接庫或共享對象的目標文件中。在編譯的鏈接階段,動態鏈接庫只提供符號表和其他少量信息用于保證所有符號引用都有定義,保證編譯順利通過。動態鏈接器(ld-linux.so)鏈接程序在運行過程中根據記錄的共享對象的符號定義來動態加載共享庫,然后完成重定位。在此可執行文件被執行時,動態鏈接庫的全部內容將被映射到運行時相應進程的虛地址空間。動態鏈接程序將根據可執行程序中記錄的信息找到相應的函數代碼。 ?
四、加載過程
加載器把可執行文件從外存加載到內存并進行執行。 Linux中進程運行時的內存映像如下:
?
?加載過程如下:
加載器首先創建如上圖所示的內存映像,然后根據段頭部表,把目標文件拷貝到內存的數據和代碼段中。然后,加載器跳轉到程序入口點(即符號_start 的地址),執行啟動代碼(startup code),啟動代碼的調用順序如所示:
?
五、處理目標的常用工具
UNIX系統提供了一系列工具幫助理解和處理目標文件。GNUbinutils 包也提供了很多幫助。這些工具包括:
- AR :創建靜態庫,插入、刪除、列出和提取成員;
- STRINGS :列出目標文件中所有可以打印的字符串;
- STRIP :從目標文件中刪除符號表信息;
- NM :列出目標文件符號表中定義的符號;
- SIZE :列出目標文件中節的名字和大小;
- READELF :顯示一個目標文件的完整結構,包括ELF 頭中編碼的所有信息。
- OBJDUMP :顯示目標文件的所有信息,最有用的功能是反匯編.text節中的二進制指令。
- LDD :列出可執行文件在運行時需要的共享庫。
總結
以上是生活随笔為你收集整理的C编译器、链接器、加载器详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快速理解Docker - 容器级虚拟化解
- 下一篇: 基于 Bochs 的操作系统内核实现