標籤:
程式的機器級表示。
GCC C語言編譯器以彙編代碼的形式產生輸出,彙編代碼是機器代碼的文本表示,然後GCC調用彙編器和連結器,從而根據彙編代碼產生可執行檔機器代碼。
一、曆史觀點
Linux使用了平坦定址方式,使程式員將整個儲存空間看做一個大的位元組數組。
二、程式編碼
將原始碼轉化成可執行代碼,C前置處理器擴充原始碼。
1.機器碼代碼
兩種抽象:機器級程式的格式和行為,定義為指令集體繫結構;二是機器級程式使用的儲存空間地址是虛擬位址,提供的儲存空間模型看上去是一個非常大的位元組數組。
2.程式碼範例
3.關于格式的注釋
三、資料格式
1、大多數常用資料類型都是以雙字形式儲存的。包括INT和LONG INT。
2、處理字串資料時,通常會用到位元組。
3、浮點數有三種形式:單精確度值float,雙精確度值double,和擴充精度。
4、大多數GCC產生的彙編代碼指令都有一個字元尾碼,表明運算元的大小。
四、訪問資訊
一個IA32中央處理單元CPU包含一組8個儲存32位值的寄存器。寄存器用來儲存整數資料和指標。
1.運算元指示符
各種不同的運算元的可能性被分為三種類型。第一種類型是立即數,也就是常數值。第二種類型是寄存器,它表示某個寄存器的內容,對雙字操作來說,可以是8個32位寄存器中的一個。第三類運算元是儲存空間引用,它會根據計算出來的地址訪問某個儲存空間地址。
2.資料傳送指令
把許多不同的指令分成了指令類,一類中的指令執行一樣的操作,只不過運算元大小不同。如 MOV中的指令將源運算元的值複製到目的運算元中。源運算元指定的值是一個立即數,儲存在寄存器中或者儲存空間中。目的運算元指定一個位置,要麼是一個寄存器,要麼是一個儲存地址。而 PUSH指令的功能是把資料壓入到棧上,而 POPL指令是彈出資料。這些指令都只有一個運算元——壓入的資料來源和彈出的資料目的。
因為棧和程式碼以及其他形式的程式資料都是放在同樣的儲存空間中,所以程式可以用標準的儲存空間定址方法訪問棧內任意位置。
3.資料傳送樣本
五、算術和邏輯操作
給出的每個指令類都有對位元組、字、和雙字資料進行操作的指令。這些指令被分為四組:載入有效地址、一元操作、二元操作和移位;二元操作有兩個運算元,而一元操作有一個運算元。
1.載入有效地址
載入有效地址指令LEAL實際上MOVL指令的變形。它的指令形式是從儲存空間讀資料到寄存器,但是實際上它根本沒有引用儲存空間。
目的運算元必須是一個寄存器。
2.一元操作和二元操作
第二組中的操作是一元操作,它只有一個運算元,既是源又是目的;第三組中操作是二元操作,其中第二個運算元既是源又是目的。
3.移位操作
最後一組是移位操作,先給出移位量,然後第二項給出的是要移位的位元。它可以進行算術和邏輯右移。移位量可以是單節編碼,還可以是一個立即數。移位運算元的目的運算元可以是一個寄存器,或者是一個儲存空間位置。
4.討論
5.特殊的算術操作
六、控制
到目前為止,我們只考慮了直線代碼的行為,也就是指令一條接著一條順序的執行。機器代碼提供兩種基本的低級低制來實現有條件的行為:測試資料值,然後根據測試的結果來改變控制流程或者資料流。
用JUMP指令可以改變一組機器代碼的執行順序;編譯器必須產生指令序列,這些指令序列構建在這種實現C語言控制結構的低級機制之上。
1.條件碼
除了整數寄存器,CPU還維護著一組單個位的條件碼寄存器。最常見的條件碼有CF、ZF、SF、OF;
LEAL指令不改變任何條件碼,因為它是用來進行地址計算的。
CMP指令和SUB指令的行為是一樣的。
TEST指令和AND指令的行為一樣的,除了他們只設定條件碼而改變目的寄存器的值。典型的用法,兩個運算元是一樣的,或者其中的一個運算元是一個掩碼,用來指示哪些位應該被測試。
2.條件訪問碼
條件碼通常不會直接讀取,常見的使用方法有:1.)可以根據條件碼的某個組合,將一個位元組設定為0或者1;2.)可以條件跳轉到程式的某個其他的部分3.)可以有條件地傳送資料。
機器代碼對於有符號和無符號兩種情況都使用一樣的指令,這是因為許多的算術運算對無符號和補碼算術都有一樣的位級行為。
3.跳轉指令極其編碼
跳轉指令會導致執行切換到程式中一個全新的位置。
4.翻譯條件分支
將條件運算式和語句從C語言翻譯成機器代碼,最常用的方式是結合有條件和無條件跳轉。
5.迴圈
大多數彙編器根據一個迴圈的do-while形式來產生迴圈代碼。
6.條件傳送指令
實現條件操作的傳統方法是利用控制的條件轉移。資料的條件轉移是一種替代的策略。這種方法先計算一個條件操作的兩種結果,然後再根據條件是否滿足從而選取一個。
7.switch語句
選擇開關語句不僅僅提高C代碼的可讀性,而是通過使用跳轉表這種資料結構使得實現更加高效。
七、過程
一個程序呼叫包括將資料和控制從代碼的一部分傳遞到另一部分。另外,它必須在進入時為過程的局部分量分配空間,並在退出時釋放這些空間。資料傳遞和、局部變數的分配和釋放通過操縱程式棧來實現。
1.棧幀結構
IA32程式用程式棧來支援程序呼叫。機器用棧來傳遞過程參數、儲存返回資訊、儲存寄存器用於以後恢複,以及本機存放區。為單個過程分配的那部分棧為棧幀,棧幀的最頂端以兩個指標界定,寄存器%ebp為幀指標,而寄存器%esp為棧指標。當程式執行時,棧指標可以移動,因此大多數資訊的訪問都是相對於幀指標的。(棧用來傳遞參數,儲存返回資訊,儲存寄存器,以及本機存放區)
2.轉移控制
CALL指令有一個目標,即指明被調用過程起始的指令地址。CALL指令的效果是將返回地址入棧,並跳轉到被調用過程的起始處。RET指令從棧中彈出地址,並跳轉到這個位置。
3.寄存器使用慣例
寄存器%eax,%edx和%ecx被劃分為調用者儲存寄存器。當過程P調用Q時,Q可以覆蓋這些寄存器,而不會破壞任何P所需要的資料。另一方面,寄存器%ebx,%esi和%dei被劃分為被調用者儲存寄存器。根據描述的慣例,必須保持寄存器%ebp和%esp。
4.過程樣本
編譯器根據一組很簡單的慣例來產生慣例棧結構的代碼。參數在棧上傳遞給函數,可以從棧中用相對於%ebp的正位移量,來訪問他們。可以用push指令或者是從棧指標減去位移量來在棧上分配空間,在返回前,函數必須將棧恢複到原始條件,可以恢複所有的被調用者儲存寄存器和%ebp,並且重設%esp使其指向返回地址。為了使程式能夠正確執行,讓所有過程都遵循一組建立和恢複棧的一致慣例很重要。
5.遞迴過程
八、數組分配和訪問
1.基本原則
2.指標運算
如果P是一個指向類型為T的資料的指標,P為值xp,那麼運算式p+i的值為xp+L,這裡L是指資料類型T的大小。
3.嵌套的數組
4.定長數組
5.變長數組
九、異質的資料結構
1.結構
2.聯合
3.資料對齊
十、綜合理解指標
十一、使用GDB調試器
通常的方法是在程式感興趣的地方附近設定斷點。斷點可以設定在函數入口後面,或者一個程式的地址處。在斷點處,可以單步追蹤程式,一次只執行幾條指令,或是前進到下一斷點。一些命令,如 kill-停止程式,break sum-在函數sum入口設定斷點。disas-反組譯碼當前函數。等。
第五周學習報告