国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C編譯器、鏈接器、加載器詳解

2019-11-10 18:04:44
字體:
供稿:網(wǎng)友

一、概述

C語言的編譯鏈接過程要把我們編寫的一個(gè)c程序(源代碼)轉(zhuǎn)換成可以在硬件上運(yùn)行的程序(可執(zhí)行代碼),需要進(jìn)行編譯和鏈接。編譯就是把文本形式源代碼翻譯為機(jī)器語言形式的目標(biāo)文件的過程。鏈接是把目標(biāo)文件、操作系統(tǒng)的啟動(dòng)代碼和用到的庫文件進(jìn)行組織形成最終生成可加載、可執(zhí)行代碼的過程。

過程圖解如下: 

預(yù)處理器:將.c 文件轉(zhuǎn)化成 .i文件,使用的gcc命令是:gcc –E,對應(yīng)于預(yù)處理命令cpp;編譯器:將.c/.h文件轉(zhuǎn)換成.s文件,使用的gcc命令是:gcc –S,對應(yīng)于編譯命令 cc –S;匯編器:將.s 文件轉(zhuǎn)化成 .o文件,使用的gcc 命令是:gcc –c,對應(yīng)于匯編命令是 as;鏈接器:將.o文件轉(zhuǎn)化成可執(zhí)行程序,使用的gcc 命令是: gcc,對應(yīng)于鏈接命令是 ld;加載器:將可執(zhí)行程序加載到內(nèi)存并進(jìn)行執(zhí)行,loader和ld-linux.so。

 

二、編譯過程

編譯過程又可以分成兩個(gè)階段:編譯和匯編。

2.1編譯

編譯是指編譯器讀取源程序(字符流),對之進(jìn)行詞法和語法的分析,將高級語言指令轉(zhuǎn)換為功能等效的匯編代碼。

源文件的編譯過程包含兩個(gè)主要階段:

第一個(gè)階段是預(yù)處理階段,在正式的編譯階段之前進(jìn)行。預(yù)處理階段將根據(jù)已放置在文件中的預(yù)處理指令來修改源文件的內(nèi)容。

主要是以下幾方面的處理:

宏定義指令,如 #define a b 對于這種偽指令,預(yù)編譯所要做的是將程序中的所有a用b替換,但作為字符串常量的 a則不被替換。還有 #undef,則將取消對某個(gè)宏的定義,使以后該串的出現(xiàn)不再被替換。條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif等。 這些偽指令的引入使得程序員可以通過定義不同的宏來決定編譯程序?qū)δ男┐a進(jìn)行處理。預(yù)編譯程序?qū)⒏鶕?jù)有關(guān)的文件,將那些不必要的代碼過濾掉頭文件包含指令,如#include "FileName"或者#include 等。 該指令將頭文件中的定義統(tǒng)統(tǒng)都加入到它所產(chǎn)生的輸出文件中,以供編譯程序?qū)χM(jìn)行處理。特殊符號,預(yù)編譯程序可以識別一些特殊的符號。 例如在源程序中出現(xiàn)的LINE標(biāo)識將被解釋為當(dāng)前行號(十進(jìn)制數(shù)),F(xiàn)ILE則被解釋為當(dāng)前被編譯的C源程序的名稱。預(yù)編譯程序?qū)τ谠谠闯绦蛑谐霈F(xiàn)的這些串將用合適的值進(jìn)行替換。

頭文件的目的主要是為了使某些定義可以供多個(gè)不同的C源程序使用,這涉及到頭文件的定位即搜索路徑問題。頭文件搜索規(guī)則如下:

所有header file的搜尋會從-I開始然后找環(huán)境變量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH指定的路徑再找默認(rèn)目錄(/usr/include、/usr/local/include、/usr/lib/gcc-lib/i386-linux/2.95.2/include......)

 

第二個(gè)階段編譯、優(yōu)化階段,編譯程序所要作得工作就是通過詞法分析和語法分析,在確認(rèn)所有的指令都符合語法規(guī)則之后,將其翻譯成等價(jià)的中間代碼表示或匯編代碼。 

 

2.2匯編

匯編實(shí)際上指匯編器(as)把匯編語言代碼翻譯成目標(biāo)機(jī)器指令的過程。目標(biāo)文件中所存放的也就是與源程序等效的目標(biāo)的機(jī)器語言代碼。目標(biāo)文件由段組成。通常一個(gè)目標(biāo)文件中至少有兩個(gè)段:

代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可執(zhí)行的,但一般卻不可寫。數(shù)據(jù)段:主要存放程序中要用到的各種全局變量或靜態(tài)的數(shù)據(jù)。一般數(shù)據(jù)段都是可讀,可寫,可執(zhí)行的。

 

2.3目標(biāo)文件(Executable and Linkable Format)

可重定位(Relocatable)文件:由編譯器和匯編器生成,可以與其他可重定位目標(biāo)文件合并創(chuàng)建一個(gè)可執(zhí)行或共享的目標(biāo)文件;共享(Shared)目標(biāo)文件:一類特殊的可重定位目標(biāo)文件,可以在鏈接(靜態(tài)共享庫)時(shí)加入目標(biāo)文件或加載時(shí)或運(yùn)行時(shí)(動(dòng)態(tài)共享庫)被動(dòng)態(tài)的加載到內(nèi)存并執(zhí)行;可執(zhí)行(Executable)文件:由鏈接器生成,可以直接通過加載器加載到內(nèi)存中充當(dāng)進(jìn)程執(zhí)行的文件。

 

2.4 靜態(tài)庫與動(dòng)態(tài)庫

靜態(tài)庫(static library)就是將相關(guān)的目標(biāo)模塊打包形成的單獨(dú)的文件。使用ar命令。

靜態(tài)庫的優(yōu)點(diǎn)在于:

程序員不需要顯式的指定所有需要鏈接的目標(biāo)模塊,因?yàn)橹付ㄊ且粋€(gè)耗時(shí)且容易出錯(cuò)的過程;鏈接時(shí),連接程序只從靜態(tài)庫中拷貝被程序引用的目標(biāo)模塊,這樣就減小了可執(zhí)行文件在磁盤和內(nèi)存中的大小。

動(dòng)態(tài)庫(dynamic library)是一種特殊的目標(biāo)模塊,它可以在運(yùn)行時(shí)被加載到任意的內(nèi)存地址,或者是與任意的程序進(jìn)行鏈接。

動(dòng)態(tài)庫的優(yōu)點(diǎn)在于:

更新動(dòng)態(tài)庫,無需重新鏈接;對于大系統(tǒng),重新鏈接是一個(gè)非常耗時(shí)的過程;運(yùn)行中可供多個(gè)程序使用,內(nèi)存中只需要有一份,節(jié)省內(nèi)存。

 

三、鏈接過程

鏈接器主要是將有關(guān)的目標(biāo)文件彼此相連接生成可加載、可執(zhí)行的目標(biāo)文件。鏈接器的核心工作就是符號表解析和重定位。

3.1 鏈接的時(shí)機(jī):

編譯時(shí),就是源代碼被編譯成機(jī)器代碼時(shí)(靜態(tài)鏈接器負(fù)責(zé));加載時(shí),也就是程序被加載到內(nèi)存時(shí)(加載器負(fù)責(zé));運(yùn)行時(shí),由應(yīng)用程序來實(shí)施(動(dòng)態(tài)鏈接器負(fù)責(zé))。

3.2 鏈接的作用(軟件復(fù)用):

使得分離編譯成為可能;動(dòng)態(tài)綁定(binding):使定義、實(shí)現(xiàn)、使用分離

3.3 靜態(tài)庫搜索路徑(由靜態(tài)鏈接器負(fù)責(zé))

gcc先從-L尋找;再找環(huán)境變量LIBRARY_PATH指定的搜索路徑;再找內(nèi)定目錄 /lib /usr/lib /usr/local/lib 這是當(dāng)初compile gcc時(shí)寫在程序內(nèi)的。

3.4 動(dòng)態(tài)庫搜索路徑(由動(dòng)態(tài)鏈接器負(fù)責(zé))

編譯目標(biāo)代碼時(shí)指定的動(dòng)態(tài)庫搜索路徑-L;環(huán)境變量LD_LIBRARY_PATH指定的動(dòng)態(tài)庫搜索路徑;配置文件/etc/ld.so.conf中指定的動(dòng)態(tài)庫搜索路徑;默認(rèn)的動(dòng)態(tài)庫搜索路徑/lib /usr/lib/ /usr/local/lib

3.5 靜態(tài)鏈接(編譯時(shí))

鏈接器將函數(shù)的代碼從其所在地(目標(biāo)文件或靜態(tài)鏈接庫中)拷貝到最終的可執(zhí)行程序中。這樣該程序在被執(zhí)行時(shí)這些代碼將被裝入到該進(jìn)程的虛擬地址空間中。靜態(tài)鏈接庫實(shí)際上是一個(gè)目標(biāo)文件的集合,其中的每個(gè)文件含有庫中的一個(gè)或者一組相關(guān)函數(shù)的代碼。

為創(chuàng)建可執(zhí)行文件,鏈接器必須要完成的主要任務(wù):

符號解析:把目標(biāo)文件中符號的定義和引用聯(lián)系起來;重定位:把符號定義和內(nèi)存地址對應(yīng)起來,然后修改所有對符號的引用。

關(guān)于符號表和符號解析以及重定位的分析后續(xù)學(xué)習(xí)。

3.6 動(dòng)態(tài)鏈接(加載、運(yùn)行時(shí))

在此種方式下,函數(shù)的定義在動(dòng)態(tài)鏈接庫或共享對象的目標(biāo)文件中。在編譯的鏈接階段,動(dòng)態(tài)鏈接庫只提供符號表和其他少量信息用于保證所有符號引用都有定義,保證編譯順利通過。動(dòng)態(tài)鏈接器(ld-linux.so)鏈接程序在運(yùn)行過程中根據(jù)記錄的共享對象的符號定義來動(dòng)態(tài)加載共享庫,然后完成重定位。在此可執(zhí)行文件被執(zhí)行時(shí),動(dòng)態(tài)鏈接庫的全部內(nèi)容將被映射到運(yùn)行時(shí)相應(yīng)進(jìn)程的虛地址空間。動(dòng)態(tài)鏈接程序?qū)⒏鶕?jù)可執(zhí)行程序中記錄的信息找到相應(yīng)的函數(shù)代碼。  

四、加載過程

加載器把可執(zhí)行文件從外存加載到內(nèi)存并進(jìn)行執(zhí)行。 Linux中進(jìn)程運(yùn)行時(shí)的內(nèi)存映像如下:

 

 加載過程如下:

加載器首先創(chuàng)建如上圖所示的內(nèi)存映像,然后根據(jù)段頭部表,把目標(biāo)文件拷貝到內(nèi)存的數(shù)據(jù)和代碼段中。然后,加載器跳轉(zhuǎn)到程序入口點(diǎn)(即符號_start 的地址),執(zhí)行啟動(dòng)代碼(startup code),啟動(dòng)代碼的調(diào)用順序如所示:

 

五、處理目標(biāo)的常用工具

UNIX系統(tǒng)提供了一系列工具幫助理解和處理目標(biāo)文件。GNUbinutils 包也提供了很多幫助。這些工具包括:

AR :創(chuàng)建靜態(tài)庫,插入、刪除、列出和提取成員;STRINGS :列出目標(biāo)文件中所有可以打印的字符串;STRip :從目標(biāo)文件中刪除符號表信息;NM :列出目標(biāo)文件符號表中定義的符號;SIZE :列出目標(biāo)文件中節(jié)的名字和大小;READELF :顯示一個(gè)目標(biāo)文件的完整結(jié)構(gòu),包括ELF 頭中編碼的所有信息。OBJDUMP :顯示目標(biāo)文件的所有信息,最有用的功能是反匯編.text節(jié)中的二進(jìn)制指令。LDD :列出可執(zhí)行文件在運(yùn)行時(shí)需要的共享庫。
上一篇:異常捕獲

下一篇:LEETCODE--Add Strings

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 乐陵市| 宝清县| 衡阳市| 阜南县| 城口县| 保定市| 桓仁| 陇川县| 新河县| 盐边县| 汶上县| 大宁县| 武夷山市| 页游| 庆云县| 多伦县| 正镶白旗| 司法| 高青县| 白玉县| 冀州市| 杭锦后旗| 通辽市| 成武县| 肥城市| 长治县| 噶尔县| 华蓥市| 铜鼓县| 福贡县| 南丰县| 莒南县| 颍上县| 金山区| 恭城| 剑川县| 隆昌县| 大连市| 永嘉县| 涞源县| 海兴县|