王雪 原創作品轉載請注明出處 《linux內核分析》MOOC課程 http://mooc.study.163.com/course/USTC-1000029000
一、理論基礎 (1)馮諾依曼體系結構:存儲程序計算機的工作模型 從硬件角度看程序的執行過程:其中ip(instruction pointer)為CPU中的寄存器,指向內存中的某一塊,CPU執行指令時,會從IP中取出一條指令后執行,執行過后,IP自加一(增加一個指令的長度),取下一條指令執行。 從程序員的角度看: CPU是一個大的for循環,不停地執行next instruction命令,存儲器主要負責數據和代碼等信息的存儲,CPU與Main Memory通過總線進行連接。 注意: 1.程序員不可以直接修改IP的值,只能通過一些指令,如CALL、RET、JMP等間接修改IP的值 2.IP在32位機器中表示為eip(本次實驗以32位作為分析),在64位機器中表示為rip。 (2)x86匯編基礎知識(32位機器) 一.寄存器:在x86體系中,寄存器可分為通用寄存器、段寄存器、標志寄存器 1通用寄存器: EAX:累加器 EBX:基地址寄存器 ECX:計數寄存器 EDX:數據寄存器 EBP:堆棧基指針 ESI,EDI:變址寄存器 ESP:堆棧頂指針 2.段寄存器:CS,DS,ES,SS,FS,GS 3.標志寄存器 CPU在實際取指令時根據CS:eip來準確確定某一個指令在內存中的地址。 二:匯編指令(以常見的MOV,PUSH,POP,CALL等進行說明) (1)MOV指令及尋址方式 1.MOV指令,能實現以下操作: ① CPU內部寄存器之間數據的任意傳送(除了碼段寄存器CS和指令指針IP以外)。 ② 立即數傳送至CPU內部的通用寄存器組(即AX、BX、CX、DX、BP、SP、SI、DI),給這些寄存器賦初值。 ③ CPU內部寄存器(除了CS和IP以外)與存儲器(所有尋址方式)之間的數據傳送,可以實現一個字節或一個字的傳送。 ④ 能實現用立即數給存儲單元賦初值。 movb,movl,movw,movq分別針對8位,16位,32位,64位系統 (2)pushl和pop指令,pushl用于入棧(棧的擴張),pop用于出棧(棧的收縮) 棧是向下生長的,也就是說棧底位于高地址,棧頂位于低地址,用ebp指向棧底,用esp指向棧頂。 入棧操作:pushl %eax完成的操作包括兩步
出棧操作:pop %eax完成的操作包括兩步
movl (%esp),%eaxaddl $4,%esp(3)CALL主要用于函數調用 call 0x12345 完成的操作包括兩步
push %eipmovl %0x12345,%eip(4)RET指令用于恢復操作,完成pop %eip操作 (5)enter和leave操作 enter 置為空棧,完成的操作包括兩步
pushl %ebpmovl %esp,%ebpleave用于撤銷函數調用堆棧,完成的操作包括兩步
movl %ebp,%esppopl %ebp注: (1)函數調用堆棧是由邏輯上多個堆棧疊加起來的(比如函數的嵌套) (2)函數的返回值默認使用eax寄存器存儲返回給上一級函數 (3)一定要注意的是eip不能由程序員直接修改,程序員想修改eip只能通過特殊指令間接修改。 2.尋址方式 (1)寄存器尋址(不訪問內存) movl % eax,% edx <==>edx= eax,將eax中的值賦值給edx (2)立即尋址(不訪問內存)用$表示立即數 movl $ 0x1234,%edx <==>edx = 0x1234 (3)直接尋址 movl 0x123,%edx <==>edx = * (int32_t*)0x123 將內存地址0x123所指向的內存數據賦值給edx (4)間接尋址 movl (%ebx),%edx <==>edx = * (int32_t*)ebx (5)變址尋址 movl 4(%ebx),%edx <==>edx = * (int32_t*)(ebx+4) 二、通過實驗分析計算機是如何工作的 (1)實驗代碼與截圖 在實驗樓的linux終端下創建一個main.c文件(注意實驗樓的環境為64位) 利用指令(64位下生成32位的匯編文件)
進入main.s匯編文件,去掉所有以.開始的代碼行(以.開始的代碼是連接時的輔助信息)得到純匯編代碼
可以看到剛才介紹過的指令操作。 (2)實驗分析——-對執行過程的分析 與C語言類似,匯編代碼的入口也為main函數 初始時棧的狀態
進入main函數,執行18行代碼
執行后棧的狀態 0標號下下存放ebp的內容,esp指向標號1處
esp和ebp指向相同位置
執行call時實際執行兩個動作,pushl %eip ; movl f ,%eip 此時eip指向第23行代碼 執行后,eip指向f 跳轉到f中執行:
在執行過movl%esp,%ebp后,esp和ebp置于相同的標號處(4) 執行subl $4,%esp,esp向下移動到標號5
movl 8(%ebp),%eax 變址尋址,將ebp(此處為標號4)加8(向上移動兩個標號,也就是標號2處)的值賦給eax,所以%eax = 8 movl %eax,(%esp),將eax的內容也就是8賦值到esp下也就是標號5處
執行call時實際執行兩個動作,pushl %eip ; movl g,%eip 此時eip指向第15行代碼leave的位置,此時eip指向g跳轉到g中去執行 在g中執行:
pushl %ebp同上,將esp向下移動,將ebp(標號為4)壓棧 movl %esp,%ebp esp和ebp指向相同的位置
強ebp向上移動兩個標號的值(也就是8)賦給eax
addl $57,%eaxpopl %ebpret%eax中存放的值為8,addl操作,將eax的值與立即數57相加,結果為65,將65存回到eax popl %ebp,將ebp的值放回到ebp,執行效果:ebp重新指向標號為4的位置,同時esp減4 ret執行popl %eip,也就是說esp向上移動指向5的標號的位置,同時eip指向15行指令的位置(call的下一條指令)
回到f中執行
leave執行兩條指令, movl %ebp,%esp popl %ebp 首先,將esp指向ebp相同的位置(也就是標號4的位置),popl %ebp,將ebp出棧,此時ebp指回標號1的位置,由于popl,esp向上移動
ret執行popl %eip 由于popl,esp向上增加一個,指向標號2,eip指向第23行代碼處
eip執行第23行代碼,回到main處,執行
eax此時的值為65,執行addl,65+1 = 66,將66存回到eax。 函數返回值默認使用eax來存儲 執行leave,分為兩步, movl %ebp,%esp,將esp指向ebp的位置,popl %ebp,將ebp出棧(ebp指向0的位置),popl指令時esp向上移動,也就是說esp,ebp均指向標號0的位置,棧回到main函數最初的狀態。 ret,return的是main函數之前的堆棧,此處由操作系統管理。 棧向下生長,向上還原,增增減減,將程序變為指令流,從CPU上流過。 此時,小程序執行完成 三、實驗總結–對計算機如何工作的理解 1.計算機的基本原理是存儲程序和程序控制,預先要把指揮計算機如何進行操作的指令序列(稱為程序)和原始數據通過輸入設備輸送到計算機內存貯器中。每一條指令中明確規定了計算機從哪個地址取數,進行什么操作,然后送到什么地址去等步驟。 計算機在運行時,先從內存中取出第一條指令,通過控制器的譯碼,按指令的要求,從存儲器中取出數據進行指定的運算和邏輯操作等加工,然后再按地址把結果送到內存中去。接下來,再取出第二條指令,在控制器的指揮下完成規定操作。依此進行下去。直至遇到停止指令。簡單來說就是CPU負責處理和運算,存儲器負責保存指令和數據。通過操作系統得調度和安排,不停地進行取址、譯碼、執行的循環。 2.匯編代碼是什么? 計算機語言的發展過程從機器語言(計算機能直接識別的二進制0和1的組合)->匯編語言(為了減輕使用機器語言編程的痛苦,人們進行了一種有益的改進:用一些簡潔的英文字母、符號串來替代一個特定的指令的二進制串,依賴于硬件)->高級語言(接近于數學語言或人的自然語言,同時又不依賴于計算機硬件,編出的程序能在所有機器上通用)。 我們編寫了一個小程序,比如上面實驗中寫到的main.c文件,編譯器執行的過程,
可執行的二進制文件是計算機“認識”的文件,可以直接執行。 3.以上便是我對這次實驗的總結,計算機很“單純”,它可以執行很多復雜的指令,但它也是被“告訴”要執行什么,才會去執行什么,通過對匯編語言的分析可以方便我們理解計算機處理的過程,了解計算機如何工作等等,這也會成為我今后學習的重點。感謝為我們辛苦準備課程的老師!
新聞熱點
疑難解答
圖片精選