7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
1

Под капотом вызова функции

28.02.2019, 12:50. Показов 2804. Ответов 8

Author24 — интернет-сервис помощи студентам
Решил разобраться как вызываются функции в c++. То есть как компилятор преобразовывает код в машинный код.

С обычным вызовом функций разобрался, но вот с виртуальными нет.
Ниже представлен код на c++ и его аналог на ассемблере после компиляции gcc:
Код C++:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using namespace std;
// Type your code here, or load an example.
class A {
 
public:
    virtual void find() = 0;
};
 
class B : public A {
public:
    virtual void find() {
        int a = 10;
    }
};
 
class C : public B {
public:
    virtual void find() {
        int a = 5;
    }
};
 
 
 
 
int main() {
    C *c = new C;
    c->find();
    return 0;
}
Код на ассемблере (проблема начинается со строки 66):
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
B::find():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-24], rdi
        mov     DWORD PTR [rbp-4], 10
        nop
        pop     rbp
        ret
C::find():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-24], rdi
        mov     DWORD PTR [rbp-4], 5
        nop
        pop     rbp
        ret
A::A() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     edx, OFFSET FLAT:vtable for A+16
        mov     rax, QWORD PTR [rbp-8]
        mov     QWORD PTR [rax], rdx
        nop
        pop     rbp
        ret
B::B() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    A::A() [base object constructor]
        mov     edx, OFFSET FLAT:vtable for B+16
        mov     rax, QWORD PTR [rbp-8]
        mov     QWORD PTR [rax], rdx
        nop
        leave
        ret
C::C() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    B::B() [base object constructor]
        mov     edx, OFFSET FLAT:vtable for C+16
        mov     rax, QWORD PTR [rbp-8]
        mov     QWORD PTR [rax], rdx
        nop
        leave
        ret
main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        mov     edi, 8
        call    operator new(unsigned long)
        mov     rbx, rax
        mov     rdi, rbx
        call    C::C() [complete object constructor]
        mov     QWORD PTR [rbp-24], rbx ;Что здесь происходит
        mov     rax, QWORD PTR [rbp-24]
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax]
        mov     rdx, QWORD PTR [rbp-24]
        mov     rdi, rdx
        call    rax
        mov     eax, 0
        add     rsp, 24
        pop     rbx
        pop     rbp
        ret
vtable for C:
        .quad   0
        .quad   typeinfo for C
        .quad   C::find()
vtable for B:
        .quad   0
        .quad   typeinfo for B
        .quad   B::find()
vtable for A:
        .quad   0
        .quad   typeinfo for A
        .quad   __cxa_pure_virtual
typeinfo for C:
        .quad   vtable for __cxxabiv1::__si_class_type_info+16
        .quad   typeinfo name for C
        .quad   typeinfo for B
typeinfo name for C:
        .string "1C"
typeinfo for B:
        .quad   vtable for __cxxabiv1::__si_class_type_info+16
        .quad   typeinfo name for B
        .quad   typeinfo for A
typeinfo name for B:
        .string "1B"
typeinfo for A:
        .quad   vtable for __cxxabiv1::__class_type_info+16
        .quad   typeinfo name for A
typeinfo name for A:
        .string "1A"
Мне не понятен участок кода вызова виртуального метода:
Assembler
1
2
3
4
5
6
7
        mov     QWORD PTR [rbp-24], rbx ;Что здесь происходит
        mov     rax, QWORD PTR [rbp-24]
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax]
        mov     rdx, QWORD PTR [rbp-24]
        mov     rdi, rdx
        call    rax
Объясните пожалуйста, что в этом участке кода происходит? Откуда такие операции?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
28.02.2019, 12:50
Ответы с готовыми решениями:

А что под капотом у vector?
Не знаю куда написать и вроде тем отвечающий на вопрос не нашел. Вопрос заключается в том, что...

Что под капотом std::mutex
Собственно сабж. Под виндой это сделано на основе критической секции или через мьютекс как объект...

оформить решение в виде функции следующими способами: 1. функция расположена после ее вызова; 2. функция расположена после до ее вызова; 3. функ
оформить решение в виде функции следующими способами: 1. функция расположена после ее вызова;...

Выделение памяти с помощью new под объекты без вызова их конструкторов
здравствуйте, корректен ли следующий код: myClass* pttr = static_cast<myClass*>(::operator new(5 *...

8
Мозгоправ
1744 / 1038 / 468
Регистрация: 01.10.2018
Сообщений: 2,138
Записей в блоге: 2
28.02.2019, 13:45 2
Лучший ответ Сообщение было отмечено DARKPALADIN как решение

Решение

Видимо это как раз поиск адреса виртуального метода.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24                  ; отвели место в стеке под локальные переменные
        mov     edi, 8
        call    operator new(unsigned long)
        mov     rbx, rax                 ; в rax вернулся адрес выделенной памяти из кучи
                                         ; запомнили его в rbx для использования в конструкторе
        mov     rdi, rbx
        call    C::C() [complete object constructor]
        mov     QWORD PTR [rbp-24], rbx  ; адрес объекта сохранили в переменной c
        mov     rax, QWORD PTR [rbp-24]  ; поместили адрес объекта в rax
        mov     rax, QWORD PTR [rax]     ; из объекта, по смещению 0, взяли адрес vtable и поместили в rax
        mov     rax, QWORD PTR [rax]     ; из vtable, по смещению 0, взяли адрес функции find и поместили в rax
        mov     rdx, QWORD PTR [rbp-24]  ; адрес объекта поместили в rdx
        mov     rdi, rdx
        call    rax                      ; вызвали функцию find объекта
Наверное так, если ничего не напутал.
1
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
28.02.2019, 14:13  [ТС] 3
L0M, Да вы правы. Такой код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class A {
 
public:
    virtual void foo() = 0;
    virtual void foo1() = 0;
    virtual void find() = 0;
};
 
class B : public A {
public:
    virtual void foo() {}
 
    virtual void foo1() {}
 
    virtual void find() {}
};
 
class C : public B {
public:
    virtual void foo() {}
 
    virtual void find() {}
};
 
int main() {
    C *c = new C;
    c->find();
    c->foo();
    return 0;
}
А вызов find() происходит со смещение 16

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
        mov     rax, QWORD PTR [rbp-24] ; Поиск find
        mov     rax, QWORD PTR [rax]
        add     rax, 16                 ; Смещение 16
        mov     rax, QWORD PTR [rax]
        mov     rdx, QWORD PTR [rbp-24]
        mov     rdi, rdx
        call    rax                      ; Вызов find
        mov     rax, QWORD PTR [rbp-24]  ; Поиск foo
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax]     ; Смещение 0
        mov     rdx, QWORD PTR [rbp-24]
        mov     rdi, rdx  
        call    rax                      ; Вызов foo
0
5231 / 3204 / 362
Регистрация: 12.12.2009
Сообщений: 8,113
Записей в блоге: 2
28.02.2019, 14:32 4
https://en.wikipedia.org/wiki/Virtual_method_table
Только учти, что это в Стандарте не прописано, хотя большинство компиляторов именно так и делает.
1
"C with Classes"
1645 / 1402 / 523
Регистрация: 16.08.2014
Сообщений: 5,877
Записей в блоге: 1
28.02.2019, 14:46 5
---
0
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
28.02.2019, 16:16  [ТС] 6
L0M, Почему вычитаем из адреса стека?
Цитата Сообщение от L0M Посмотреть сообщение
mov rax, QWORD PTR [rbp-24] ; поместили адрес объекта в rax
И здесь точно также
Цитата Сообщение от L0M Посмотреть сообщение
sub rsp, 24 ; отвели место в стеке под локальные переменные
0
6091 / 3449 / 1402
Регистрация: 07.02.2019
Сообщений: 8,768
28.02.2019, 16:44 7
Цитата Сообщение от DARKPALADIN Посмотреть сообщение
Почему вычитаем из адреса стека?
ну так стек задом наперед растет вроде
1
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
28.02.2019, 16:46  [ТС] 8
Цитата Сообщение от zayats80888 Посмотреть сообщение
ну так стек задом наперед растет вроде
То есть с 0xffff до 0x0000? (как пример)
0
6091 / 3449 / 1402
Регистрация: 07.02.2019
Сообщений: 8,768
28.02.2019, 16:47 9
ну да
0
28.02.2019, 16:47
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
28.02.2019, 16:47
Помогаю со студенческими работами здесь

Выделение памяти под массив объектов без вызова конструктора
Добрый вечер. Насколько я знаю, при выделении памяти под массив объектов, для каждого из них...

Вызова функции
Как сделать, что бы MAIN предназначался только для вызова функции(+нельзя использовать глобальные...

Абстракция вызова функции
Итак я хотел бы обсудить с участниками форума такую задачу. Но перед тем как перейти к сути -...

Многоточие после вызова функции
Приветствую зашедших - возможно кто-нибудь подскажет, что означает/делает многоточие в конце...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru