2012年10月15日 星期一

c++ virtual function table

1st edit: 2012/09/17

env:
g++ 4.7.1
git commit: 6d785159984cd0587afdff69b05d41a3634777f9
(path: simple_os/cpp_runtime/global_object/dos_cpp)

研究 c++ runtime 之後, 我才了解 c++ compiler 的邪惡, 它為了我們的程式碼插了很多額外的 code, 令人又愛又恨。在大部份狀況下, 我們不用管這些事情, 搞懂這些東西對我來說純粹是個機緣。有點中毒, 愈挖愈深。

建議先看這篇: C++ 虚函数表解析

這次的東西很麻煩, 我花了好些功夫才從反組譯中搞懂。

cppb.cpp
 1 /*
 2  * c++ runtime test
 3  */
 4 
 5 __asm__(".code16gcc\n");
 6 #include "io.h"
 7 #include "obj.h"
 8 #include "dos_io.h"
 9 
10 #define BOCHS_MB __asm__ __volatile__("xchg %bx, %bx");
11 
12 extern "C" int cpp_main(void)
13 {
14   //BOCHS_MB  
15   const char *ver=__VERSION__;
16   print("io hello cpp class\r\n");
17   print("io g++ version: ");
18   print(ver);
19   print("\r\n");
20   Io io;
21   DeriveIo d_io;
22   Io *p_io = &io;
23   Io *p_d_io = &d_io;
24   p_io->fun1();
25   p_d_io->fun1();
26 
27   return 0;
28 }

io.h
 1 __asm__(".code16gcc\n");
 2 #ifndef IO_H
 3 #define IO_H
 4 
 5 class Io
 6 {
 7   public:
 8     Io();
 9     ~Io();
10     void print(const char   *s);
11     virtual void fun1()
12     {
13       print("Io::func1\r\n");
14     }
15   private:
16     const char *str_;
17 };
18 
19 class Ab
20 {
21   public:
22     Ab(int i);
23     ~Ab();
24     void print(const char   *s);
25   private:
26     const char *str_;
27     int i_;
28 };
29 
30 class DeriveIo : public Io
31 {
32   public:
33     virtual void fun1()
34     {
35       print("DeriveIo::func1\r\n");
36     }
37   private:
38 };
39 
40 #endif

Io::fun1(), DerivIo::fun1() 是 virtual function。根據 c++ 書籍提到的, 會有一個 virtual table 簡稱 vtable。我們來找看看它在哪裡。

objdump -d -m i8086 cppb.elf (只列出相關部份)
  1 
  2 cppb.elf:     file format elf32-i386
  3 
  4 
  5 Disassembly of section .text:
  6 
  7 00000100 <_start-0x3>:
  8  100: e9 00 00              jmp    103 <_start>
  9 
 10 00000103 <_start>:
 11  103: 87 db                 xchg   %bx,%bx
 12  105: 8c c8                 mov    %cs,%ax
 13  107: 8e d8                 mov    %ax,%ds
 14  109: 8e d0                 mov    %ax,%ss
 15  10b: b8 00 b8              mov    $0xb800,%ax
 16  10e: 8e e8                 mov    %ax,%gs
 17  110: b0 ef                 mov    $0xef,%al
 18  112: 66 e8 7e 00 00 00     calll  196 <disp_al>
 19  118: b0 12                 mov    $0x12,%al
 20  11a: 66 e8 76 00 00 00     calll  196 <disp_al>
 21  120: 8b 1e a4 0a           mov    0xaa4,%bx
 22  124: 66 e8 ae 00 00 00     calll  1d8 <disp_bx>
 23  12a: b0 aa                 mov    $0xaa,%al
 24  12c: 66 e8 64 00 00 00     calll  196 <disp_al>
 25  132: e8 40 00              call   175 <init_bss_asm>
 26  135: 8b 1e a4 0a           mov    0xaa4,%bx
 27  139: 66 e8 99 00 00 00     calll  1d8 <disp_bx>
 28  13f: 66 e8 11 00 00 00     calll  156 <init_cpp_global_asm>
 29  145: 66 e8 a1 00 00 00     calll  1ec <cpp_main>
 30  14b: 66 e8 6f 04 00 00     calll  5c0 <g_dtors>
 31  151: b8 00 4c              mov    $0x4c00,%ax
 32  154: cd 21                 int    $0x21
 33 


101  ...
102 
103 000001ec <cpp_main>:
104  1ec: 66 55                 push   %ebp
105  1ee: 66 89 e5              mov    %esp,%ebp
106  1f1: 66 53                 push   %ebx
107  1f3: 66 83 ec 34           sub    $0x34,%esp
 
122  23d: 67 66 8d 45 e4        addr32 lea -0x1c(%ebp),%eax  # local obj: io
123  242: 67 66 89 04 24        addr32 mov %eax,(%esp)
124  247: 66 e8 ed 00 00 00     calll  33a <_ZN2IoC1Ev> # call Io::ctor
125  24d: 67 66 8d 45 dc        addr32 lea -0x24(%ebp),%eax  # local obj: d_io
126  252: 67 66 89 04 24        addr32 mov %eax,(%esp)
127  257: 66 e8 d5 06 00 00     calll  932 <_ZN8DeriveIoC1Ev> # call DerveIo::ctor
128  25d: 67 66 8d 45 e4        addr32 lea -0x1c(%ebp),%eax  # local obj: io
129  262: 67 66 89 45 f0        addr32 mov %eax,-0x10(%ebp)  # local obj: io
130  267: 67 66 8d 45 dc        addr32 lea -0x24(%ebp),%eax  # local obj: d_io
131  26c: 67 66 89 45 f4        addr32 mov %eax,-0xc(%ebp)   # local obj: d_io
132  271: 67 66 8b 45 f0        addr32 mov -0x10(%ebp),%eax
133  276: 67 66 8b 00           addr32 mov (%eax),%eax
134  27a: 67 66 8b 10           addr32 mov (%eax),%edx
135  27e: 67 66 8b 45 f0        addr32 mov -0x10(%ebp),%eax
136  283: 67 66 89 04 24        addr32 mov %eax,(%esp)
137  288: 66 ff d2              calll  *%edx
138  28b: 67 66 8b 45 f4        addr32 mov -0xc(%ebp),%eax # d_io
139  290: 67 66 8b 00           addr32 mov (%eax),%eax
140  294: 67 66 8b 10           addr32 mov (%eax),%edx
141  298: 67 66 8b 45 f4        addr32 mov -0xc(%ebp),%eax
142  29d: 67 66 89 04 24        addr32 mov %eax,(%esp)
143  2a2: 66 ff d2              calll  *%edx
144  2a5: 66 bb 00 00 00 00     mov    $0x0,%ebx
145  2ab: 67 66 8d 45 dc        addr32 lea -0x24(%ebp),%eax
146  2b0: 67 66 89 04 24        addr32 mov %eax,(%esp)
147  2b5: 66 e8 a1 06 00 00     calll  95c <_ZN8DeriveIoD1Ev>
148  2bb: 67 66 8d 45 e4        addr32 lea -0x1c(%ebp),%eax
149  2c0: 67 66 89 04 24        addr32 mov %eax,(%esp)
150  2c5: 66 e8 05 01 00 00     calll  3d0 <_ZN2IoD1Ev>
151  2cb: 66 89 d8              mov    %ebx,%eax
152  2ce: 66 83 c4 34           add    $0x34,%esp
153  2d2: 66 5b                 pop    %ebx
154  2d4: 66 5d                 pop    %ebp
155  2d6: 66 c3                 retl   
156 
157 000002d8 <_ZN2IoC2Ev>:
158  2d8: 66 55                 push   %ebp
159  2da: 66 89 e5              mov    %esp,%ebp
160  2dd: 66 83 ec 18           sub    $0x18,%esp
161  2e1: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
162  2e6: 67 66 c7 00 50 0a 00  addr32 movl $0xa50,(%eax)
163  2ed: 00 
164  2ee: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
165  2f3: 67 66 c7 40 04 10 0a  addr32 movl $0xa10,0x4(%eax)
166  2fa: 00 00 
167  2fc: 67 66 c7 44 24 04 1e  addr32 movl $0xa1e,0x4(%esp)
168  303: 0a 00 00 
169  306: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
170  30b: 67 66 89 04 24        addr32 mov %eax,(%esp)
171  310: 66 e8 ee 00 00 00     calll  404 <_ZN2Io5printEPKc>
172  316: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
173  31b: 67 66 8b 40 04        addr32 mov 0x4(%eax),%eax
174  320: 67 66 89 44 24 04     addr32 mov %eax,0x4(%esp)
175  326: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
176  32b: 67 66 89 04 24        addr32 mov %eax,(%esp)
177  330: 66 e8 ce 00 00 00     calll  404 <_ZN2Io5printEPKc>
178  336: 66 c9                 leavel 
179  338: 66 c3                 retl   
180 
181 0000033a <_ZN2IoC1Ev>:
182  33a: 66 55                 push   %ebp
183  33c: 66 89 e5              mov    %esp,%ebp
184  33f: 66 83 ec 18           sub    $0x18,%esp
185  343: 67 66 8b 45 08        mov 0x8(%ebp),%eax
186  348: 67 66 c7 00 50 0a 00  movl $0xa50,(%eax) # virtual function table address
187  34f: 00 
188  350: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
189  355: 67 66 c7 40 04 10 0a  addr32 movl $0xa10,0x4(%eax)
190  35c: 00 00 
191  35e: 67 66 c7 44 24 04 1e  addr32 movl $0xa1e,0x4(%esp)
192  365: 0a 00 00 
193  368: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
194  36d: 67 66 89 04 24        addr32 mov %eax,(%esp)
195  372: 66 e8 8c 00 00 00     calll  404 <_ZN2Io5printEPKc>
196  378: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
197  37d: 67 66 8b 40 04        addr32 mov 0x4(%eax),%eax
198  382: 67 66 89 44 24 04     addr32 mov %eax,0x4(%esp)
199  388: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
200  38d: 67 66 89 04 24        addr32 mov %eax,(%esp)
201  392: 66 e8 6c 00 00 00     calll  404 <_ZN2Io5printEPKc>
202  398: 66 c9                 leavel 
203  39a: 66 c3                 retl   
204 
205 0000039c <_ZN2IoD2Ev>:
206  39c: 66 55                 push   %ebp
207  39e: 66 89 e5              mov    %esp,%ebp
208  3a1: 66 83 ec 18           sub    $0x18,%esp
209  3a5: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
210  3aa: 67 66 c7 00 50 0a 00  addr32 movl $0xa50,(%eax)
211  3b1: 00 
212  3b2: 67 66 c7 44 24 04 28  addr32 movl $0xa28,0x4(%esp)
213  3b9: 0a 00 00 
214  3bc: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
215  3c1: 67 66 89 04 24        addr32 mov %eax,(%esp)
216  3c6: 66 e8 38 00 00 00     calll  404 <_ZN2Io5printEPKc>
217  3cc: 66 c9                 leavel 
218  3ce: 66 c3                 retl   
219 
220 000003d0 <_ZN2IoD1Ev>:
221  3d0: 66 55                 push   %ebp
222  3d2: 66 89 e5              mov    %esp,%ebp
223  3d5: 66 83 ec 18           sub    $0x18,%esp
224  3d9: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
225  3de: 67 66 c7 00 50 0a 00  addr32 movl $0xa50,(%eax)
226  3e5: 00 
227  3e6: 67 66 c7 44 24 04 28  addr32 movl $0xa28,0x4(%esp)
228  3ed: 0a 00 00 
229  3f0: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
230  3f5: 67 66 89 04 24        addr32 mov %eax,(%esp)
231  3fa: 66 e8 04 00 00 00     calll  404 <_ZN2Io5printEPKc>
232  400: 66 c9                 leavel 
233  402: 66 c3                 retl   
234 
235 00000404 <_ZN2Io5printEPKc>:
236  404: 66 55                 push   %ebp
237  406: 66 89 e5              mov    %esp,%ebp
238  409: 66 83 ec 18           sub    $0x18,%esp
239  40d: 67 66 8b 45 0c        addr32 mov 0xc(%ebp),%eax
240  412: 67 66 89 04 24        addr32 mov %eax,(%esp)
241  417: 66 e8 b8 02 00 00     calll  6d5 <_Z9print_strPKc>
242  41d: 66 c9                 leavel 
243  41f: 66 c3                 retl   
244  421: 90                    nop
245 
246 00000422 <_ZN2AbC2Ei>:
247  422: 66 55                 push   %ebp
248  424: 66 89 e5              mov    %esp,%ebp
249  427: 66 83 ec 18           sub    $0x18,%esp
250  42b: 67 66 c7 44 24 04 32  addr32 movl $0xa32,0x4(%esp)
251  432: 0a 00 00 
252  435: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
253  43a: 67 66 89 04 24        addr32 mov %eax,(%esp)
254  43f: 66 e8 99 00 00 00     calll  4de <_ZN2Ab5printEPKc>
255  445: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
256  44a: 67 66 8b 55 0c        addr32 mov 0xc(%ebp),%edx
257  44f: 67 66 89 50 04        addr32 mov %edx,0x4(%eax)
258  454: 66 c9                 leavel 
259  456: 66 c3                 retl   
260 
261 00000458 <_ZN2AbC1Ei>:
262  458: 66 55                 push   %ebp
263  45a: 66 89 e5              mov    %esp,%ebp
264  45d: 66 83 ec 18           sub    $0x18,%esp
265  461: 67 66 c7 44 24 04 32  addr32 movl $0xa32,0x4(%esp)
266  468: 0a 00 00 
267  46b: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
268  470: 67 66 89 04 24        addr32 mov %eax,(%esp)
269  475: 66 e8 63 00 00 00     calll  4de <_ZN2Ab5printEPKc>
270  47b: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
271  480: 67 66 8b 55 0c        addr32 mov 0xc(%ebp),%edx
272  485: 67 66 89 50 04        addr32 mov %edx,0x4(%eax)
273  48a: 66 c9                 leavel 
274  48c: 66 c3                 retl   
275 
276 0000048e <_ZN2AbD2Ev>:
277  48e: 66 55                 push   %ebp
278  490: 66 89 e5              mov    %esp,%ebp
279  493: 66 83 ec 18           sub    $0x18,%esp
280  497: 67 66 c7 44 24 04 3d  addr32 movl $0xa3d,0x4(%esp)
281  49e: 0a 00 00 
282  4a1: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
283  4a6: 67 66 89 04 24        addr32 mov %eax,(%esp)
284  4ab: 66 e8 2d 00 00 00     calll  4de <_ZN2Ab5printEPKc>
285  4b1: 66 c9                 leavel 
286  4b3: 66 c3                 retl   
287  4b5: 90                    nop
288 
289 000004b6 <_ZN2AbD1Ev>:
290  4b6: 66 55                 push   %ebp
291  4b8: 66 89 e5              mov    %esp,%ebp
292  4bb: 66 83 ec 18           sub    $0x18,%esp
293  4bf: 67 66 c7 44 24 04 3d  addr32 movl $0xa3d,0x4(%esp)
294  4c6: 0a 00 00 
295  4c9: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
296  4ce: 67 66 89 04 24        addr32 mov %eax,(%esp)
297  4d3: 66 e8 05 00 00 00     calll  4de <_ZN2Ab5printEPKc>
298  4d9: 66 c9                 leavel 
299  4db: 66 c3                 retl   
300  4dd: 90                    nop
301 

420  ...
421 
422 00000690 <_Z5printPKc>:
423  690: 66 55                 push   %ebp
424  692: 66 89 e5              mov    %esp,%ebp
425  695: 66 83 ec 18           sub    $0x18,%esp
426  699: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
427  69e: 67 66 89 04 24        addr32 mov %eax,(%esp)
428  6a3: 66 e8 2c 00 00 00     calll  6d5 <_Z9print_strPKc>
429  6a9: 66 c9                 leavel 
430  6ab: 66 c3                 retl   
431 

466 

585 
586 Disassembly of section .text._ZN8DeriveIo4fun1Ev:
587 
588 0000090a <_ZN8DeriveIo4fun1Ev>:
589  90a: 66 55                 push   %ebp
590  90c: 66 89 e5              mov    %esp,%ebp
591  90f: 66 83 ec 18           sub    $0x18,%esp
592  913: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
593  918: 67 66 c7 44 24 04 b0  addr32 movl $0x9b0,0x4(%esp)
594  91f: 09 00 00 
595  922: 67 66 89 04 24        addr32 mov %eax,(%esp)
596  927: 66 e8 d7 fa ff ff     calll  404 <_ZN2Io5printEPKc>
597  92d: 66 c9                 leavel 
598  92f: 66 c3                 retl   
599 
600 Disassembly of section .text._ZN8DeriveIoC1Ev:
601 
602 00000932 <_ZN8DeriveIoC1Ev>:
603  932: 66 55                 push   %ebp
604  934: 66 89 e5              mov    %esp,%ebp
605  937: 66 83 ec 18           sub    $0x18,%esp
606  93b: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
607  940: 67 66 89 04 24        addr32 mov %eax,(%esp)
608  945: 66 e8 8d f9 ff ff     calll  2d8 <_ZN2IoC2Ev>
609  94b: 67 66 8b 45 08        mov 0x8(%ebp),%eax
610  950: 67 66 c7 00 00 0a 00  movl $0xa00,(%eax) # virtual function table address
611  957: 00 
612  958: 66 c9                 leavel 
613  95a: 66 c3                 retl   
614 
615 Disassembly of section .text._ZN8DeriveIoD1Ev:
616 
617 0000095c <_ZN8DeriveIoD1Ev>:
618  95c: 66 55                 push   %ebp
619  95e: 66 89 e5              mov    %esp,%ebp
620  961: 66 83 ec 18           sub    $0x18,%esp
621  965: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
622  96a: 67 66 c7 00 00 0a 00  addr32 movl $0xa00,(%eax)
623  971: 00 
624  972: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
625  977: 67 66 89 04 24        addr32 mov %eax,(%esp)
626  97c: 66 e8 1a fa ff ff     calll  39c <_ZN2IoD2Ev>
627  982: 66 c9                 leavel 
628  984: 66 c3                 retl   
629 
630 Disassembly of section .text._ZN2Io4fun1Ev:
631 
632 00000986 <_ZN2Io4fun1Ev>:
633  986: 66 55                 push   %ebp
634  988: 66 89 e5              mov    %esp,%ebp
635  98b: 66 83 ec 18           sub    $0x18,%esp
636  98f: 67 66 c7 44 24 04 04  addr32 movl $0xa04,0x4(%esp)
637  996: 0a 00 00 
638  999: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
639  99e: 67 66 89 04 24        addr32 mov %eax,(%esp)
640  9a3: 66 e8 5b fa ff ff     calll  404 <_ZN2Io5printEPKc>
641  9a9: 66 c9                 leavel 
642  9ab: 66 c3                 retl   

cppb.bin
  1 00000000  e9 00 00 87 db 8c c8 8e  d8 8e d0 b8 00 b8 8e e8  |................|
  2 00000010  b0 ef 66 e8 7e 00 00 00  b0 12 66 e8 76 00 00 00  |..f.~.....f.v...|
  3 00000020  8b 1e a4 0a 66 e8 ae 00  00 00 b0 aa 66 e8 64 00  |....f.......f.d.|
  4 00000030  00 00 e8 40 00 8b 1e a4  0a 66 e8 99 00 00 00 66  |...@.....f.....f|
  5 00000040  e8 11 00 00 00 66 e8 a1  00 00 00 66 e8 6f 04 00  |.....f.....f.o..|

144 000008f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
145 00000900  0a 09 00 00 49 6f 3a 3a  66 75 6e 63 31 0d 0a 00  |....Io::func1...|
146 00000910  64 61 74 61 20 6d 65 6d  62 65 72 0d 0a 00 49 6f  |data member...Io|
147 00000920  20 63 74 6f 72 3a 20 00  69 6f 20 64 74 6f 72 0d  | ctor: .io dtor.|
148 00000930  0a 00 41 62 3a 3a 63 74  6f 72 0d 0a 00 61 62 20  |..Ab::ctor...ab |
149 00000940  64 74 6f 72 0d 0a 00 00  00 00 00 00 00 00 00 00  |dtor............|
150 00000950  86 09 00 00 61 74 65 78  69 74 0d 0a 00 0d 0a 00  |....atexit......|
151 00000960  67 5f 64 74 6f 72 73 0d  0a 00 00 00 0d 0a 00 25  |g_dtors........%|
152 00000970  43 53 3a 20 00 25 44 53  3a 20 00 25 53 53 3a 20  |CS: .%DS: .%SS: |
153 00000980  00 76 61 72 3a 20 00 00  00 00 00 00              |.var: ......|
154 0000098c

virtual function table 在 compile time 就決定好了, 在 cppb.bin L145, L150。

186  348: 67 66 c7 00 50 0a 00  movl $0xa50,(%eax) # virtual function table address
610  950: 67 66 c7 00 00 0a 00  movl $0xa00,(%eax) # virtual function table address

在 Io::Io(), DeriveIo::DeriveIo()
會設定這個 vtable 的位址, constructor 似乎比我們從 c++ code 看到的還要多一些東西, 你的 c++ code 又不知不覺加大了。

0xa50, 0xa00 分別是 io, d_io 的 vtable 位址, 查看 cppb.bin offset 0x900, 0x950 可以看到, 有點亂, 還是一個一個來好了 ...

local object io: 0xa50 (vtable address) 對應到 cppb.bin offset 0x950, 為什麼差了 0x100, 因為這是一個在 dos 下執行的 com 執行檔, 會被載入到  %cs:0x100 的地方, 所以 0x950 被載入到 ram 時, 就會在 0xa50。為什麼 0x950 我敢說它是 vtable 的位址呢?仔細看前 4 byte 是 86090000 -> 00000986 (little endian),

632 00000986 <_ZN2Io4fun1Ev>:

搭配 objdump 可以看到這是 io::fun1() 的位址, 看和陈皓的文章有點不同的是, 我看不到結束符號 (所以我有可能搞錯了, 如果我搞錯, 請告知我)。

同樣方式:
local object d_io vtable address: 0xa00, 查看 cppb.bin 0x900 的前 4 個 byte:
0a090000 -> 0000090a ()

588 0000090a <_ZN8DeriveIo4fun1Ev>:
 
constructor 有一部份的程式碼就是在初始化 vtable。

186  348: 67 66 c7 00 50 0a 00  movl $0xa50,(%eax) # virtual function table address
610  950: 67 66 c7 00 00 0a 00  movl $0xa00,(%eax) # virtual function table address

把 vtable address 複製到 d_io 的位址上, 後續才會是其他 member data 的部份, 呼應了陈皓的文章。

再來就是看看如何使用 base class pointer 指向 derive class 來喚起 derive class virtual function。

138  28b: 67 66 8b 45 f4        addr32 mov -0xc(%ebp),%eax # d_io
139  290: 67 66 8b 00           addr32 mov (%eax),%eax
140  294: 67 66 8b 10           addr32 mov (%eax),%edx
141  298: 67 66 8b 45 f4        addr32 mov -0xc(%ebp),%eax
142  29d: 67 66 89 04 24        addr32 mov %eax,(%esp)
143  2a2: 66 ff d2              calll  *%edx

d_io 的位址的開始 4 byte 就是 vtable address, L141 則是 this pointer, 這段 code 就是將 vtable address 的第一個 function 取出來執行。請細細品嘗, 組合語言得要你自己看懂才行, 我該說明的已經都說明完了。

所以也知道了 virtual function 為什麼會慢了, 在程式上不是對應一個 call 指令, 而是需要存取 vtable, 再找出該 virtual function 位址, 然後才是 call 指令。

objdump 的反組譯程式, 我加上部份註解, 相信可以幫助你理解, 很難懂嗎?我當時可花了好幾天才看懂, 又花了好幾天才有這篇文章, 若要能馬上看懂可能有點困難, 希望可以降低你的理解時間。

知道這能幹嘛, 說實在, 沒什麼用處, 只要知道 c++ 書上寫的東西, 就可以使用 virtual function, 所以看不懂也沒關係, 我自己只是純粹「想知道」而已。

ref:
http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。