当前位置: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
[1] [2] [3] 下一页 

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);
上一页 [1] [2] [3] 下一页 

(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

(出处:http://www.sheup.com)


上一页 [1] [2] [3] 

(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

(出处:http://www.sheup.com)


上一页 [1] [2] [3] [4]