通過分析一個C程序的彙編指令執行過程,理解計算機的工作。

鄭德倫 原創作品轉載請註明出處 《Linux內核分析》MOOC課程
http://mooc.study.163.com/course/USTC-1000029000
首先創建一個C程序的文件,main.c這裏寫圖片描述
將一段C程序代碼使用命令 gcc –S –o main.s main.c -m32編譯成彙編代碼。
我們刪除點開頭的輔助信息來得到彙編代碼。
這裏寫圖片描述
這樣就可以清晰的看到main.c文件的彙編代碼了。
根據彙編代碼我們來一步一步分析程序在執行過程中堆棧的變化過程。
首先程序從main開始執行,最初的堆棧爲空,esp和ebp指向同一個地址:
這裏寫圖片描述
然後執行18:pushl %ebp, 保存舊的ebp地址,把舊的ebp地址壓入堆棧,此時堆棧情況如下:
這裏寫圖片描述
執行19: movl %esp, %ebp 將esp的值放入ebp中,執行之後堆棧的狀態如下,此時ebp和esp又指向同一地址
這裏寫圖片描述
執行20:subl $4, %esp 將esp的值減4,此時堆棧的情況如下:
這裏寫圖片描述
執行21: movl $8, (%esp), 將立即數8放入esp所指向的地址中。此時堆棧情況如下:
這裏寫圖片描述
執行22:call f, 將eip(23)壓入棧中,然後跳轉到f:處執行,此時堆棧情況如下:
這裏寫圖片描述
執行9:pushl %ebp,將老的ebp地址壓入棧中,此時堆棧情況如下:
這裏寫圖片描述
執行10:movl %esp, %ebp,將esp的地址賦給ebp,此時堆棧情況如下:
這裏寫圖片描述
執行11: subl $4, %esp, 將esp的值減4,此時堆棧的情況如下:
這裏寫圖片描述
執行12:movl 8(%ebp), %eax, 將ebp + 8所存放的值,放入eax寄存器中,ebp+8存放的值爲8,所以此時eax寄存器存放的值爲8. 此時堆棧無變化
執行13:movl %eax, (%esp), 將eax寄存器存放的值,放入esp所執行的地址中。此時堆棧的變化如下:
這裏寫圖片描述
執行14: call g, 將eip(15)壓入堆棧,並且跳轉到函數g執行,此時堆棧的變化如下:這裏寫圖片描述
執行2: pushl %ebp, 將老的ebp壓入堆棧,此時堆棧的變化如下:
這裏寫圖片描述
執行3: movl %esp, %ebp, 將esp存放的值放入ebp中。此時堆棧的變化如下:
這裏寫圖片描述
執行4:movl 8(%ebp), %eax, 將ebp + 8所指向的內容放入eax寄存器中,ebp + 8存放的內容爲8,所以eax寄存器存放的內容爲8,此時堆棧無變化。
執行5: addl $11, %eax, 將eax寄存器的內容與11相加,並且將結果放入eax寄存器中, 此時eax寄存器爲19。堆棧無變化
執行6:popl %ebp,將棧頂元素彈出,放入ebp寄存器中,即將old ebp還原。此時堆棧的變化如下:
這裏寫圖片描述
執行7:ret, 程序將執行15行代碼,此時堆棧變化如下:
這裏寫圖片描述
執行15: leave 相當於
movl %ebp, %esp
popl %ebp
將ebp寄存器的值放入esp寄存器中,並且彈出棧頂元素,存放到ebp中。此時堆棧變化如下:
這裏寫圖片描述
執行16:ret ,程序將執行23行代碼,此時堆棧變化如下:
這裏寫圖片描述
執行23: addl $7, %eax, 將eax寄存器的值加上7,並且將結果放入eax寄存器。
執行此指令前,eax寄存器的值爲19(上面已經計算得出),執行之後,eax寄存器變爲26,此時堆棧無變化
執行24:leave 相當於:
movl %ebp, %esp
popl %ebp
此時堆棧還原到最初的狀態,如下圖所示:
這裏寫圖片描述
最後執行25: ret 程序退出。

通過分析前面的C程序的執行過程,可以看出計算機的執行過程,就是CPU讀取一條一條的指令來進行執行,指令寄存器EIP就指向下一條需要執行的指令。
而編譯器將高級語言的代碼編譯成CPU可以理解的二進制代碼。任何複雜的程序,都最終變成一條條簡單的計算機指令堆疊而成。
程序執行過程中一些數據的保存,大都是通過堆棧進行的。堆棧寄存器EBP和ESP共同確定了一個堆棧的棧底和棧頂。
通過本節課程學習,初步瞭解了一個C程序在指令級的運行方式,理解了程序在執行過程中,堆棧的變化過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章