1. 程式人生 > >一個簡單程式的 gdb 反彙編 分析

一個簡單程式的 gdb 反彙編 分析

《深入理解計算機》的第三章已經看了兩遍了,一直都是在看書裡的程式,一些內容都是第一次接觸,也一直沒有在真正的Linux上用過,今天寫了下發現和書裡的還是有些出入,下面是我自己的理解。

gdb除了反彙編外,還可以用於程式碼的除錯。下面就簡單記錄下容易忘記的部分。

1.gcc -g -o code code.c   這裡不要忘記-g

2.watchpoint的概念

   VC用慣了就是不好,在vc裡ms沒有這個概念,至少我沒遇到過。在gdb中有一個watch命令,格式是watch + addr, 當記憶體中這個地址被寫入新內容時程式就會中斷。

3.檢視記憶體內容的方法

  X/4b  &temp

  這個意思就是輸出7個byte從temp所在地址開始的記憶體內容。

  比如我的temp = 0xfeff,那麼會輸出0xff 0xfe 0x00 0x00(小端地址法)

下面是自己花了點時間在Linux下寫了一個簡單的程式,並分析了下反彙編的程式碼,以及記憶體情況。

第一部分是c語言程式碼,編譯時用gcc -o code code.c,沒用-O2優化

第二部分是用gdb code的反彙編程式碼,';'後面是我自己的註釋,關於前4句的原理可以看最下面的文章,對理解比較重要。

有疑問的部分:

0x080483d8 <main+49>:   add    $0x10,%esp:從這裡往前看,似乎只有3次push操作,應該add 0xc,只是在這個程式中多add0x4也沒關係。

0x080483ec <main+69>:   sub    $0x8,%esp: 這個好像沒用到,好像也不是用於位元組對齊,因為在它前面一次呼叫也是從這個值開始的。

在這裡leave等價於:

mov %esp,%ebp;

pop %ebp

第三部分是記憶體變化的簡單示意圖:

-----------------------------------------------------------------------------------------------------------------------

下面來自於網上

C 程式碼:

int main(){
}

push %ebp
mov %esp,%ebp
sub $0x8,%esp//這句可以勉強理解,可能是為以後定義變數欲留位置,不知對否?
and $0xfffffff0,%esp//這裡就不好理解了,為什麼要把後面的4位全都置0???
mov $0x0, %eax
sub %eax,%esp
leave
ret

首先我覺得有必要把LEAVE替換一下,他等價於mov esp,ebp;pop ebp這兩條指令.因此有如下的解釋:

因此在執行了1之後的堆疊圖是: ----> 這個ARG其實就是int main(int argc, char argv[]),其實這裡還可以跟一個env來著
ARG[N]
..........
ARG[0]
EIP
EBP //ARG表示涵數的引數,對於MAIN涵數,POSIX定義了兩個,歷史上有三個的.
因此3語句的作用正好把ESP指向了為ARG[0]保留的地址,也就是跳過為EBP和EIP保留的位置
那麼4語句就是為ARG[0]ARG[1]保留位置了
由於MAIN被POSIX規定了兩個引數,因此不論有沒有都要保留兩個位置,又因為沒有ARG[2]和MAIN的區域性變數,所以把EAX置0,這樣一舉兩得,EAX又可以作為返回值,(通常涵數返回值是INT的,都用EAX表示).
其實大家可能還有不理解,那就是既然涵數的引數都已經壓棧了,直接用不就行了?為什麼還要拷貝過來呢?
這是C語言的要求,為了區域性化,從邏輯上,EBP(含)以上都是屬於主調涵數的,所以拷過來當然是必要的.否則C語言如何實現傳值而不改變主調涵數的值呢?
整個註釋如下:
1:push %ebp
2:mov %esp,%ebp
3:sub $0x8,%esp //為EBP,EIP保留位置
4:and $0xfffffff0,%esp //為ARG[0],ARG[1]保留位置
5:mov $0x0, %eax //一舉兩用,既作返回值,有表示不需要保留位置
6:sub %eax,%esp //保留0個位置
mov %epb ,%esp //恢復ESP到PUSH %EBP指令時的位置
pop %ebp //恢復EBP的值
ret