Я хочу убедиться, что я понимаю, что мой код на самом деле компилируется до того, как из него будет сделана 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
_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. Я удивлен тем, что эта неоптимизированная версия все еще использует жестко закодированные значения.
Других решений пока нет …