《coredump問(wèn)題原理探究》Linux x86版6.4節(jié)虛函數(shù)
來(lái)源:程序員人生 發(fā)布時(shí)間:2015-03-02 08:42:22 閱讀次數(shù):3171次
在上1節(jié)已探究了類的成員變量的排列,現(xiàn)在看1下虛函數(shù)表和成員變量的排列及虛函數(shù)之間的排列.
先看1個(gè)例子:
1 #include <stdio.h>
2 class xuzhina_dump_c06_s3
3 {
4 private:
5 int m_a;
6 public:
7 xuzhina_dump_c06_s3() { m_a = 0; }
8 virtual void inc() { m_a++; }
9 virtual void dec() { m_a--; }
10 virtual void print()
11 {
12 printf( "%d
", m_a );
13 }
14 };
15
16 int main()
17 {
18 xuzhina_dump_c06_s3* test = new xuzhina_dump_c06_s3;
19 if ( test != NULL )
20 {
21 test->inc();
22 test->inc();
23 test->print();
24 }
25 return 0;
26 }
匯編代碼:
(gdb) disassemble main
Dump of assembler code for function main:
0x08048560 <+0>: push %ebp
0x08048561 <+1>: mov %esp,%ebp
0x08048563 <+3>: push %ebx
0x08048564 <+4>: and $0xfffffff0,%esp
0x08048567 <+7>: sub $0x20,%esp
0x0804856a <+10>: movl $0x8,(%esp)
0x08048571 <+17>: call 0x8048450 <_Znwj@plt>
0x08048576 <+22>: mov %eax,%ebx
0x08048578 <+24>: mov %ebx,(%esp)
0x0804857b <+27>: call 0x80485cc <_ZN19xuzhina_dump_c06_s3C2Ev>
0x08048580 <+32>: mov %ebx,0x1c(%esp)
0x08048584 <+36>: cmpl $0x0,0x1c(%esp)
0x08048589 <+41>: je 0x80485c1 <main+97>
0x0804858b <+43>: mov 0x1c(%esp),%eax
0x0804858f <+47>: mov (%eax),%eax
0x08048591 <+49>: mov (%eax),%eax
0x08048593 <+51>: mov 0x1c(%esp),%edx
0x08048597 <+55>: mov %edx,(%esp)
0x0804859a <+58>: call *%eax
0x0804859c <+60>: mov 0x1c(%esp),%eax
0x080485a0 <+64>: mov (%eax),%eax
0x080485a2 <+66>: mov (%eax),%eax
0x080485a4 <+68>: mov 0x1c(%esp),%edx
0x080485a8 <+72>: mov %edx,(%esp)
0x080485ab <+75>: call *%eax
0x080485ad <+77>: mov 0x1c(%esp),%eax
0x080485b1 <+81>: mov (%eax),%eax
0x080485b3 <+83>: add $0x8,%eax
0x080485b6 <+86>: mov (%eax),%eax
0x080485b8 <+88>: mov 0x1c(%esp),%edx
0x080485bc <+92>: mov %edx,(%esp)
0x080485bf <+95>: call *%eax
0x080485c1 <+97>: mov $0x0,%eax
0x080485c6 <+102>: mov -0x4(%ebp),%ebx
0x080485c9 <+105>: leave
0x080485ca <+106>: ret
End of assembler dump.
由上面代碼可知,履行完了構(gòu)造函數(shù),test的m_a的值,會(huì)變成0。且由上面匯編可以看到this指針在調(diào)用構(gòu)造函數(shù)前后,是放在ebx寄存器。
在0x08048578,0x08048580都打斷點(diǎn),看1下this指針?biāo)赶虻牡刂肥欠袷沁@樣的結(jié)果。
(gdb) tbreak *0x08048578
Temporary breakpoint 1 at 0x8048578
(gdb) tbreak *0x08048580
Temporary breakpoint 2 at 0x8048580
(gdb) r
Starting program: /home/buckxu/work/6/3/xuzhina_dump_c6_s3
Temporary breakpoint 1, 0x08048578 in main ()
(gdb) x /4x $ebx
0x804a008: 0x00000000 0x00000000 0x00000000 0x00020ff1
(gdb) c
Continuing.
Temporary breakpoint 2, 0x08048580 in main ()
(gdb) x /4x $ebx
0x804a008: 0x080486d0 0x00000000 0x00000000 0x00020ff1
非常奇怪,依照上1節(jié)的內(nèi)容,地址0x804a008應(yīng)當(dāng)寄存m_a,會(huì)初始化為0.究竟類xuzhina_dump_c06_s3的構(gòu)造函數(shù)做了甚么事情?而0x080486d0是甚么東西來(lái)的?
看1下類xuzhina_dump_c06_s3的構(gòu)造函數(shù):
(gdb) disassemble _ZN19xuzhina_dump_c06_s3C2Ev
Dump of assembler code for function _ZN19xuzhina_dump_c06_s3C2Ev:
0x080485cc <+0>: push %ebp
0x080485cd <+1>: mov %esp,%ebp
0x080485cf <+3>: mov 0x8(%ebp),%eax
0x080485d2 <+6>: movl $0x80486d0,(%eax)
0x080485d8 <+12>: mov 0x8(%ebp),%eax
0x080485db <+15>: movl $0x0,0x4(%eax)
0x080485e2 <+22>: pop %ebp
0x080485e3 <+23>: ret
End of assembler dump.
由構(gòu)造函數(shù)的匯編可知,0x80486d0這個(gè)值是在構(gòu)造函數(shù)設(shè)置,但還不清楚是甚么東西。而
0x080485d8 <+12>: mov 0x8(%ebp),%eax
0x080485db <+15>: movl $0x0,0x4(%eax)
卻恰好對(duì)應(yīng)了
7 xuzhina_dump_c06_s3() { m_a = 0; }
也就是說(shuō),類xuzhina_dump_c06_s3的第1個(gè)成員變量m_a放在偏移this指針的地方,那末0x80486d0是甚么東西,占了m_a的位置呢?
重新看1下main函數(shù)的匯編:
(gdb) disassemble main
Dump of assembler code for function main:
0x08048560 <+0>: push %ebp
0x08048561 <+1>: mov %esp,%ebp
0x08048563 <+3>: push %ebx
0x08048564 <+4>: and $0xfffffff0,%esp
0x08048567 <+7>: sub $0x20,%esp
0x0804856a <+10>: movl $0x8,(%esp)
0x08048571 <+17>: call 0x8048450 <_Znwj@plt>
0x08048576 <+22>: mov %eax,%ebx
0x08048578 <+24>: mov %ebx,(%esp)
0x0804857b <+27>: call 0x80485cc <_ZN19xuzhina_dump_c06_s3C2Ev>
=> 0x08048580 <+32>: mov %ebx,0x1c(%esp)
0x08048584 <+36>: cmpl $0x0,0x1c(%esp)
0x08048589 <+41>: je 0x80485c1 <main+97>
0x0804858b <+43>: mov 0x1c(%esp),%eax
0x0804858f <+47>: mov (%eax),%eax
0x08048591 <+49>: mov (%eax),%eax
0x08048593 <+51>: mov 0x1c(%esp),%edx
0x08048597 <+55>: mov %edx,(%esp)
0x0804859a <+58>: call *%eax
0x0804859c <+60>: mov 0x1c(%esp),%eax
0x080485a0 <+64>: mov (%eax),%eax
0x080485a2 <+66>: mov (%eax),%eax
0x080485a4 <+68>: mov 0x1c(%esp),%edx
0x080485a8 <+72>: mov %edx,(%esp)
0x080485ab <+75>: call *%eax
0x080485ad <+77>: mov 0x1c(%esp),%eax
0x080485b1 <+81>: mov (%eax),%eax
0x080485b3 <+83>: add $0x8,%eax
0x080485b6 <+86>: mov (%eax),%eax
0x080485b8 <+88>: mov 0x1c(%esp),%edx
0x080485bc <+92>: mov %edx,(%esp)
0x080485bf <+95>: call *%eax
0x080485c1 <+97>: mov $0x0,%eax
0x080485c6 <+102>: mov -0x4(%ebp),%ebx
0x080485c9 <+105>: leave
0x080485ca <+106>: ret
End of assembler dump.
由
0x0804857b <+27>: call 0x80485cc <_ZN19xuzhina_dump_c06_s3C2Ev>
0x08048580 <+32>: mov %ebx,0x1c(%esp)
可知,esp+0x1c用來(lái)寄存this指針。
再看1下這幾段指令:
0x0804858b <+43>: mov 0x1c(%esp),%eax
0x0804858f <+47>: mov (%eax),%eax
0x08048591 <+49>: mov (%eax),%eax
0x08048593 <+51>: mov 0x1c(%esp),%edx
0x08048597 <+55>: mov %edx,(%esp)
0x0804859a <+58>: call *%eax
0x0804859c <+60>: mov 0x1c(%esp),%eax
0x080485a0 <+64>: mov (%eax),%eax
0x080485a2 <+66>: mov (%eax),%eax
0x080485a4 <+68>: mov 0x1c(%esp),%edx
0x080485a8 <+72>: mov %edx,(%esp)
0x080485ab <+75>: call *%eax
0x080485ad <+77>: mov 0x1c(%esp),%eax
0x080485b1 <+81>: mov (%eax),%eax
0x080485b3 <+83>: add $0x8,%eax
0x080485b6 <+86>: mov (%eax),%eax
0x080485b8 <+88>: mov 0x1c(%esp),%edx
0x080485bc <+92>: mov %edx,(%esp)
0x080485bf <+95>: call *%eax
由因而順序結(jié)構(gòu),可知,這3段指令恰好對(duì)應(yīng)
21 test->inc();
22 test->inc();
23 test->print();
分析1下第3段匯編:
0x080485ad <+77>: mov 0x1c(%esp),%eax
0x080485b1 <+81>: mov (%eax),%eax
0x080485b3 <+83>: add $0x8,%eax
0x080485b6 <+86>: mov (%eax),%eax
0x080485b8 <+88>: mov 0x1c(%esp),%edx
0x080485bc <+92>: mov %edx,(%esp)
0x080485bf <+95>: call *%eax
可見(jiàn)eax正好是放著print這個(gè)虛函數(shù)的指針。而這個(gè)指針終究是由esp+0x1c來(lái)獲得。由
0x080485ad <+77>: mov 0x1c(%esp),%eax
0x080485b1 <+81>: mov (%eax),%eax
可知,正好是從this指針的第1個(gè)成員取出來(lái)的,也就是說(shuō),這個(gè)成員是虛函數(shù)表指針。根據(jù)上面的分析,可知這個(gè)虛函數(shù)表指針的值是0x80486d0。來(lái)驗(yàn)證1下,它是否是虛函數(shù)表指針。
(gdb) x /4x 0x80486d0
0x80486d0 <_ZTV19xuzhina_dump_c06_s3+8>: 0x080485e4 0x080485f8 0x0804860c 0x75783931
(gdb) shell c++filt _ZTV19xuzhina_dump_c06_s3
vtable for xuzhina_dump_c06_s3
(gdb) info symbol 0x080485e4
xuzhina_dump_c06_s3::inc() in section .text of /home/buckxu/work/6/3/xuzhina_dump_c6_s3
(gdb) info symbol 0x080485f8
xuzhina_dump_c06_s3::dec() in section .text of /home/buckxu/work/6/3/xuzhina_dump_c6_s3
(gdb) info symbol 0x0804860c
xuzhina_dump_c06_s3::print() in section .text of /home/buckxu/work/6/3/xuzhina_dump_c6_s3
可見(jiàn),0x80486d0所指向正是虛函數(shù)表,且里面的表項(xiàng)順序正好和虛函數(shù)的聲明順序1樣。
由上面分析,test所指向的對(duì)象的內(nèi)存布局以下圖:
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)