用于虚函数的汇编程序代码

时间:2014-06-13 07:38:11

标签: c++ assembly

file.cpp
#include <iostream>
class Base {
public:
  virtual  void f(int a ){}
};
 class Derived : public Base {
public:
    void f( int a ){ }
};

int main(int argc, char** argv) {
    Derived obj; 
    Base& ref = obj;
    ref.f(777);
    return 0;
}

使用以下代码生成代码:g ++ -S file.cpp is:

使用以下代码生成代码:g ++ -S file.cpp is:

    .file   "cp.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "Base::f()\n"
    .section    .text._ZN4Base1fEi,"axG",@progbits,_ZN4Base1fEi,comdat
    .align 2
    .p2align 4,,15
    .weak   _ZN4Base1fEi
    .type   _ZN4Base1fEi, @function
_ZN4Base1fEi:
.LFB1006:
    .cfi_startproc
    movl    $10, %edx
    movl    $.LC0, %esi
    movl    $_ZSt4cout, %edi
    jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
    .cfi_endproc
.LFE1006:
    .size   _ZN4Base1fEi, .-_ZN4Base1fEi
    .section    .rodata.str1.1
.LC1:
    .string "Derived::f()\n"
    .section    .text._ZN7Derived1fEi,"axG",@progbits,_ZN7Derived1fEi,comdat
    .align 2
    .p2align 4,,15
    .weak   _ZN7Derived1fEi
    .type   _ZN7Derived1fEi, @function
_ZN7Derived1fEi:
.LFB1007:
    .cfi_startproc
    movl    $13, %edx
    movl    $.LC1, %esi
    movl    $_ZSt4cout, %edi
    jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
    .cfi_endproc
.LFE1007:
    .size   _ZN7Derived1fEi, .-_ZN7Derived1fEi
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1008:
    .cfi_startproc
    subq    $24, %rsp
    .cfi_def_cfa_offset 32
    movl    $777, %esi
    movq    %rsp, %rdi
    movq    $_ZTV7Derived+16, (%rsp)
    call    _ZN7Derived1fEi
    xorl    %eax, %eax
    addq    $24, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1008:
    .size   main, .-main
    .p2align 4,,15
    .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1019:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1019:
    .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .ctors,"aw",@progbits
    .align 8
    .quad   _GLOBAL__sub_I_main
    .weak   _ZTV7Derived
    .section    .rodata._ZTV7Derived,"aG",@progbits,_ZTV7Derived,comdat
    .align 16
    .type   _ZTV7Derived, @object
    .size   _ZTV7Derived, 24
_ZTV7Derived:
    .quad   0
    .quad   _ZTI7Derived
    .quad   _ZN7Derived1fEi
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .weak   _ZTI7Derived
    .section    .rodata._ZTI7Derived,"aG",@progbits,_ZTI7Derived,comdat
    .align 16
    .type   _ZTI7Derived, @object
    .size   _ZTI7Derived, 24
_ZTI7Derived:
    .quad   _ZTVN10__cxxabiv120__si_class_type_infoE+16
    .quad   _ZTS7Derived
    .quad   _ZTI4Base
    .weak   _ZTS7Derived
    .section    .rodata._ZTS7Derived,"aG",@progbits,_ZTS7Derived,comdat
    .type   _ZTS7Derived, @object
    .size   _ZTS7Derived, 9
_ZTS7Derived:
    .string "7Derived"
    .weak   _ZTI4Base
    .section    .rodata._ZTI4Base,"aG",@progbits,_ZTI4Base,comdat
    .align 16
    .type   _ZTI4Base, @object
    .size   _ZTI4Base, 16
_ZTI4Base:
    .quad   _ZTVN10__cxxabiv117__class_type_infoE+16
    .quad   _ZTS4Base
    .weak   _ZTS4Base
    .section    .rodata._ZTS4Base,"aG",@progbits,_ZTS4Base,comdat
    .type   _ZTS4Base, @object
    .size   _ZTS4Base, 6
_ZTS4Base:
    .string "4Base"
    .weakref    _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
    .weakref    _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
    .weakref    _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
    .weakref    _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
    .weakref    _ZL20__gthrw_pthread_joinmPPv,pthread_join
    .weakref    _ZL21__gthrw_pthread_equalmm,pthread_equal
    .weakref    _ZL20__gthrw_pthread_selfv,pthread_self
    .weakref    _ZL22__gthrw_pthread_detachm,pthread_detach
    .weakref    _ZL22__gthrw_pthread_cancelm,pthread_cancel
    .weakref    _ZL19__gthrw_sched_yieldv,sched_yield
    .weakref    _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
    .weakref    _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
    .weakref    _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
    .weakref    _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
    .weakref    _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
    .weakref    _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
    .weakref    _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
    .weakref    _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
    .weakref    _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
    .weakref    _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
    .weakref    _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
    .weakref    _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
    .weakref    _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
    .weakref    _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
    .weakref    _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
    .weakref    _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits   

我有问题。 请告诉我在这里举行的asssembler代码中的晚期绑定。

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

没有实际的&#34;后期绑定&#34;在您编译的代码中。它最终纯粹是概念性的。编译器很聪明,可以在编译时弄清楚应该将此调用分派给Derived::f。它生成的代码直接调用Derived::f(内部名称_ZN7Derived1fEi

如果你想看到实际的后期绑定,你需要提出一个不太明显的代码。一个好主意是使对象的动态类型依赖于运行时值(如rand()调用)。

的内容
Base objb;
Derived objd; 
Base& ref = rand() % 2 == 0 ? objb : objd;
ref.f(777);

答案 1 :(得分:2)

因为编译器可以弄清楚发生了什么,所以这里没有后期绑定,而是直接在这里进行调用:

movq    $_ZTV7Derived+16, (%rsp)
call    _ZN7Derived1fEi

这是编译器已经能够做了一段时间的事情,并且最新版本甚至进行了大量的分析,以便在不太明显的情况下尝试确定绑定。

编辑(从工作回家)。如果我们稍微编辑代码,就像这样:

class Base {
public:
  virtual  void f(int a ){}
};

class Derived : public Base {
public:
    void f( int a ){ }
}; 

extern Derived& MakeObject();

int main(int argc, char** argv) {
    Base& ref = MakeObject();
    ref.f(777);
    return 0;
}

然后编译代码,我们得到这个(来自clang++ -O2 -S virt.cpp):

main:
    pushq   %rax
    callq   _Z10MakeObjectv
    movq    (%rax), %rcx
    movl    $777, %esi
    movq    %rax, %rdi
    callq   *(%rcx)
    xorl    %eax, %eax
    popq    %rdx
    retq

此处%rcx是虚函数的地址,%rax包含this值 - 然后将其放入%rdi这是第一个参数。 %esi是第二个参数。

或者,要了解更多内容,我们可以查看LLVM IR(中间表示):

define i32 @main(i32 %argc, i8** nocapture readnone %argv) #0 {
entry:
  %call = tail call %class.Derived* @_Z10MakeObjectv()
  %0 = getelementptr inbounds %class.Derived* %call, i64 0, i32 0
  %1 = bitcast %class.Derived* %call to void (%class.Base*, i32)***
  %vtable = load void (%class.Base*, i32)*** %1, align 8, !tbaa !1
  %2 = load void (%class.Base*, i32)** %vtable, align 8
  tail call void %2(%class.Base* %0, i32 777)
  ret i32 0
}

在这里,您可以看到%2在从%vtable加载后被调用,而getelementptr又是%1的结果(这是LLVM描述如何在结构中查找内容的方式或数组,在vtable内部)。它从%callbitcast(实际上是reinterpret_cast变成的那个)派生Derived& - 因为我们返回一个base&对象,我们需要更改它在编译器内部Base*(与getelementptr相同)。

我喜欢LLVM / Clang这样的事情,因为LLVM IR [一旦你理解它]比最终的汇编程序代码更加神秘,更“解释” - 例如main: subq $8, %rsp call _Z10MakeObjectv movq (%rax), %rdx movq %rax, %rdi movl $777, %esi call *(%rdx) xorl %eax, %eax addq $8, %rsp ret 通常只是变成了添加和/或乘法运算的某种组合[或者,在这种情况下,完全消失,因为我们在零偏移处获取],这就不那么明显了。

但是,g ++也提供相同的输出:

movq    $_ZTV7Derived+16, anobject(%rip)
ret

作为参考,这里是派生的vtable:

_ZTV7Derived:     .quad 0     .quad _ZTI7Derived     .quad _ZN7Derived1fEi

请注意,vtable的加载位置为:

ZN7Derived1fEi

所以*(%rcx)确实指向函数{{1}}。