靜態語義:通常包含宣告和型別的匹配,型別的轉換等。
動態語義:像比如0作為除數就是乙個執行期的語義錯誤。
彙編階段主要是將編譯階段生成的彙編**程式設計二進位制的機器語言並生成.o檔案,稱為目標檔案,.o檔案屬於二進位制檔案。主要做的就是翻譯指令。在這一階段有生成中間檔案,中間檔案經過**生成器優化(比如選擇合適的定址方式、使用位移代替乘法運算、刪除多餘的指令等)後才生成目標檔案。中間檔案使得編譯器分為前端和後端,前端負責產生與機器無關的中間**,後端(包括**生成器和目標**優化器)將中間**轉換成目標機器**。
在前面,每個檔案都是單獨編譯的,但是在編譯鏈結之後,不論原始檔多或少,最終都只會生成乙個可執行檔案。而鏈結階段就是將乙個或多個目標檔案鏈結成乙個可執行檔案。c/c++模組之間通訊的方式:函式呼叫和模組間的變數訪問,而這兩個方式都是需要知道函式或者變數的位址。鏈結主要就是將各個模組相互連線的部分處理好。鏈結有靜態鏈結和動態鏈結。主要處理包括:
二進位制可重定位目標檔案(obj檔案)
目標檔案是已經編譯後的可執行檔案格式,只是還沒有進行鏈結,符號位址沒調整,執行環境沒載入。windonws下是pe,linux下是elf。
目標檔案中有編譯後的機器指令**、資料、鏈結時需要的資訊(符號表、除錯資訊等),這些東西會分別存在不同的段。指令放在**段.text段中,全域性變數和靜態區域性變數放在資料段.data和.bss段(.bss段只是為未初始化或者初始化為0的全域性變數和靜態區域性變數預留位置,在檔案中不佔據空間,不佔記憶體,佔磁碟)。字串常量是一種制度資料放在唯讀資料段.rodata段。段表儲存了各個段的基本屬性(段名、長度、在檔案中的偏移、讀寫許可權等等)。編譯器、鏈結器和裝載器都是依靠段表定位和訪問各個段屬性。符號表,在鏈結過程由彙編器構造的,中把函式和變數統稱符號,函式名或者變數名就是符號名,位址就是符號值。符號表中有名、值、大小、型別(未知-0,資料-1,函式**-2,段-3,檔名-4)和繫結資訊(區域性符號-0,全域性符號-1,弱引用-2)、符號所在的段。
命令objdump和readelf
使用objdump –h main.o可以檢視目標(elf)檔案的關鍵段的基本資訊,objdump –s main.o檢視關鍵段的內容,-d檢視所有包含指令的段的反彙編。
readelf –s main.o 檢視所有段的內容
還有哪些東西參與鏈結過程
可執行目標檔案和obj檔案的比較
可重定位目標檔案:包含二進位制**和資料,可以和其他可重定位目標檔案合併生成可執行目標檔案。有一種特殊型別的可重定位目標檔案是共享目標檔案,可以載入或者執行時動態載入記憶體並鏈結。
可執行目標檔案:包含二進位制**和資料可以直接在記憶體中執行。是由多個可重定位目標檔案合併,各種段相似,段的位址已經被重定位到最終執行時記憶體位址,還有多了乙個小函式_init,程式的初始化**會呼叫,而且沒有.rel段。
符號解析和符號重定向
符號解析:目的就是將每個符號引用正好和乙個符號定義關聯起來。如果編譯器遇到乙個不是在當前模組中定義的符號,會先假設在其他模組中定義了,生成鏈結器符號表條目,交給鏈結器,如果鏈結器找不到就會輸出錯誤資訊並終止。
重定位:每個符號定義和乙個記憶體位址關聯起來之後,鏈結器就知道它的輸入目標模組的**段和資料段的確切大小,就可以開始重定位。重定位段(合併段)和符號定義(將執行時的位址賦給新的段,和模組中的每乙個符號)。重定位段中的符號引用(修改段中對每個符號的引用,使得它們指向正確的執行時位址)。
編譯鏈結原理
從源 到可執行程式,需要經歷以下幾個過程 預處理 編譯 彙編 連線。1.預處理 預處理主要是處理以 開頭的預編譯指令,包括 include define if等 刪除注釋 新增行號以及標頭檔案展開。2.編譯 編譯的主要工作是詞法分析 語法分析 優化編譯,將源 翻譯成彙編 3.彙編 彙編是將彙編 翻譯...
編譯鏈結原理
32位計算機,每個程式都有4g的虛擬位址空間。首先虛擬位址空間分為兩大塊,乙個是使用者空間,乙個是核心空間。使用者空間佔3g的大小,並且它是每個程序所獨有的,它的開頭128m存放的是我們無法訪問的地方。1 預編譯 生成 i 檔案 操作命令 gcc e main.c o main.i 具體內容 1 巨...
編譯鏈結原理
32位計算機,每個程式都有4g的虛擬位址空間。首先虛擬位址空間分為兩大塊,乙個是使用者空間,乙個是核心空間。使用者空間佔3g的大小,並且它是每個程序所獨有的,它的開頭128m存放的是我們無法訪問的地方。text c語言的編譯後執行語句都編譯成機器 儲存在.text段 data 已初始化的全域性變數和...