1

我想确保在生成 exe/库之前了解我的代码实际上被编译成什么。我有以下用 C++98 编写的程序。这源于这个网站http://www.phpcompiler.org/articles/virtualinheritance.html

#include <stdio.h>

class top
{
public:
  int t;
};

class left : virtual public top
{
public:
  int l;
};

class right : virtual public top
{
public:
  int r;
};

class bottom : public left, public right
{
public:
  int b;
};

int main() 
{
  bottom *b = new bottom();
  b->l = 5;
  left *l = b;
  printf("%d\n", l->l);  
}

用 编译的程序集输出g++ -S main.cpp在下面附有关于我认为应该如何分解的评论(这是我需要接受一些教育的地方)以及一些明确标记的问题。回答下面代码中的问题是我正在寻找的。

    .file   "main.cpp"
    .section    .text._ZN3topC2Ev,"axG",@progbits,_ZN3topC5Ev,comdat
    .align 2
    .weak   _ZN3topC2Ev
    .type   _ZN3topC2Ev, @function
_ZN3topC2Ev:
.LFB3:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    pushq   %rbp                                          ; LFB3 Associated with the address for the class top constructor 
    .cfi_def_cfa_offset 16                                ;
    .cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for t). 
    movq    %rsp, %rbp                                    ; onto the stack
    .cfi_def_cfa_register 6
    movq    %rdi, -8(%rbp)
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE3:
    .size   _ZN3topC2Ev, .-_ZN3topC2Ev
    .weak   _ZN3topC1Ev
    .set    _ZN3topC1Ev,_ZN3topC2Ev
    .section    .text._ZN4leftC2Ev,"axG",@progbits,_ZN4leftC2Ev,comdat
    .align 2
    .weak   _ZN4leftC2Ev
    .type   _ZN4leftC2Ev, @function
_ZN4leftC2Ev:
.LFB6:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                      
    pushq   %rbp                                          ; LFB6 Associated with the adress for the class left constructor 
    .cfi_def_cfa_offset 16                                ;
    .cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for l). 
    movq    %rsp, %rbp                                    ; onto the stack
    .cfi_def_cfa_register 6                               ;
    movq    %rdi, -8(%rbp)                                ; %rdi, -16(%rbp) pushes 8 more bytes (64 bits for t). 
    movq    %rsi, -16(%rbp)                               ;
    movq    -16(%rbp), %rax                               ; What does the rest of this do?
    movq    (%rax), %rdx
    movq    -8(%rbp), %rax
    movq    %rdx, (%rax)
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE6:
    .size   _ZN4leftC2Ev, .-_ZN4leftC2Ev
    .section    .text._ZN5rightC2Ev,"axG",@progbits,_ZN5rightC2Ev,comdat
    .align 2
    .weak   _ZN5rightC2Ev
    .type   _ZN5rightC2Ev, @function
_ZN5rightC2Ev:
.LFB9:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    pushq   %rbp                                          ; LFB9 Associated with the adress for the class left constructor 
    .cfi_def_cfa_offset 16                                ;
    .cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for r).
    movq    %rsp, %rbp                                    ; onto the stack
    .cfi_def_cfa_register 6                               ;
    movq    %rdi, -8(%rbp)                                ; %rdi, -16(%rbp) pushes 8 more bytes (64 bits for t). 
    movq    %rsi, -16(%rbp)                               ;
    movq    -16(%rbp), %rax                               ; What does the rest of this do?
    movq    (%rax), %rdx
    movq    -8(%rbp), %rax
    movq    %rdx, (%rax)
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE9:
    .size   _ZN5rightC2Ev, .-_ZN5rightC2Ev
    .section    .text._ZN6bottomC1Ev,"axG",@progbits,_ZN6bottomC1Ev,comdat
    .align 2
    .weak   _ZN6bottomC1Ev
    .type   _ZN6bottomC1Ev, @function
_ZN6bottomC1Ev:
.LFB12:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    pushq   %rbp                                          ; LFB12 Associated with the adress for the class left constructor
    .cfi_def_cfa_offset 16                                ;
    .cfi_offset 6, -16                                    ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for b).
    movq    %rsp, %rbp                                    ; onto the stack
    .cfi_def_cfa_register 6                               ;
    subq    $16, %rsp                                     ; Construct all the base objects placing t into b only once?
    movq    %rdi, -8(%rbp)                                ; 
    movq    -8(%rbp), %rax
    addq    $32, %rax
    movq    %rax, %rdi
    call    _ZN3topC2Ev
    movl    $_ZTT6bottom+8, %edx
    movq    -8(%rbp), %rax
    movq    %rdx, %rsi
    movq    %rax, %rdi
    call    _ZN4leftC2Ev
    movl    $_ZTT6bottom+16, %eax
    movq    -8(%rbp), %rdx
    addq    $16, %rdx
    movq    %rax, %rsi
    movq    %rdx, %rdi
    call    _ZN5rightC2Ev
    movl    $_ZTV6bottom+24, %edx
    movq    -8(%rbp), %rax
    movq    %rdx, (%rax)
    movl    $_ZTV6bottom+48, %edx
    movq    -8(%rbp), %rax
    movq    %rdx, 16(%rax)
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE12:
    .size   _ZN6bottomC1Ev, .-_ZN6bottomC1Ev
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    pushq   %rbp                                          ; Store off the base pointer
    .cfi_def_cfa_offset 16                                ; Debug code to trace where stack pointer is?
    .cfi_offset 6, -16                                    ;
    movq    %rsp, %rbp                                    ; Move where stack pointer is to base pointer
    .cfi_def_cfa_register 6                               ; 
    pushq   %rbx                                          ; Store off what might have been in rbx
    subq    $24, %rsp                                     ; Push argc onto stack
    .cfi_offset 3, -24                                    ;
    movl    $40, %edi                                     ; Push argv onto stack
    call    _Znwm                                         ; Call new
    movq    %rax, %rbx                                    ; Create room for b %rax contains address of memory
    movq    $0, (%rbx)                                    ; location where new returned?
    movq    $0, 8(%rbx)                                   ; 
    movq    $0, 16(%rbx)                                  ; 
    movq    $0, 24(%rbx)                                  ; 
    movq    $0, 32(%rbx)                                  ; 
    movq    %rbx, %rdi                                    ; Move that data into dynamic memory?
    call    _ZN6bottomC1Ev                                ; Call the construtor of the object
    movq    %rbx, -32(%rbp)                               ; 
    movq    -32(%rbp), %rax                               ; Can someone explain how this code relates to
    movl    $5, 8(%rax)                                   ; the explaination at:
    movq    -32(%rbp), %rax                               ; http://www.phpcompiler.org/articles/virtualinheritance.html?
    movq    %rax, -24(%rbp)
    movq    -24(%rbp), %rax
    movl    8(%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    addq    $24, %rsp
    popq    %rbx
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .weak   _ZTV6bottom
    .section    .rodata._ZTV6bottom,"aG",@progbits,_ZTV6bottom,comdat
    .align 32
    .type   _ZTV6bottom, @object
    .size   _ZTV6bottom, 48
_ZTV6bottom:
    .quad   32
    .quad   0
    .quad   _ZTI6bottom
    .quad   16
    .quad   -16
    .quad   _ZTI6bottom
    .weak   _ZTT6bottom
    .section    .rodata._ZTT6bottom,"aG",@progbits,_ZTV6bottom,comdat
    .align 32
    .type   _ZTT6bottom, @object
    .size   _ZTT6bottom, 32
_ZTT6bottom:
    .quad   _ZTV6bottom+24
    .quad   _ZTC6bottom0_4left+24
    .quad   _ZTC6bottom16_5right+24
    .quad   _ZTV6bottom+48
    .weak   _ZTC6bottom0_4left
    .section    .rodata._ZTC6bottom0_4left,"aG",@progbits,_ZTV6bottom,comdat
    .align 16
    .type   _ZTC6bottom0_4left, @object
    .size   _ZTC6bottom0_4left, 24
_ZTC6bottom0_4left:
    .quad   32
    .quad   0
    .quad   _ZTI4left
    .weak   _ZTC6bottom16_5right
    .section    .rodata._ZTC6bottom16_5right,"aG",@progbits,_ZTV6bottom,comdat
    .align 16
    .type   _ZTC6bottom16_5right, @object
    .size   _ZTC6bottom16_5right, 24
_ZTC6bottom16_5right:
    .quad   16
    .quad   0
    .quad   _ZTI5right
    .weak   _ZTS6bottom
    .section    .rodata._ZTS6bottom,"aG",@progbits,_ZTS6bottom,comdat
    .type   _ZTS6bottom, @object
    .size   _ZTS6bottom, 8
_ZTS6bottom:
    .string "6bottom"
    .weak   _ZTI6bottom
    .section    .rodata._ZTI6bottom,"aG",@progbits,_ZTI6bottom,comdat
    .align 32
    .type   _ZTI6bottom, @object
    .size   _ZTI6bottom, 56
_ZTI6bottom:
    .quad   _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
    .quad   _ZTS6bottom
    .long   2
    .long   2
    .quad   _ZTI4left
    .quad   2
    .quad   _ZTI5right
    .quad   4098
    .weak   _ZTS5right
    .section    .rodata._ZTS5right,"aG",@progbits,_ZTS5right,comdat
    .type   _ZTS5right, @object
    .size   _ZTS5right, 7
_ZTS5right:
    .string "5right"
    .weak   _ZTI5right
    .section    .rodata._ZTI5right,"aG",@progbits,_ZTI5right,comdat
    .align 32
    .type   _ZTI5right, @object
    .size   _ZTI5right, 40
_ZTI5right:
    .quad   _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
    .quad   _ZTS5right
    .long   0
    .long   1
    .quad   _ZTI3top
    .quad   -6141
    .weak   _ZTS4left
    .section    .rodata._ZTS4left,"aG",@progbits,_ZTS4left,comdat
    .type   _ZTS4left, @object
    .size   _ZTS4left, 6
_ZTS4left:
    .string "4left"
    .weak   _ZTI4left
    .section    .rodata._ZTI4left,"aG",@progbits,_ZTI4left,comdat
    .align 32
    .type   _ZTI4left, @object
    .size   _ZTI4left, 40
_ZTI4left:
    .quad   _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
    .quad   _ZTS4left
    .long   0
    .long   1
    .quad   _ZTI3top
    .quad   -6141
    .weak   _ZTS3top
    .section    .rodata._ZTS3top,"aG",@progbits,_ZTS3top,comdat
    .type   _ZTS3top, @object
    .size   _ZTS3top, 5
_ZTS3top:
    .string "3top"
    .weak   _ZTI3top
    .section    .rodata._ZTI3top,"aG",@progbits,_ZTI3top,comdat
    .align 16
    .type   _ZTI3top, @object
    .size   _ZTI3top, 16
_ZTI3top:
    .quad   _ZTVN10__cxxabiv117__class_type_infoE+16
    .quad   _ZTS3top
    .ident  "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3"
    .section    .note.GNU-stack,"",@progbits
4

1 回答 1

1
_ZN4leftC2Ev:
.LFB6:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                      
    pushq   %rbp                                          ; LFB6 Associated with the adress for the class left constructor 
    .cfi_def_cfa_offset 16                                ;
    .cfi_offset 6, -16                                    ; %rdi, -8(%rbp) - his doesn't initialize anything - it is just pushing this value on local stack, in optimized version it will probably dissapear. 
    movq    %rsp, %rbp                                    ; %rsi, -16(%rbp) - just as above;
    .cfi_def_cfa_register 6                               ; %rsi - pointer to virtual table table (not a mistake it's vtt, not vt) for left-in-bottom
    movq    %rdi, -8(%rbp)                                ;  %rdi - pointer to left instance
    movq    %rsi, -16(%rbp)                               ;
    movq    -16(%rbp), %rax                               ; What does the rest of this do?
    movq    (%rax), %rdx                                  ;  It is just copying vtable address form vtt to first eight bytes of actual object.
    movq    -8(%rbp), %rax
    movq    %rdx, (%rax)
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

这是一个未优化的代码,所以编译器做了很多不必要的事情,比如将值从寄存器放入内存,然后再放回寄存器,但如果你遵循这些值,你会发现实际上并没有发生太多事情。代码获取指向对象存储的指针和指向 vtt 条目的指针,它只是推迟 vtt 条目并将找到的 vtable 指针指向对象存储的前 8 个字节。使用的 vtt 实际上只是一个临时的,用于子对象的构造,因为最后一个是由bottom构造函数添加的:

    movl    $_ZTV6bottom+24, %edx
    movq    -8(%rbp), %rax
    movq    %rdx, (%rax)
    movl    $_ZTV6bottom+48, %edx
    movq    -8(%rbp), %rax
    movq    %rdx, 16(%rax)

至于main

main:
.LFB0:
    .cfi_startproc                                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    pushq   %rbp                                          ; Store off the base pointer
    .cfi_def_cfa_offset 16                                ; Debug code to trace where stack pointer is?
    .cfi_offset 6, -16                                    ;
    movq    %rsp, %rbp                                    ; Move where stack pointer is to base pointer
    .cfi_def_cfa_register 6                               ; 
    pushq   %rbx                                          ; Store off what might have been in rbx
    subq    $24, %rsp                                     ; Push argc onto stack
    .cfi_offset 3, -24                                    ;
    movl    $40, %edi                                     ; Push argv onto stack
    call    _Znwm                                         ; Call new
    movq    %rax, %rbx                                    ; Create room for b %rax contains address of memory
    movq    $0, (%rbx)                                    ; location where new returned?
    movq    $0, 8(%rbx)                                   ; A: Yes - eax contains address of returned buffer
    movq    $0, 16(%rbx)                                  ;    It is being zeroed to 5*8 = 48 bytes
    movq    $0, 24(%rbx)                                  ;
    movq    $0, 32(%rbx)                                  ; 
    movq    %rbx, %rdi                                    ; Move that data into dynamic memory? -hmmm, what? Just moving pointr to it into rdi, where bottom constructor expects it
                                                          ; it is still where it was - in dynamic memory from new (_Znwm), jut it's pointer changed register ;)
    call    _ZN6bottomC1Ev                                ; Call the construtor of the object
    movq    %rbx, -32(%rbp)                               ; 
    movq    -32(%rbp), %rax                               ; Here isn't happening much - classes bottom and left are sure to start at the same address, so compiler doesn't need to chack for anything,
    movl    $5, 8(%rax)                                   ; just use offset to addres fields, and copy the pointer without modification to do the castting from bottom to left.
    movq    -32(%rbp), %rax                               ; 
    movq    %rax, -24(%rbp)
    movq    -24(%rbp), %rax
    movl    8(%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    addq    $24, %rsp
    popq    %rbx
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret

至于链接的文章,我不确定你的意思是什么。你不确定什么?您的代码实际上对类中的字段没有任何作用,并且它们不会出现在汇编中 - 代码只是处理周围的 vmt 指针。你不明白的究竟是什么?

值得注意的一件事是,即使子类实例的虚拟偏移量在 vtable 中,但在对象的完整类型已知的任何地方,这些值都可以简单地硬编码;虚拟方法调用和任何与 vtable 相关的东西也是如此。尽管未优化的版本仍然使用硬编码值,但我感到很惊讶。

于 2013-05-26T05:13:17.757 回答