歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

函數調用分析

  測試環境:Red Hat Linux 7.2 注解 : eip 寄存器內容式當前執行指令的下一條指令的地址; mov eax, ebx 將寄存器eax內容移到ebx; 機器指令2字節。 leave 指令所做的操作相當於mov ebp, esp 然後 pop ebp; 機器指令1字節。 ret 指令所做的操作相當於pop eip; 機器指令1字節。 call addr 指令所做的操作相當於push eip 然後 jmp addr; 機器指令4字節。 jmp addr 執行所做的操作相當於 mov addr, eip; 機器指令2字節。 注意:我們所說的”相當於”是在功能上的等價,並不是實際機器指令的等價。 1. 編寫測試程序,如下: //file name : cc.c #include int foo(int fi,int fj) { int fk; fk=3; return(0); } int main() { int mi; int mj; mi=1; mj=2; foo(mi,mj); return(0); } 2. 對代碼進行編譯: gcc –g –o cc cc.c 3. 用gdb進行debug:gdb cc (1) 查看源程序: (gdb) list 4 { 5 int fk; 6 fk=3; 7 return(0); 8 } 9 int main() 10 { 11 int mi; 12 int mj; 13 mi=1; (gdb) 14 mj=2; 15 foo(mi,mj); 16 17 return(0); 18 } (2)查看匯編代碼: (gdb) disass main Dump of assembler code for function main: 0x8048444 : push %ebp 0x8048445 : mov %esp,%ebp 0x8048447 : sub $0x8,%esp 0x804844a : movl $0x1,0xfffffffc(%ebp) 0x8048451 : movl $0x2,0xfffffff8(%ebp) 0x8048458 : sub $0x8,%esp 0x804845b : pushl 0xfffffff8(%ebp) 0x804845e : pushl 0xfffffffc(%ebp) 0x8048461 : call 0x8048430 0x8048466 : add $0x10,%esp 0x8048469 : mov $0x0,%eax 0x804846e : leave 0x804846f : ret End of assembler dump. (gdb) disass foo Dump of assembler code for function foo: 0x8048430 : push %ebp 0x8048431 : mov %esp,%ebp 0x8048433 : sub $0x4,%esp 0x8048436 : movl $0x3,0xfffffffc(%ebp) 0x804843d : mov $0x0,%eax 0x8048442 : leave 0x8048443 : ret End of assembler dump. (3)在主函數設置斷點,並執行程序,讓程序在main函數剛開始時暫停: (gdb) break 9 Breakpoint 1 at 0x8048444: file c1.c, line 9. (gdb) run Starting program: /home/syf/p/cc Breakpoint 1, main () at c1.c:10 10 { (4)查看關鍵寄存器內容: (gdb) i reg esp esp 0xbffffaec 0xbffffaec (gdb) i reg ebp ebp 0xbffffb28 0xbffffb28 (gdb) i reg eip eip 0x8048444 0x8048444 (5)查看棧空間內容: (gdb) x/32xw 0xbffffae0 0xbffffae0: 0x080494e8 0x080495e4 0xbffffaf8 0x40046507 0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2


0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1 0xbffffb10: 0x00000000 0xbffffb5c 0x4015ec3c 0x40016300 0xbffffb20: 0x00000001 0x08048330 0x00000000 0x08048351 0xbffffb30: 0x08048444 0x00000001 0xbffffb54 0x080482bc 0xbffffb40: 0x080484b0 0x4000dc14 0xbffffb4c 0x40016944 0xbffffb50: 0x00000001 0xbffffc53 0x00000000 0xbffffc62 注意:ebp內容是:0xbffffb28,這個地址對應的內容是0x00000000; (6)執行一條機器指令(0x8048444 : push %ebp),即將ebp壓棧: (gdb) si 0x08048445 10 { (gdb) i reg esp esp 0xbffffae8 0xbffffae8 (gdb) i reg ebp ebp 0xbffffb28 0xbffffb28 (gdb) i reg eip eip 0x8048445 0x8048445 (gdb) x/12xw 0xbffffae0 0xbffffae0: 0x080494e8 0x080495e4 0xbffffb28 0x40046507 0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2 0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1 可以看到,ebp(內容是0xbffffb28)被壓到0xbffffae8處。 (7)執行一條機器指令(0x8048445 : mov %esp,%ebp),即將esp->ebp,查看寄存器內容和堆內容: (gdb) si 0x08048447 in main () at c1.c:10 10 { (gdb) i reg ebp ebp 0xbffffae8 0xbffffae8 (gdb) i reg esp esp 0xbffffae8 0xbffffae8 (gdb) i reg eip eip 0x8048447 0x8048447 (gdb) x/12xw 0xbffffae0 0xbffffae0: 0x080494e8 0x080495e4 0xbffffb28 0x40046507 0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2 0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1 可以看到,棧空間內容沒變,只是寄存器ebp變成了esp的值。 (8) 執行三條機器指令,為mi,mj賦值。 三條指令是: 0x8048447 : sub $0x8,%esp 0x804844a : movl $0x1,0xfffffffc(%ebp) 0x8048451 : movl $0x2,0xfffffff8(%ebp) 這三條指令的作用是為mi,mi賦值。 (gdb) si 13 mi=1; (gdb) si 14 mj=2; (gdb) si 15 foo(mi,mj); (gdb) i reg esp esp 0xbffffae0 0xbffffae0 (gdb) i reg ebp ebp 0xbffffae8 0xbffffae8 (gdb) i reg eip eip 0x8048458 0x8048458 (gdb) x/12xw 0xbffffae0 0xbffffae0: 0x00000002 0x00000001 0xbffffb28 0x40046507 0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2 0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1 可以看到,系統為mi,mj在棧內分配了空間(對應的地址分別是:0xbffffae4,0xbffffae0),並進行了賦值(分別是:0x00000001,0x00000002)。 (9)執行下一條指令(0x8048458 : sub $0x8,%esp),將堆棧棧空間加8個字節的空間,沒有意義,可以略去,在優化代碼時,將被略去。 注:優化代碼的方法是:gcc –O –o cc cc.c (gdb) si 0x0804845b 15 foo(mi,mj); (10)執行下兩條指令,堆將要調用的函數foo(int, int)的參數壓棧。兩條指令是: 0x804845b : pushl 0xfffffff8(%ebp) 0x804845e : pushl 0xfffffffc(%ebp) 這兩條指令的作用是對調用的函數foo(int fi, int fj)的參數壓棧。 (gdb) si 0x0804845e 15 foo(mi,mj);

(gdb) si 0x08048461 15 foo(mi,mj); (gdb) i reg esp esp 0xbffffad0 0xbffffad0 (gdb) i reg ebp ebp 0xbffffae8 0xbffffae8 (gdb) i reg eip eip 0x8048461 0x8048461 (gdb) x/12xw 0xbffffad0 0xbffffad0: 0x00000001 0x00000002 0xbffffaf8 0x08048411 0xbffffae0: 0x00000002 0x00000001 0xbffffb28 0x40046507 0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2 (11)執行下一條指令:0x8048461 : call 0x8048430 。 這條call指令可以分解成兩條指令:push eip;jmp 0x8048430。 其作用是保存main函數的foo(int, int)函數調用後的下一條指令的地址(0x8048466),以便foo(int ,int)函數調用返回後在main()函數繼續指令,同時還要跳轉到foo(int, int)的地址(0x8048430),以便執行函數foo(int ,int); 注意:此時的eip為0x8048466,對應的指令是:0x8048466 : add $0x10,%esp (gdb) si foo (fi=1, fj=-1073743020) at c1.c:4 4 { (gdb) i reg esp esp 0xbffffacc 0xbffffacc (gdb) i reg ebp ebp 0xbffffae8 0xbffffae8 (gdb) i reg eip eip 0x8048430 0x8048430 (gdb) x/12xw 0xbffffacc 0xbffffacc: 0x08048466 0x00000001 0x00000002 0xbffffaf8 0xbffffadc: 0x08048411 0x00000002 0x00000001 0xbffffb28 0xbffffaec: 0x40046507 0x00000001 0xbffffb54 0xbffffb5c (12)執行下兩條指令,並查看寄存器和堆內容: 這兩條指令是: 0x8048430 : push %ebp 0x8048431 : mov %esp,%ebp 這兩條指令的作用是對main函數中的 ebp進行壓棧,以便返回時用(和第15步對應),同時將esp賦值為ebp(ebp->esp),一般本函數返回(和第16步對應)。 (gdb) si 0x08048431 4 { (gdb) si 0x08048433 in foo (fi=1, fj=2) at c1.c:4 4 { (gdb) i reg esp esp 0xbffffac8 0xbffffac8 (gdb) i reg ebp eb



(gdb) si 0x08048431 4 { (gdb) si 0x08048433 in foo (fi=1, fj=2) at c1.c:4 4 { (gdb) i reg esp esp 0xbffffac8 0xbffffac8 (gdb) i reg ebp eb



Copyright © Linux教程網 All Rights Reserved