2018年4月12日 星期四

c++ virtual function 的實作 by cfront

c++ virtual function 被很多人拿來研究, 由於沒有 cfront, 大部分的人都是從反組譯來觀察, 太苦了。

目前我已經把 cfront 建構出來, 可以使用 cfront 來看看 virtual function 轉出的 c code 長什麼樣, 破解 c++ virtual function。

hello_a.C
 1 #include <stream.h>
 2 
 3 class A
 4 {
 5   public:
 6     virtual void foo(int a = 0)
 7     {
 8       printf("A %d\n", a);
 9     }
10     virtual void va(int a)
11     {
12       printf("va: A %d\n", a);
13     }
14 };
15 
16 class B : public A
17 {
18   public:
19     virtual void foo(int a = 1)
20     {
21       printf("B a: %d\n", a);
22     }
23 };
24 
25 
26 main()
27 {
28   A *p = new A();
29   p->foo();
30   p->va(25);
31 }

hello.a.c 是 cfont 轉出來的 c code, 31 行的 c++ 程式碼透過 cfront 轉出 773 行 c code, 先來看看 class A, class B 被轉成什麼? hello.a.c L633, L643 就是對應的 class A, class B, 被轉成 strcut:
633 struct A { /* sizeof A == 8 */
636   struct __mptr *__vptr__1A ;
637 };
639 
643 struct B { /* sizeof B == 8 */
646   struct __mptr *__vptr__1A ;
647 };
裡頭有一個 __vptr__1A:
646 struct __mptr *__vptr__1A ;
struct __mptr 定義在 L17。
17 struct __mptr {short d; short i; __vptp f; };
注意其中的 __vptp f 即可, 這個就是用來儲存 virtual function, 其實 virtual function 就是一般的 c function, 所以也只是把 virtual function 的位址存起來。

再來是 L766 的 __ptbl_vec__hello_C_, 這邊建構了__vtbl__1A__hello_C
766 struct __mptr* __ptbl_vec__hello_C_[] = {
767 __vtbl__1A__hello_C,
768 
769 };
__vtbl__1A__hello_C 在 L699 定義:
699 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
700 0,0,(__vptp)foo__1AFi ,
701 0,0,(__vptp)va__1AFi ,
702 0,0,0};
__vtbl__1A__hello_C[0]: 0,0,0
__vtbl__1A__hello_C[1]: 0,0, foo__1AFi 就是 class A 的 virtual void foo(int a = 0)
__vtbl__1A__hello_C[2]: 0,0, va__1AFi 就是 class A virtual void va(int a)
A *p = new A(); 會用 new (__nw__FUl 就是 new) 建構 struct A, 把 p->__vptr__1A = __ptbl_vec__hello_C_[0]
對應到 hello.a.c L668
668 __1p = ( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct A *)__nw__FUl ( (unsigned long )(sizeof (struct A))) ))?(__0__X52 ->
669 #line 28 "hello.C"
670 __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X52 ) ) ;
p->foo(); 就是執行 p->__vptr__1A[1].f

對應到 hello.a.c L671
671 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
p->va(25); 就是執行 p->__vptr__1A[2].f
對應到 hello.a.c L672
672 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
這些很恐怖的轉型/設定程式碼就是在做這些事情 (花點時間應該可以看懂), 也就是為什麼 class B 可以呼叫 class A 的 virtaul function va 的祕密。

hello.a.c
  1 #line 1 "hello.C"
  2 
  3 /* <<AT&T C++ Language System <3.0.3> 05/05/94>> */
  4 char __cfront_version_303_xxxxxxxx;
  5 /* < hello.C > */
  6 
  7 #pragma lib "ape/libap.a"
  8 
  9 #pragma lib "c++/libC.a"
 10 
 11 #line 1 "hello.C"
 12 void *__vec_new (void *, int , int , void *);
 13 
 14 #line 1 "hello.C"
 15 void __vec_delete (void *, int , int , void *, int , int );
 16 typedef int (*__vptp)(void);
 17 struct __mptr {short d; short i; __vptp f; };
 18 
 19 #line 1 "hello.C"
 20 extern struct __mptr* __ptbl_vec__hello_C_[];
 21 
632 #line 4 "hello.C"
633 struct A { /* sizeof A == 8 */
634 
635 #line 14 "hello.C"
636 struct __mptr *__vptr__1A ;
637 };
638 struct B;
639 
640 #line 14 "hello.C"
641 
642 #line 17 "hello.C"
643 struct B { /* sizeof B == 8 */
644 
645 #line 14 "hello.C"
646 struct __mptr *__vptr__1A ;
647 };
648 
649 #line 14 "hello.C"
650 
651 #line 6 "hello.C"
652 static void foo__1AFi (struct A *__0this , int __2a );
653 
654 #line 10 "hello.C"
655 static void va__1AFi (struct A *__0this , int __2a );
656 
657 #line 26 "hello.C"
658 int main (void ){ _main(); 
659 #line 27 "hello.C"
660 { 
661 #line 28 "hello.C"
662 struct A *__1p ;
663 
664 #line 29 "hello.C"
665 struct A *__0__X52 ;
666 
667 #line 28 "hello.C"
668 __1p = ( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct A *)__nw__FUl ( (unsigned long )(sizeof (struct A))) ))?(__0__X52 ->
669 #line 28 "hello.C"
670 __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X52 ) ) ;
671 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
672 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
673 }
674 } 
675 #line 31 "hello.C"
676 void __sti__hello_C_main_ (void )
677 #line 664 "incl-master/incl-linux32/iostream.h"
678 { __ct__13Iostream_initFv ( & iostream_init ) ;
679 
680 #line 664 "incl-master/incl-linux32/iostream.h"
681 }
682 
683 #line 31 "hello.C"
684 void __std__hello_C_main_ (void )
685 #line 664 "incl-master/incl-linux32/iostream.h"
686 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
687 
688 #line 664 "incl-master/incl-linux32/iostream.h"
689 }
690 static void foo__1BFi (
691 #line 19 "hello.C"
692 struct B *__0this , 
693 #line 19 "hello.C"
694 int __2a );
695 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
696 0,0,(__vptp)foo__1BFi ,
697 0,0,(__vptp)va__1AFi ,
698 0,0,0};
699 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
700 0,0,(__vptp)foo__1AFi ,
701 0,0,(__vptp)va__1AFi ,
702 0,0,0};
703 static void foo__1BFi (struct B *__0this , 
704 #line 19 "hello.C"
705 int __2a )
706 #line 20 "hello.C"
707 { 
708 #line 21 "hello.C"
709 printf ( (const char *)"B a: %d\n",
710 #line 21 "hello.C"
711 __2a ) ;
712 }
713 
714 #line 10 "hello.C"
715 static void va__1AFi (struct A *__0this , 
716 #line 10 "hello.C"
717 int __2a )
718 #line 11 "hello.C"
719 { 
720 #line 12 "hello.C"
721 printf ( (const char *)"va: A %d\n",
722 #line 12 "hello.C"
723 __2a ) ;
724 }
725 
726 #line 6 "hello.C"
727 static void foo__1AFi (struct A *__0this , 
728 #line 6 "hello.C"
729 int __2a )
730 #line 7 "hello.C"
731 { 
732 #line 8 "hello.C"
733 printf ( (const char *)"A %d\n",
734 #line 8 "hello.C"
735 __2a ) ;
736 }
766 struct __mptr* __ptbl_vec__hello_C_[] = {
767 __vtbl__1A__hello_C,
768 
769 };
770 
771 #line 31 "hello.C"
772 
773 /* the end */

hello_b.C 和 hello_a.C 不同之處在於 new B(), 對於整個轉出來的程式當中, 只有一點點的不同:

hello_b.C
 1 #include <stream.h>
 2 
 3 class A
 4 {
 5   public:
 6     virtual void foo(int a = 0)
 7     {
 8       printf("A %d\n", a);
 9     }
10     virtual void va(int a)
11     {
12       printf("va: A %d\n", a);
13     }
14 };
15 
16 class B : public A
17 {
18   public:
19     virtual void foo(int a = 1)
20     {
21       printf("B a: %d\n", a);
22     }
23 };
24 
25 
26 main()
27 {
28   A *p = new B();
29   p->foo();
30   p->va(25);
31 }

hello.b.c 733

773 struct __mptr* __ptbl_vec__hello_C_[] = {
774 __vtbl__1A__hello_C,
775 __vtbl__1B__hello_C,
776 
777 };

多建構了一個 __vtbl__1B__hello_C
695 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
696 0,0,(__vptp)foo__1BFi ,
697 0,0,(__vptp)va__1AFi ,
698 0,0,0};

__vtbl__1B__hello_C[0]: 0,0,0
__vtbl__1B__hello_C[1]: 0,0, foo__1AFi 就是 class B 的 virtual void foo(int a = 1)
__vtbl__1B__hello_C[2]: 0,0, va__1AFi 就是 class A 的 virtual void va(int a)

A *p = new B(); 會用 new 建構 struct B, 把 p->__vptr__1A = __ptbl_vec__hello_C_[1]
對應到 hello.b.c L671

671 __1p = (struct A *)( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct B *)__nw__FUl ( (unsigned long )(sizeof (struct B)))
672 #line 28 "hello.C"
673 ))?( (__0__X52 = (struct B *)( (__0__X51 = (((struct A *)__0__X52 ))), ( ((__0__X51 || (__0__X51 = (struct A *)__nw__FUl ( (unsigned long
674 #line 28 "hello.C"
675 )(sizeof (struct A))) ))?(__0__X51 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X51 ) ) ), (__0__X52 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[1])) :0 ),
676 #line 28 "hello.C"
677 __0__X52 ) ) ;
p->foo(); 對應到 hello.b.c L678
678 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
p->va(25); 對應到 hello.b.c L679
679 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
這些很恐怖的轉型/設定程式碼就是在做這些事情, 而呼叫 virtual function 的程式碼是相同的, 只有在設定 __vptr__1A 有所不同而已。 看懂這些資料結構之後, virtaul function 就沒有那麼神秘了。 至於
17 struct __mptr {short d; short i; __vptp f; };
神秘的 d, i, 應該是用在繼承上的, 可能是其他的繼承方式, 有興趣的朋友可以繼續追蹤下去。
hello.b.c
632 #line 4 "hello.C"
633 struct A { /* sizeof A == 8 */
634 
635 #line 14 "hello.C"
636 struct __mptr *__vptr__1A ;
637 };
638 struct B;
639 
640 #line 14 "hello.C"
641 
642 #line 17 "hello.C"
643 struct B { /* sizeof B == 8 */
644 
645 #line 14 "hello.C"
646 struct __mptr *__vptr__1A ;
647 };
648 
649 #line 23 "hello.C"
650 
651 #line 6 "hello.C"
652 static void foo__1AFi (struct A *__0this , int __2a );
653 
654 #line 10 "hello.C"
655 static void va__1AFi (struct A *__0this , int __2a );
656 
657 #line 26 "hello.C"
658 int main (void ){ _main(); 
659 #line 27 "hello.C"
660 { 
661 #line 28 "hello.C"
662 struct A *__1p ;
663 
664 #line 29 "hello.C"
665 struct B *__0__X52 ;
666 
667 #line 29 "hello.C"
668 struct A *__0__X51 ;
669 
670 #line 28 "hello.C"
671 __1p = (struct A *)( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct B *)__nw__FUl ( (unsigned long )(sizeof (struct B)))
672 #line 28 "hello.C"
673 ))?( (__0__X52 = (struct B *)( (__0__X51 = (((struct A *)__0__X52 ))), ( ((__0__X51 || (__0__X51 = (struct A *)__nw__FUl ( (unsigned long
674 #line 28 "hello.C"
675 )(sizeof (struct A))) ))?(__0__X51 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X51 ) ) ), (__0__X52 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[1])) :0 ),
676 #line 28 "hello.C"
677 __0__X52 ) ) ;
678 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
679 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
680 }
681 } 
682 #line 31 "hello.C"
683 void __sti__hello_C_main_ (void )
684 #line 664 "incl-master/incl-linux32/iostream.h"
685 { __ct__13Iostream_initFv ( & iostream_init ) ;
686 
687 #line 664 "incl-master/incl-linux32/iostream.h"
688 }
689 
690 #line 31 "hello.C"
691 void __std__hello_C_main_ (void )
692 #line 664 "incl-master/incl-linux32/iostream.h"
693 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
694 
695 #line 664 "incl-master/incl-linux32/iostream.h"
696 }
697 static void foo__1BFi (
698 #line 19 "hello.C"
699 struct B *__0this , 
700 #line 19 "hello.C"
701 int __2a );
702 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
703 0,0,(__vptp)foo__1BFi ,
704 0,0,(__vptp)va__1AFi ,
705 0,0,0};
706 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
707 0,0,(__vptp)foo__1AFi ,
708 0,0,(__vptp)va__1AFi ,
709 0,0,0};
710 static void foo__1BFi (struct B *__0this , 
711 #line 19 "hello.C"
712 int __2a )
713 #line 20 "hello.C"
714 { 
715 #line 21 "hello.C"
716 printf ( (const char *)"B a: %d\n",
717 #line 21 "hello.C"
718 __2a ) ;
719 }
720 
721 #line 10 "hello.C"
722 static void va__1AFi (struct A *__0this , 
723 #line 10 "hello.C"
724 int __2a )
725 #line 11 "hello.C"
726 { 
727 #line 12 "hello.C"
728 printf ( (const char *)"va: A %d\n",
729 #line 12 "hello.C"
730 __2a ) ;
731 }
732 
733 #line 6 "hello.C"
734 static void foo__1AFi (struct A *__0this , 
735 #line 6 "hello.C"
736 int __2a )
737 #line 7 "hello.C"
738 { 
739 #line 8 "hello.C"
740 printf ( (const char *)"A %d\n",
741 #line 8 "hello.C"
742 __2a ) ;
743 }
744 
773 struct __mptr* __ptbl_vec__hello_C_[] = {
774 __vtbl__1A__hello_C,
775 __vtbl__1B__hello_C,
776 
777 };
778 
779 #line 31 "hello.C"
780 
781 /* the end */

沒有留言:

張貼留言

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

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