函数执行顺序
C++对象构造和析构
//遗留的问题:C++中构造函数和析构函数的执行顺序到底是怎么样的呢?
class Object
{
private:
int val;
public:
Object(int x)
{
val = x;
cout << "create :" << val << endl;
}
};
Object o1(1);
int main()
{
Object o2(2);
}
Object o3(3);
代码在Linux64平台编译运行,objdump -d 生成反汇编代码
。
汇编代码如下:
反汇编代码
Object: 文件格式 elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 callq *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 retq
Disassembly of section .plt:
0000000000001020 <.plt>:
1020: ff 35 62 2f 00 00 pushq 0x2f62(%rip) # 3f88 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 63 2f 00 00 bnd jmpq *0x2f63(%rip) # 3f90 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nopl (%rax)
1030: f3 0f 1e fa endbr64
1034: 68 00 00 00 00 pushq $0x0
1039: f2 e9 e1 ff ff ff bnd jmpq 1020 <.plt>
103f: 90 nop
1040: f3 0f 1e fa endbr64
1044: 68 01 00 00 00 pushq $0x1
1049: f2 e9 d1 ff ff ff bnd jmpq 1020 <.plt>
104f: 90 nop
1050: f3 0f 1e fa endbr64
1054: 68 02 00 00 00 pushq $0x2
1059: f2 e9 c1 ff ff ff bnd jmpq 1020 <.plt>
105f: 90 nop
1060: f3 0f 1e fa endbr64
1064: 68 03 00 00 00 pushq $0x3
1069: f2 e9 b1 ff ff ff bnd jmpq 1020 <.plt>
106f: 90 nop
1070: f3 0f 1e fa endbr64
1074: 68 04 00 00 00 pushq $0x4
1079: f2 e9 a1 ff ff ff bnd jmpq 1020 <.plt>
107f: 90 nop
1080: f3 0f 1e fa endbr64
1084: 68 05 00 00 00 pushq $0x5
1089: f2 e9 91 ff ff ff bnd jmpq 1020 <.plt>
108f: 90 nop
Disassembly of section .plt.got:
0000000000001090 <__cxa_finalize@plt>:
1090: f3 0f 1e fa endbr64
1094: f2 ff 25 2d 2f 00 00 bnd jmpq *0x2f2d(%rip) # 3fc8 <__cxa_finalize@GLIBC_2.2.5>
109b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Disassembly of section .plt.sec:
00000000000010a0 <__cxa_atexit@plt>:
10a0: f3 0f 1e fa endbr64
10a4: f2 ff 25 ed 2e 00 00 bnd jmpq *0x2eed(%rip) # 3f98 <__cxa_atexit@GLIBC_2.2.5>
10ab: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000010b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>:
10b0: f3 0f 1e fa endbr64
10b4: f2 ff 25 e5 2e 00 00 bnd jmpq *0x2ee5(%rip) # 3fa0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@GLIBCXX_3.4>
10bb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000010c0 <_ZNSolsEPFRSoS_E@plt>:
10c0: f3 0f 1e fa endbr64
10c4: f2 ff 25 dd 2e 00 00 bnd jmpq *0x2edd(%rip) # 3fa8 <_ZNSolsEPFRSoS_E@GLIBCXX_3.4>
10cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000010d0 <__stack_chk_fail@plt>:
10d0: f3 0f 1e fa endbr64
10d4: f2 ff 25 d5 2e 00 00 bnd jmpq *0x2ed5(%rip) # 3fb0 <__stack_chk_fail@GLIBC_2.4>
10db: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000010e0 <_ZNSt8ios_base4InitC1Ev@plt>:
10e0: f3 0f 1e fa endbr64
10e4: f2 ff 25 cd 2e 00 00 bnd jmpq *0x2ecd(%rip) # 3fb8 <_ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4>
10eb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000010f0 <_ZNSolsEi@plt>:
10f0: f3 0f 1e fa endbr64
10f4: f2 ff 25 c5 2e 00 00 bnd jmpq *0x2ec5(%rip) # 3fc0 <_ZNSolsEi@GLIBCXX_3.4>
10fb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Disassembly of section .text:
0000000000001100 <_start>:
1100: f3 0f 1e fa endbr64
1104: 31 ed xor %ebp,%ebp
1106: 49 89 d1 mov %rdx,%r9
1109: 5e pop %rsi
110a: 48 89 e2 mov %rsp,%rdx
110d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1111: 50 push %rax
1112: 54 push %rsp
1113: 4c 8d 05 f6 02 00 00 lea 0x2f6(%rip),%r8 # 1410 <__libc_csu_fini>
111a: 48 8d 0d 7f 02 00 00 lea 0x27f(%rip),%rcx # 13a0 <__libc_csu_init>
1121: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 11e9 <main>
1128: ff 15 b2 2e 00 00 callq *0x2eb2(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
112e: f4 hlt
112f: 90 nop
0000000000001130 <deregister_tm_clones>:
1130: 48 8d 3d e1 2e 00 00 lea 0x2ee1(%rip),%rdi # 4018 <__TMC_END__>
1137: 48 8d 05 da 2e 00 00 lea 0x2eda(%rip),%rax # 4018 <__TMC_END__>
113e: 48 39 f8 cmp %rdi,%rax
1141: 74 15 je 1158 <deregister_tm_clones+0x28>
1143: 48 8b 05 8e 2e 00 00 mov 0x2e8e(%rip),%rax # 3fd8 <_ITM_deregisterTMCloneTable>
114a: 48 85 c0 test %rax,%rax
114d: 74 09 je 1158 <deregister_tm_clones+0x28>
114f: ff e0 jmpq *%rax
1151: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
1158: c3 retq
1159: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001160 <register_tm_clones>:
1160: 48 8d 3d b1 2e 00 00 lea 0x2eb1(%rip),%rdi # 4018 <__TMC_END__>
1167: 48 8d 35 aa 2e 00 00 lea 0x2eaa(%rip),%rsi # 4018 <__TMC_END__>
116e: 48 29 fe sub %rdi,%rsi
1171: 48 89 f0 mov %rsi,%rax
1174: 48 c1 ee 3f shr $0x3f,%rsi
1178: 48 c1 f8 03 sar $0x3,%rax
117c: 48 01 c6 add %rax,%rsi
117f: 48 d1 fe sar %rsi
1182: 74 14 je 1198 <register_tm_clones+0x38>
1184: 48 8b 05 65 2e 00 00 mov 0x2e65(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable>
118b: 48 85 c0 test %rax,%rax
118e: 74 08 je 1198 <register_tm_clones+0x38>
1190: ff e0 jmpq *%rax
1192: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
1198: c3 retq
1199: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000011a0 <__do_global_dtors_aux>:
11a0: f3 0f 1e fa endbr64
11a4: 80 3d a5 2f 00 00 00 cmpb $0x0,0x2fa5(%rip) # 4150 <completed.8060>
11ab: 75 2b jne 11d8 <__do_global_dtors_aux+0x38>
11ad: 55 push %rbp
11ae: 48 83 3d 12 2e 00 00 cmpq $0x0,0x2e12(%rip) # 3fc8 <__cxa_finalize@GLIBC_2.2.5>
11b5: 00
11b6: 48 89 e5 mov %rsp,%rbp
11b9: 74 0c je 11c7 <__do_global_dtors_aux+0x27>
11bb: 48 8b 3d 46 2e 00 00 mov 0x2e46(%rip),%rdi # 4008 <__dso_handle>
11c2: e8 c9 fe ff ff callq 1090 <__cxa_finalize@plt>
11c7: e8 64 ff ff ff callq 1130 <deregister_tm_clones>
11cc: c6 05 7d 2f 00 00 01 movb $0x1,0x2f7d(%rip) # 4150 <completed.8060>
11d3: 5d pop %rbp
11d4: c3 retq
11d5: 0f 1f 00 nopl (%rax)
11d8: c3 retq
11d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000011e0 <frame_dummy>:
11e0: f3 0f 1e fa endbr64
11e4: e9 77 ff ff ff jmpq 1160 <register_tm_clones>
00000000000011e9 <main>:
11e9: f3 0f 1e fa endbr64
11ed: 55 push %rbp
11ee: 48 89 e5 mov %rsp,%rbp
11f1: 48 83 ec 10 sub $0x10,%rsp
11f5: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
11fc: 00 00
11fe: 48 89 45 f8 mov %rax,-0x8(%rbp)
1202: 31 c0 xor %eax,%eax
1204: 48 8d 45 f4 lea -0xc(%rbp),%rax
1208: be 02 00 00 00 mov $0x2,%esi
120d: 48 89 c7 mov %rax,%rdi
1210: e8 e7 00 00 00 callq 12fc <_ZN6ObjectC1Ei>
1215: 48 8d 45 f4 lea -0xc(%rbp),%rax
1219: 48 89 c7 mov %rax,%rdi
121c: e8 35 01 00 00 callq 1356 <_ZN6ObjectD1Ev>
1221: b8 00 00 00 00 mov $0x0,%eax
1226: 48 8b 55 f8 mov -0x8(%rbp),%rdx
122a: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
1231: 00 00
1233: 74 05 je 123a <main+0x51>
1235: e8 96 fe ff ff callq 10d0 <__stack_chk_fail@plt>
123a: c9 leaveq
123b: c3 retq
000000000000123c <_Z41__static_initialization_and_destruction_0ii>:
123c: f3 0f 1e fa endbr64
1240: 55 push %rbp
1241: 48 89 e5 mov %rsp,%rbp
1244: 48 83 ec 10 sub $0x10,%rsp
1248: 89 7d fc mov %edi,-0x4(%rbp)
124b: 89 75 f8 mov %esi,-0x8(%rbp)
124e: 83 7d fc 01 cmpl $0x1,-0x4(%rbp)
1252: 0f 85 88 00 00 00 jne 12e0 <_Z41__static_initialization_and_destruction_0ii+0xa4>
1258: 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp)
125f: 75 7f jne 12e0 <_Z41__static_initialization_and_destruction_0ii+0xa4>
1261: 48 8d 3d f4 2e 00 00 lea 0x2ef4(%rip),%rdi # 415c <_ZStL8__ioinit>
1268: e8 73 fe ff ff callq 10e0 <_ZNSt8ios_base4InitC1Ev@plt>
126d: 48 8d 15 94 2d 00 00 lea 0x2d94(%rip),%rdx # 4008 <__dso_handle>
1274: 48 8d 35 e1 2e 00 00 lea 0x2ee1(%rip),%rsi # 415c <_ZStL8__ioinit>
127b: 48 8b 05 76 2d 00 00 mov 0x2d76(%rip),%rax # 3ff8 <_ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4>
1282: 48 89 c7 mov %rax,%rdi
1285: e8 16 fe ff ff callq 10a0 <__cxa_atexit@plt>
128a: be 01 00 00 00 mov $0x1,%esi
128f: 48 8d 3d be 2e 00 00 lea 0x2ebe(%rip),%rdi # 4154 <o1>
1296: e8 61 00 00 00 callq 12fc <_ZN6ObjectC1Ei>
129b: 48 8d 15 66 2d 00 00 lea 0x2d66(%rip),%rdx # 4008 <__dso_handle>
12a2: 48 8d 35 ab 2e 00 00 lea 0x2eab(%rip),%rsi # 4154 <o1>
12a9: 48 8d 3d a6 00 00 00 lea 0xa6(%rip),%rdi # 1356 <_ZN6ObjectD1Ev>
12b0: e8 eb fd ff ff callq 10a0 <__cxa_atexit@plt>
12b5: be 03 00 00 00 mov $0x3,%esi
12ba: 48 8d 3d 97 2e 00 00 lea 0x2e97(%rip),%rdi # 4158 <o3>
12c1: e8 36 00 00 00 callq 12fc <_ZN6ObjectC1Ei>
12c6: 48 8d 15 3b 2d 00 00 lea 0x2d3b(%rip),%rdx # 4008 <__dso_handle>
12cd: 48 8d 35 84 2e 00 00 lea 0x2e84(%rip),%rsi # 4158 <o3>
12d4: 48 8d 3d 7b 00 00 00 lea 0x7b(%rip),%rdi # 1356 <_ZN6ObjectD1Ev>
12db: e8 c0 fd ff ff callq 10a0 <__cxa_atexit@plt>
12e0: 90 nop
12e1: c9 leaveq
12e2: c3 retq
00000000000012e3 <_GLOBAL__sub_I_o1>:
12e3: f3 0f 1e fa endbr64
12e7: 55 push %rbp
12e8: 48 89 e5 mov %rsp,%rbp
12eb: be ff ff 00 00 mov $0xffff,%esi
12f0: bf 01 00 00 00 mov $0x1,%edi
12f5: e8 42 ff ff ff callq 123c <_Z41__static_initialization_and_destruction_0ii>
12fa: 5d pop %rbp
12fb: c3 retq
00000000000012fc <_ZN6ObjectC1Ei>:
12fc: f3 0f 1e fa endbr64
1300: 55 push %rbp
1301: 48 89 e5 mov %rsp,%rbp
1304: 48 83 ec 10 sub $0x10,%rsp
1308: 48 89 7d f8 mov %rdi,-0x8(%rbp)
130c: 89 75 f4 mov %esi,-0xc(%rbp)
130f: 48 8b 45 f8 mov -0x8(%rbp),%rax
1313: 8b 55 f4 mov -0xc(%rbp),%edx
1316: 89 10 mov %edx,(%rax)
1318: 48 8d 35 e6 0c 00 00 lea 0xce6(%rip),%rsi # 2005 <_ZStL19piecewise_construct+0x1>
131f: 48 8d 3d 1a 2d 00 00 lea 0x2d1a(%rip),%rdi # 4040 <_ZSt4cout@@GLIBCXX_3.4>
1326: e8 85 fd ff ff callq 10b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
132b: 48 89 c2 mov %rax,%rdx
132e: 48 8b 45 f8 mov -0x8(%rbp),%rax
1332: 8b 00 mov (%rax),%eax
1334: 89 c6 mov %eax,%esi
1336: 48 89 d7 mov %rdx,%rdi
1339: e8 b2 fd ff ff callq 10f0 <_ZNSolsEi@plt>
133e: 48 89 c2 mov %rax,%rdx
1341: 48 8b 05 88 2c 00 00 mov 0x2c88(%rip),%rax # 3fd0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4>
1348: 48 89 c6 mov %rax,%rsi
134b: 48 89 d7 mov %rdx,%rdi
134e: e8 6d fd ff ff callq 10c0 <_ZNSolsEPFRSoS_E@plt>
1353: 90 nop
1354: c9 leaveq
1355: c3 retq
0000000000001356 <_ZN6ObjectD1Ev>:
1356: f3 0f 1e fa endbr64
135a: 55 push %rbp
135b: 48 89 e5 mov %rsp,%rbp
135e: 48 83 ec 10 sub $0x10,%rsp
1362: 48 89 7d f8 mov %rdi,-0x8(%rbp)
1366: 48 8d 35 a1 0c 00 00 lea 0xca1(%rip),%rsi # 200e <_ZStL19piecewise_construct+0xa>
136d: 48 8d 3d cc 2c 00 00 lea 0x2ccc(%rip),%rdi # 4040 <_ZSt4cout@@GLIBCXX_3.4>
1374: e8 37 fd ff ff callq 10b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
1379: 48 89 c2 mov %rax,%rdx
137c: 48 8b 05 4d 2c 00 00 mov 0x2c4d(%rip),%rax # 3fd0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4>
1383: 48 89 c6 mov %rax,%rsi
1386: 48 89 d7 mov %rdx,%rdi
1389: e8 32 fd ff ff callq 10c0 <_ZNSolsEPFRSoS_E@plt>
138e: 90 nop
138f: c9 leaveq
1390: c3 retq
1391: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
1398: 00 00 00
139b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000013a0 <__libc_csu_init>:
13a0: f3 0f 1e fa endbr64
13a4: 41 57 push %r15
13a6: 4c 8d 3d bb 29 00 00 lea 0x29bb(%rip),%r15 # 3d68 <__frame_dummy_init_array_entry>
13ad: 41 56 push %r14
13af: 49 89 d6 mov %rdx,%r14
13b2: 41 55 push %r13
13b4: 49 89 f5 mov %rsi,%r13
13b7: 41 54 push %r12
13b9: 41 89 fc mov %edi,%r12d
13bc: 55 push %rbp
13bd: 48 8d 2d b4 29 00 00 lea 0x29b4(%rip),%rbp # 3d78 <__do_global_dtors_aux_fini_array_entry>
13c4: 53 push %rbx
13c5: 4c 29 fd sub %r15,%rbp
13c8: 48 83 ec 08 sub $0x8,%rsp
13cc: e8 2f fc ff ff callq 1000 <_init>
13d1: 48 c1 fd 03 sar $0x3,%rbp
13d5: 74 1f je 13f6 <__libc_csu_init+0x56>
13d7: 31 db xor %ebx,%ebx
13d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
13e0: 4c 89 f2 mov %r14,%rdx
13e3: 4c 89 ee mov %r13,%rsi
13e6: 44 89 e7 mov %r12d,%edi
13e9: 41 ff 14 df callq *(%r15,%rbx,8)
13ed: 48 83 c3 01 add $0x1,%rbx
13f1: 48 39 dd cmp %rbx,%rbp
13f4: 75 ea jne 13e0 <__libc_csu_init+0x40>
13f6: 48 83 c4 08 add $0x8,%rsp
13fa: 5b pop %rbx
13fb: 5d pop %rbp
13fc: 41 5c pop %r12
13fe: 41 5d pop %r13
1400: 41 5e pop %r14
1402: 41 5f pop %r15
1404: c3 retq
1405: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)
140c: 00 00 00 00
0000000000001410 <__libc_csu_fini>:
1410: f3 0f 1e fa endbr64
1414: c3 retq
Disassembly of section .fini:
0000000000001418 <_fini>:
1418: f3 0f 1e fa endbr64
141c: 48 83 ec 08 sub $0x8,%rsp
1420: 48 83 c4 08 add $0x8,%rsp
1424: c3 retq
局部对象的构造和析构
首先我们找到main函数的反汇编代码:
00000000000011e9 <main>:
11e9: f3 0f 1e fa endbr64
11ed: 55 push %rbp
11ee: 48 89 e5 mov %rsp,%rbp
11f1: 48 83 ec 10 sub $0x10,%rsp
11f5: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
11fc: 00 00
11fe: 48 89 45 f8 mov %rax,-0x8(%rbp)
1202: 31 c0 xor %eax,%eax
1204: 48 8d 45 f4 lea -0xc(%rbp),%rax
1208: be 02 00 00 00 mov $0x2,%esi
120d: 48 89 c7 mov %rax,%rdi
1210: e8 e7 00 00 00 callq 12fc <_ZN6ObjectC1Ei>
1215: 48 8d 45 f4 lea -0xc(%rbp),%rax
1219: 48 89 c7 mov %rax,%rdi
121c: e8 35 01 00 00 callq 1356 <_ZN6ObjectD1Ev>
1221: b8 00 00 00 00 mov $0x0,%eax
1226: 48 8b 55 f8 mov -0x8(%rbp),%rdx
122a: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
1231: 00 00
1233: 74 05 je 123a <main+0x51>
1235: e8 96 fe ff ff callq 10d0 <__stack_chk_fail@plt>
123a: c9 leaveq
123b: c3 retq
第10行,将对象的地址传递给rax寄存器。
第11行,将括号中的参数传递给寄存器esi。
第12行,将rax寄存器的地址传递给隐藏参数&obj(this指针)。
在第16行,调用析构函数,释放函数占用的资源。
构造函数:
00000000000012fc <_ZN6ObjectC1Ei>:
12fc: f3 0f 1e fa endbr64
1300: 55 push %rbp
1301: 48 89 e5 mov %rsp,%rbp
1304: 48 83 ec 10 sub $0x10,%rsp
1308: 48 89 7d f8 mov %rdi,-0x8(%rbp)
130c: 89 75 f4 mov %esi,-0xc(%rbp)
130f: 48 8b 45 f8 mov -0x8(%rbp),%rax
1313: 8b 55 f4 mov -0xc(%rbp),%edx
1316: 89 10 mov %edx,(%rax)
1318: 48 8d 35 e6 0c 00 00 lea 0xce6(%rip),%rsi # 2005 <_ZStL19piecewise_construct+0x1>
131f: 48 8d 3d 1a 2d 00 00 lea 0x2d1a(%rip),%rdi # 4040 <_ZSt4cout@@GLIBCXX_3.4>
1326: e8 85 fd ff ff callq 10b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
132b: 48 89 c2 mov %rax,%rdx
132e: 48 8b 45 f8 mov -0x8(%rbp),%rax
1332: 8b 00 mov (%rax),%eax
1334: 89 c6 mov %eax,%esi
1336: 48 89 d7 mov %rdx,%rdi
1339: e8 b2 fd ff ff callq 10f0 <_ZNSolsEi@plt>
133e: 48 89 c2 mov %rax,%rdx
1341: 48 8b 05 88 2c 00 00 mov 0x2c88(%rip),%rax # 3fd0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4>
1348: 48 89 c6 mov %rax,%rsi
134b: 48 89 d7 mov %rdx,%rdi
134e: e8 6d fd ff ff callq 10c0 <_ZNSolsEPFRSoS_E@plt>
1353: 90 nop
1354: c9 leaveq
1355: c3 retq
析构函数:
0000000000001356 <_ZN6ObjectD1Ev>:
1356: f3 0f 1e fa endbr64
135a: 55 push %rbp
135b: 48 89 e5 mov %rsp,%rbp
135e: 48 83 ec 10 sub $0x10,%rsp
1362: 48 89 7d f8 mov %rdi,-0x8(%rbp)
1366: 48 8d 35 a1 0c 00 00 lea 0xca1(%rip),%rsi # 200e <_ZStL19piecewise_construct+0xa>
136d: 48 8d 3d cc 2c 00 00 lea 0x2ccc(%rip),%rdi # 4040 <_ZSt4cout@@GLIBCXX_3.4>
1374: e8 37 fd ff ff callq 10b0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
1379: 48 89 c2 mov %rax,%rdx
137c: 48 8b 05 4d 2c 00 00 mov 0x2c4d(%rip),%rax # 3fd0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4>
1383: 48 89 c6 mov %rax,%rsi
1386: 48 89 d7 mov %rdx,%rdi
1389: e8 32 fd ff ff callq 10c0 <_ZNSolsEPFRSoS_E@plt>
138e: 90 nop
138f: c9 leaveq
1390: c3 retq
1391: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
1398: 00 00 00
139b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
全局对象的构造和析构
从上述代码可以看出,局部对象o2的构造和析构在main函数中。
那么全局对象o1和o3的构造和析构在哪呢?
我们发现存在名字为<_Z41__static_initialization_and_destruction_0ii>的汇编代码:
000000000000123c <_Z41__static_initialization_and_destruction_0ii>:
123c: f3 0f 1e fa endbr64
1240: 55 push %rbp
1241: 48 89 e5 mov %rsp,%rbp
1244: 48 83 ec 10 sub $0x10,%rsp
1248: 89 7d fc mov %edi,-0x4(%rbp)
124b: 89 75 f8 mov %esi,-0x8(%rbp)
124e: 83 7d fc 01 cmpl $0x1,-0x4(%rbp)
1252: 0f 85 88 00 00 00 jne 12e0 <_Z41__static_initialization_and_destruction_0ii+0xa4>
1258: 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp)
125f: 75 7f jne 12e0 <_Z41__static_initialization_and_destruction_0ii+0xa4>
1261: 48 8d 3d f4 2e 00 00 lea 0x2ef4(%rip),%rdi # 415c <_ZStL8__ioinit>
1268: e8 73 fe ff ff callq 10e0 <_ZNSt8ios_base4InitC1Ev@plt>
126d: 48 8d 15 94 2d 00 00 lea 0x2d94(%rip),%rdx # 4008 <__dso_handle>
1274: 48 8d 35 e1 2e 00 00 lea 0x2ee1(%rip),%rsi # 415c <_ZStL8__ioinit>
127b: 48 8b 05 76 2d 00 00 mov 0x2d76(%rip),%rax # 3ff8 <_ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4>
1282: 48 89 c7 mov %rax,%rdi
1285: e8 16 fe ff ff callq 10a0<__cxa_atexit@plt>
128a: be 01 00 00 00 mov $0x1,%esi
128f: 48 8d 3d be 2e 00 00 lea 0x2ebe(%rip),%rdi # 4154 <o1>
1296: e8 61 00 00 00 callq 12fc <_ZN6ObjectC1Ei>
129b: 48 8d 15 66 2d 00 00 lea 0x2d66(%rip),%rdx # 4008 <__dso_handle>
12a2: 48 8d 35 ab 2e 00 00 lea 0x2eab(%rip),%rsi # 4154 <o1>
12a9: 48 8d 3d a6 00 00 00 lea 0xa6(%rip),%rdi # 1356 <_ZN6ObjectD1Ev>
12b0: e8 eb fd ff ff callq 10a0<__cxa_atexit@plt>
12b5: be 03 00 00 00 mov $0x3,%esi
12ba: 48 8d 3d 97 2e 00 00 lea 0x2e97(%rip),%rdi # 4158 <o3>
12c1: e8 36 00 00 00 callq 12fc <_ZN6ObjectC1Ei>
12c6: 48 8d 15 3b 2d 00 00 lea 0x2d3b(%rip),%rdx # 4008 <__dso_handle>
12cd: 48 8d 35 84 2e 00 00 lea 0x2e84(%rip),%rsi # 4158 <o3>
12d4: 48 8d 3d 7b 00 00 00 lea 0x7b(%rip),%rdi # 1356 <_ZN6ObjectD1Ev>
12db: e8 c0 fd ff ff callq 10a0<__cxa_atexit@plt>
12e0: 90 nop
12e1: c9 leaveq
12e2: c3 retq
静态对象(全局对象)初始化和析构。
第21行调用构造函数,
第25行调用回调函数,
第28行调用构造函数。
在这个函数中调用了静态对象(全局对象)的构造函数,并且将其析构函数通过_cxa_atexit函数注册,使其能在exit时调用。
00000000000012e3 <_GLOBAL__sub_I_o1>:
12e3: f3 0f 1e fa endbr64
12e7: 55 push %rbp
12e8: 48 89 e5 mov %rsp,%rbp
12eb: be ff ff 00 00 mov $0xffff,%esi
12f0: bf 01 00 00 00 mov $0x1,%edi
12f5: e8 42 ff ff ff callq 123c <_Z41__static_initialization_and_destruction_0ii>
12fa: 5d pop %rbp
12fb: c3 retq
_GLOBAL__sub_I_o1函数负责本编译单元所有全局\静态对象的构造和析构,那么这个函数在哪里被调用的呢?我们下面将查看程序是在哪开始执行的。
实际上在Linux环境下,glibc程序执行的入口地址是_start,这个入口是由ld链接器默认的链接脚本所指定的,当然也可以通过相关参数设定自己的入口。
程序的入口
程序入口:_start
0000000000001100 <_start>:
1100: f3 0f 1e fa endbr64
1104: 31 ed xor %ebp,%ebp
1106: 49 89 d1 mov %rdx,%r9
1109: 5e pop %rsi
110a: 48 89 e2 mov %rsp,%rdx
110d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1111: 50 push %rax
1112: 54 push %rsp
1113: 4c 8d 05 f6 02 00 00 lea 0x2f6(%rip),%r8 # 1410 <__libc_csu_fini>
111a: 48 8d 0d 7f 02 00 00 lea 0x27f(%rip),%rcx # 13a0 <__libc_csu_init>
1121: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 11e9 <main>
1128: ff 15 b2 2e 00 00 callq *0x2eb2(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
112e: f4 hlt
112f: 90 nop
汇编中的注释非常清晰。
第12行才是main函数的地址。
__ libc_csu_init函数的地址是全局对象构造的地址。
__ libc_csu_fini是全局对象析构的地址。
从_ start的汇编代码可以看出,实际上_ start函数里面调用了_ libc_start_main函数,并把_libc_csu_init函数和__libc_csu_fini函数以及main函数的地址作为参数传递进去。
由于__libc_start_main函数是在glibc动态链接库里的函数,所以可执行文件的反汇编代码中并没有这一部分的代码,不过我们只需要大概了解其中先后调用关系如下:
--->_start
--->_lib_start_main
--->__ libc_csu_init
--->main
--->__libc_csu_fini
_libc_csu_init函数
00000000000013a0 <__libc_csu_init>:
13a0: f3 0f 1e fa endbr64
13a4: 41 57 push %r15
13a6: 4c 8d 3d bb 29 00 00 lea 0x29bb(%rip),%r15 # 3d68 <__frame_dummy_init_array_entry>
13ad: 41 56 push %r14
13af: 49 89 d6 mov %rdx,%r14
13b2: 41 55 push %r13
13b4: 49 89 f5 mov %rsi,%r13
13b7: 41 54 push %r12
13b9: 41 89 fc mov %edi,%r12d
13bc: 55 push %rbp
13bd: 48 8d 2d b4 29 00 00 lea 0x29b4(%rip),%rbp # 3d78 <__do_global_dtors_aux_fini_array_entry>
13c4: 53 push %rbx
13c5: 4c 29 fd sub %r15,%rbp
13c8: 48 83 ec 08 sub $0x8,%rsp
13cc: e8 2f fc ff ff callq 1000 <_init>
13d1: 48 c1 fd 03 sar $0x3,%rbp
13d5: 74 1f je 13f6 <__libc_csu_init+0x56>
13d7: 31 db xor %ebx,%ebx
13d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
13e0: 4c 89 f2 mov %r14,%rdx
13e3: 4c 89 ee mov %r13,%rsi
13e6: 44 89 e7 mov %r12d,%edi
13e9: 41 ff 14 df callq *(%r15,%rbx,8)
13ed: 48 83 c3 01 add $0x1,%rbx
13f1: 48 39 dd cmp %rbx,%rbp
13f4: 75 ea jne 13e0 <__libc_csu_init+0x40>
13f6: 48 83 c4 08 add $0x8,%rsp
13fa: 5b pop %rbx
13fb: 5d pop %rbp
13fc: 41 5c pop %r12
13fe: 41 5d pop %r13
1400: 41 5e pop %r14
1402: 41 5f pop %r15
1404: c3 retq
1405: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)
140c: 00 00 00 00
光看汇编代码有点难以理解,我们可以去查看glibc的源代码中的__libc_csu_init函数,其中关键代码部分:
void __libc_csu_init (int argc, char **argv, char **envp){
...
const size_t size = __init_array_end - __init_array_start;
for (size_t i = 0; i < size; i++)
(*__init_array_start [i]) (argc, argv, envp); //事实上这个__init_array_satrt数组中就有全局对象构造函数的地址
}
...
}
可以看出,__ libc_csu_init函数中会将__init_array_start数组中每个指针指向的函数执行一遍。
现在回到原来的问题,负责本编译单元所有全局\静态对象的构造和析构的_GLOBAL__sub_I_o1函数的指针被保存在__init_array_start数组中,也就是在__libc_csu_init函数中被调用的。
那么_GLOBAL__sub_I_o1函数的指针怎么被放进 __ init_array_start数组的呢?答案是,一旦一个目标文件里有一个这样的函数,编译器会在这个编译单元产生的目标文件(.o)文件的“.init_array”段中放置一个指针,这个指针指向的就是_GLOBAL__sub_I_a1函数。
//objdump -s
......
Contents of section .init_array:
600dc8 b0054000 00000000 2d064000 00000000 ..@.....-.@.....
......
析构
在__lib_start_main 函数执行完main函数之后,执行exit函数:
void exit(int status){
while(__exit_func != NULL){
...
__exit_funcs = __exit_funcs->next; //__exit_funcs是存储由_cxa_atexit组成的函数的链表,
//这里的while循环则遍历该链表并逐个调用这些注册的函数
}
...
_exit(status); //调用exit系统调用,进程直接结束
}
总结
--->_start
--->_lib_start_main
--->__ libc_csu_init
--->main
--->__libc_csu_fini
_start
---> _libc_start_main
------> _libc_csu_init
---------> _GLOBAL__sub_I_o1
------------> _Z41__static_initialization_and_destruction_0ii
---------------> _ZN6ObjectC1Ei #全局构造函数
------> main #main函数
------> exit
---------> _ZN6ObjectD1Ev #全局析构函数