圖解分析彙編代碼以理解計算機是如何工作的
《Linux內核分析》MOOC課程:在線課程鏈接http://mooc.study.163.com/course/USTC-1000029000
第一講 計算機是如何工作的? 課堂筆記
@2015.03
馮諾依曼體系結構的計算機,又叫存儲程序計算機,從硬件的角度來看,其工作模型是CPU依次讀取內存中的指令來完成工作。但它具體是如何完成程序員編寫的非線性執行的程序呢?本次課的實驗,以一段彙編代碼為例,詳細介紹了CPU計算模塊、寄存器和內存是如何配合工作的!
1 知識準備
1.1 彙編語言的五種尋址模式
- 寄存器尋址 registermode: %寄存器 例如:%edx 訪問寄存器edx
- 立即尋址 immediate: $數字例如:$0x123 數值0x123
- 直接尋址 direct:數字 例如:0x123訪問地址0x123指向的內存
- 間接尋址 indirect: (%寄存器) (%ebx) 例如:訪問寄存器ebx中的地址指向的內存
- 變址尋址 displaced:偏移量(%寄存器) 4(%ebx):訪問寄存器ebx中的地址再加4指向的內存;
1.2 幾個重要的彙編指令
Example instruction |
What it does |
Pushl %eax |
Subl $4, %esp //棧頂指針減4,棧在向下生長一個位置 Movl %eax, (%esp) //將eax中的值放入棧頂指針指向的內存位置 |
Popl %eax |
Movl (%esp), %eax //從棧頂指針指向的內存中的值放入eax中 Addl $4, %esp //棧頂指針加4,棧在向上收縮 |
Call 0x12345 |
Pushl %eip //ip壓棧 Movl $0x12345, %eip //將0x12345放入eip中 |
Ret |
Popl %eip //ip出棧 |
2. 實驗
2.1 實驗環境
Ubuntu 12.4 LTS 操作系統,vi 編輯器,gcc 編譯器 2.2 實驗步驟
vi main.c 輸入以下代碼
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
保存退出。 在命令行使用下面的命令,將這段c代碼編譯成彙編代碼
gcc –S –o main.s main.c -m32
vi main.s 打開彙編代碼文件,刪除其中以符號點 開頭的語句,得到如下可被計算機執行的彙編代碼:
接下來,就以圖解的方式來分析這段分析代碼。
- 彙編代碼分析
對照c代碼,很容易就知道,這段彙編代碼也有三個函數,代碼的執行從main函數的第一條語句開始。彙編代碼共有22行可執行的代碼,下面就用22張圖來展示每一條語句執行後,主要寄存器的指向和堆棧的狀態。其中,第0張圖,是程序還未執行時的狀態;第1張圖,是程序執行完第一條語句後的情形;第2張圖,是程序執行完第22條語句後的情形;依次類圖,直到第21張圖。最後一條語句是執行main函數中的 ret 語句,即將跳出了main函數,沒有給出示意圖。
在開始看圖之前,這裡先給出圖示中的一些說明:
- 內存中堆棧是向下生長的,即堆棧丁的地址<=堆棧底的地址;這裡為了方便描述,在程序開始執行時的,我們假定堆棧底和堆棧頂對應的地址編號是0,堆棧向下每增加一個位置,編號加1;
- EAX 寄存器用於存儲函數的返回值,圖示中會標出其值;其他三個寄存器EIP、EBP、ESP分別用箭頭來表示其當前值;EIP就是指令寄存器,太總是指向當前正在執行的彙編指令的下一條彙編指令;EBP、ESP分別指向當前堆棧的底部和頂部;
- 圖片中左邊為彙編源代碼,其中加粗的一行,表示當前正在執行的語句;圖0中沒有加粗的代碼行,那是當然的,圖0是程序準備執行第一條語句時的初始狀態;
- 圖片有點長,謝謝您的耐心 :) 再囉嗦下:旁白已經夠多了,如果還不是很明白,強烈推薦您去看在線視頻http://mooc.study.163.com/course/USTC-1000029000,那裡是動態的,而且孟老師講得非常詳細。
好了,上圖: