網易雲課堂linux內核分析(一)

這是網易雲課堂linux內核分析課程的實驗報告linux

實驗的內容是經過編寫一段簡單的c程序,並分析其彙編代碼,以瞭解計算機是如何運行程序。vim

程序hello.c源代碼:bash

1 #include <stdio.h>
  2 
  3 int bar(int a)
  4 {
  5     return a;
  6 }
  7 
  8 int foo(a)
  9 { 
 10     return bar(a) + 1;
 11 }
 12 
 13 int main()
 14 {
 15     return foo(10) + 1;
 16 }

在bash下輸入以下指令,得到源代碼的彙編hello.s:函數

gcc -S -o hello.s hello.c -m32

因爲本次試驗只須要用到源代碼對應的彙編指令,因此能夠把hello.s中以.開頭的行刪除。在vim下可使用:spa

:%s/\s*\..*//g     #將以"."開頭的行換爲空行
:%g/^$/d           #刪除空行

操做後獲得如下代碼:code

1 bar:
  2     pushl   %ebp
  3     movl    %esp, %ebp
  4     movl    8(%ebp), %eax
  5     popl    %ebp
  6     ret
  7 foo:
  8     pushl   %ebp
  9     movl    %esp, %ebp
 10     subl    $4, %esp
 11     movl    8(%ebp), %eax
 12     movl    %eax, (%esp)
 13     call    bar
 14     addl    $1, %eax
 15     leave
 16     ret
 17 main:
 18     pushl   %ebp
 19     movl    %esp, %ebp
 20     subl    $4, %esp
 21     movl    $10, (%esp)
 22     call    foo
 23     addl    $1, %eax
 24     leave
 25     ret

下面對彙編代碼進行分析,從main函數開始:
1八、19行的pushlmovl指令至關於enter指令,用於保存前一個棧的信息,同時爲main函數開闢一個空棧。pushl將前一個棧的基地址保存,movlebp賦值爲前一個棧的棧頂,同時也是main棧的基地址。esp做爲main棧的棧頂。完成了上面兩部,main棧就算建成了。接下來開始執行源代碼裏的東西了。ip

20、21行。subl爲函數的棧開了空間,用來存放foo須要的參數。movl將參數放在該空間。而後執行call指令,跳到foo函數中,也就是eip要變成foo的地址。注意call指令要把當前eip壓棧(pushl %eip)。而後把目光轉到foo函數中,也就是第7行。和main函數開頭同樣,須要保存上一個棧的信息,同時爲本身開創一個棧。pushlmovl指令作了這件事。接着放置bar須要的參數在本身的棧中(就是剛剛在mian函數時放的那個參數)。完成後執行call指令跳到bar函數。內存

bar函數終於再也不調用別的函數,而是獲取foo給的參數(第4行,這時候ebp就是foo棧的棧頂)。將參數賦值給eax後進行結束函數的工做。第5行的popl指令將esp加4,變爲前一個棧(foo)的棧頂;ebp得到出棧的內容,也就是前一個棧的基址。foo的棧完成恢復。最後ret指令跳回foo函數中(至關於popl %eip),bar函數就正式結束了。回到foo函數後,根據源代碼要給返回值加1(14行),緊接着要結束foo。和結束bar函數過程同樣,結束foo函數就是執行leave而後回跳到main中。main函數爲返回值加1(23行),以一樣的流程退出main函數。程序結束。get

注意1:函數的棧在內存中是從高地址向低地址增加的,因此pushlesp要減4,而poplesp要加4
注意2:leave指令至關於movl %esp %ebppopl %ebp
因爲bar的代碼沒有使用到變量,因此在建立好函數的棧後,espebp是相等的,不須要leave指令。函數foo和main中建立了變量,並存放在棧中,修改了esp,所以退出函數時要還原esp的。因此在退出函數的時候要使用leave
注意3:call指令壓棧的內容在調用函數的棧中。畢竟call指令執行在開闢新的函數棧指令以前。it

理解:
程序就是由一條條彙編代碼組成。這些彙編代碼執行了運算,使用了內存空間。c語言中的函數,就是彙編中將當前寄存器保存到內存中,而後轉跳到另一處執行,執行完成後跳回原來地方,並恢復寄存器內容。而c語言屏蔽了這一過程,提供了抽象,咱們只須要專一於函數要實現什麼功能,不須要關注如何實現函數。

過程截圖
源代碼和彙編

xxtsmooc
原創做品轉載請註明出處
《Linux內核分析》MOOC課程
http://mooc.study.163.com/course/USTC-1000029000

相關文章
相關標籤/搜索