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-64Disassembly of section .init:0000000000001000 : 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 100f: 48 85 c0 test %rax,%rax 1012: 74 02 je 1016 1014: ff d0 callq *%rax 1016: 48 83 c4 08 add $0x8,%rsp 101a: c3 retq Disassembly of section .plt:0000000000001020 : 1020: ff 35 62 2f 00 00 pushq 0x2f62(%rip) # 3f88 1026: f2 ff 25 63 2f 00 00 bnd jmpq *0x2f63(%rip) # 3f90 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 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 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 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 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 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 108f: 90 nopDisassembly of section .plt.got:0000000000001090 : 1090: f3 0f 1e fa endbr64 1094: f2 ff 25 2d 2f 00 00 bnd jmpq *0x2f2d(%rip) # 3fc8 109b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)Disassembly of section .plt.sec:00000000000010a0 : 10a0: f3 0f 1e fa endbr64 10a4: f2 ff 25 ed 2e 00 00 bnd jmpq *0x2eed(%rip) # 3f98 10ab: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)00000000000010b0 : 10b0: f3 0f 1e fa endbr64 10b4: f2 ff 25 e5 2e 00 00 bnd jmpq *0x2ee5(%rip) # 3fa0 10bb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)00000000000010c0 : 10c0: f3 0f 1e fa endbr64 10c4: f2 ff 25 dd 2e 00 00 bnd jmpq *0x2edd(%rip) # 3fa8 10cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)00000000000010d0 : 10d0: f3 0f 1e fa endbr64 10d4: f2 ff 25 d5 2e 00 00 bnd jmpq *0x2ed5(%rip) # 3fb0 10db: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)00000000000010e0 : 10e0: f3 0f 1e fa endbr64 10e4: f2 ff 25 cd 2e 00 00 bnd jmpq *0x2ecd(%rip) # 3fb8 10eb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)00000000000010f0 : 10f0: f3 0f 1e fa endbr64 10f4: f2 ff 25 c5 2e 00 00 bnd jmpq *0x2ec5(%rip) # 3fc0 10fb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)Disassembly of section .text:0000000000001100 : 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 111a: 48 8d 0d 7f 02 00 00 lea 0x27f(%rip),%rcx # 13a0 1121: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 11e9 1128: ff 15 b2 2e 00 00 callq *0x2eb2(%rip) # 3fe0 112e: f4 hlt 112f: 90 nop0000000000001130 : 1130: 48 8d 3d e1 2e 00 00 lea 0x2ee1(%rip),%rdi # 4018 1137: 48 8d 05 da 2e 00 00 lea 0x2eda(%rip),%rax # 4018 113e: 48 39 f8 cmp %rdi,%rax 1141: 74 15 je 1158 1143: 48 8b 05 8e 2e 00 00 mov 0x2e8e(%rip),%rax # 3fd8 114a: 48 85 c0 test %rax,%rax 114d: 74 09 je 1158 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 : 1160: 48 8d 3d b1 2e 00 00 lea 0x2eb1(%rip),%rdi # 4018 1167: 48 8d 35 aa 2e 00 00 lea 0x2eaa(%rip),%rsi # 4018 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 1184: 48 8b 05 65 2e 00 00 mov 0x2e65(%rip),%rax # 3ff0 118b: 48 85 c0 test %rax,%rax 118e: 74 08 je 1198 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 : 11a0: f3 0f 1e fa endbr64 11a4: 80 3d a5 2f 00 00 00 cmpb $0x0,0x2fa5(%rip) # 4150 11ab: 75 2b jne 11d8 11ad: 55 push %rbp 11ae: 48 83 3d 12 2e 00 00 cmpq $0x0,0x2e12(%rip) # 3fc8 11b5: 00 11b6: 48 89 e5 mov %rsp,%rbp 11b9: 74 0c je 11c7 11bb: 48 8b 3d 46 2e 00 00 mov 0x2e46(%rip),%rdi # 4008 11c2: e8 c9 fe ff ff callq 1090 11c7: e8 64 ff ff ff callq 1130 11cc: c6 05 7d 2f 00 00 01 movb $0x1,0x2f7d(%rip) # 4150 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 : 11e0: f3 0f 1e fa endbr64 11e4: e9 77 ff ff ff jmpq 1160 00000000000011e9 : 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 1215: 48 8d 45 f4 lea -0xc(%rbp),%rax 1219: 48 89 c7 mov %rax,%rdi 121c: e8 35 01 00 00 callq 1356 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 1235: e8 96 fe ff ff callq 10d0 123a: c9 leaveq 123b: c3 retq 000000000000123c : 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 1258: 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp) 125f: 75 7f jne 12e0 1261: 48 8d 3d f4 2e 00 00 lea 0x2ef4(%rip),%rdi # 415c 1268: e8 73 fe ff ff callq 10e0 126d: 48 8d 15 94 2d 00 00 lea 0x2d94(%rip),%rdx # 4008 1274: 48 8d 35 e1 2e 00 00 lea 0x2ee1(%rip),%rsi # 415c 127b: 48 8b 05 76 2d 00 00 mov 0x2d76(%rip),%rax # 3ff8 1282: 48 89 c7 mov %rax,%rdi 1285: e8 16 fe ff ff callq 10a0 128a: be 01 00 00 00 mov $0x1,%esi 128f: 48 8d 3d be 2e 00 00 lea 0x2ebe(%rip),%rdi # 4154 1296: e8 61 00 00 00 callq 12fc 129b: 48 8d 15 66 2d 00 00 lea 0x2d66(%rip),%rdx # 4008 12a2: 48 8d 35 ab 2e 00 00 lea 0x2eab(%rip),%rsi # 4154 12a9: 48 8d 3d a6 00 00 00 lea 0xa6(%rip),%rdi # 1356 12b0: e8 eb fd ff ff callq 10a0 12b5: be 03 00 00 00 mov $0x3,%esi 12ba: 48 8d 3d 97 2e 00 00 lea 0x2e97(%rip),%rdi # 4158 12c1: e8 36 00 00 00 callq 12fc 12c6: 48 8d 15 3b 2d 00 00 lea 0x2d3b(%rip),%rdx # 4008 12cd: 48 8d 35 84 2e 00 00 lea 0x2e84(%rip),%rsi # 4158 12d4: 48 8d 3d 7b 00 00 00 lea 0x7b(%rip),%rdi # 1356 12db: e8 c0 fd ff ff callq 10a0 12e0: 90 nop 12e1: c9 leaveq 12e2: c3 retq 00000000000012e3 : 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 12fa: 5d pop %rbp 12fb: c3 retq 00000000000012fc : 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 131f: 48 8d 3d 1a 2d 00 00 lea 0x2d1a(%rip),%rdi # 4040 1326: e8 85 fd ff ff callq 10b0 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 133e: 48 89 c2 mov %rax,%rdx 1341: 48 8b 05 88 2c 00 00 mov 0x2c88(%rip),%rax # 3fd0 1348: 48 89 c6 mov %rax,%rsi 134b: 48 89 d7 mov %rdx,%rdi 134e: e8 6d fd ff ff callq 10c0 1353: 90 nop 1354: c9 leaveq 1355: c3 retq 0000000000001356 : 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 136d: 48 8d 3d cc 2c 00 00 lea 0x2ccc(%rip),%rdi # 4040 1374: e8 37 fd ff ff callq 10b0 1379: 48 89 c2 mov %rax,%rdx 137c: 48 8b 05 4d 2c 00 00 mov 0x2c4d(%rip),%rax # 3fd0 1383: 48 89 c6 mov %rax,%rsi 1386: 48 89 d7 mov %rdx,%rdi 1389: e8 32 fd ff ff callq 10c0 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 : 13a0: f3 0f 1e fa endbr64 13a4: 41 57 push %r15 13a6: 4c 8d 3d bb 29 00 00 lea 0x29bb(%rip),%r15 # 3d68 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 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 13d1: 48 c1 fd 03 sar $0x3,%rbp 13d5: 74 1f je 13f6 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 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 : 1410: f3 0f 1e fa endbr64 1414: c3 retq Disassembly of section .fini:0000000000001418 : 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 : 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 1215: 48 8d 45 f4 lea -0xc(%rbp),%rax 1219: 48 89 c7 mov %rax,%rdi 121c: e8 35 01 00 00 callq 1356 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 1235: e8 96 fe ff ff callq 10d0 123a: c9 leaveq 123b: c3 retq
从main函数的反汇编代码中,我们可以看出,在main函数只构造了o2这一个对象,o2的构造函数被命名为 _ZN6ObjectC1Ei,析构函数被命名为 _ZN6ObjectD1Ev。
第10行,将对象的地址传递给rax寄存器。
第11行,将括号中的参数传递给寄存器esi。
第12行,将rax寄存器的地址传递给隐藏参数&obj(this指针)。
在第16行,调用析构函数,释放函数占用的资源。
构造函数:
00000000000012fc : 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 131f: 48 8d 3d 1a 2d 00 00 lea 0x2d1a(%rip),%rdi # 4040 1326: e8 85 fd ff ff callq 10b0 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 133e: 48 89 c2 mov %rax,%rdx 1341: 48 8b 05 88 2c 00 00 mov 0x2c88(%rip),%rax # 3fd0 1348: 48 89 c6 mov %rax,%rsi 134b: 48 89 d7 mov %rdx,%rdi 134e: e8 6d fd ff ff callq 10c0 1353: 90 nop 1354: c9 leaveq 1355: c3 retq
析构函数:
0000000000001356 : 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 136d: 48 8d 3d cc 2c 00 00 lea 0x2ccc(%rip),%rdi # 4040 1374: e8 37 fd ff ff callq 10b0 1379: 48 89 c2 mov %rax,%rdx 137c: 48 8b 05 4d 2c 00 00 mov 0x2c4d(%rip),%rax # 3fd0 1383: 48 89 c6 mov %rax,%rsi 1386: 48 89 d7 mov %rdx,%rdi 1389: e8 32 fd ff ff callq 10c0 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 : 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 1258: 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp) 125f: 75 7f jne 12e0 1261: 48 8d 3d f4 2e 00 00 lea 0x2ef4(%rip),%rdi # 415c 1268: e8 73 fe ff ff callq 10e0 126d: 48 8d 15 94 2d 00 00 lea 0x2d94(%rip),%rdx # 4008 1274: 48 8d 35 e1 2e 00 00 lea 0x2ee1(%rip),%rsi # 415c 127b: 48 8b 05 76 2d 00 00 mov 0x2d76(%rip),%rax # 3ff8 1282: 48 89 c7 mov %rax,%rdi 1285: e8 16 fe ff ff callq 10a0 128a: be 01 00 00 00 mov $0x1,%esi 128f: 48 8d 3d be 2e 00 00 lea 0x2ebe(%rip),%rdi # 4154 1296: e8 61 00 00 00 callq 12fc 129b: 48 8d 15 66 2d 00 00 lea 0x2d66(%rip),%rdx # 4008 12a2: 48 8d 35 ab 2e 00 00 lea 0x2eab(%rip),%rsi # 4154 12a9: 48 8d 3d a6 00 00 00 lea 0xa6(%rip),%rdi # 1356 12b0: e8 eb fd ff ff callq 10a0 12b5: be 03 00 00 00 mov $0x3,%esi 12ba: 48 8d 3d 97 2e 00 00 lea 0x2e97(%rip),%rdi # 4158 12c1: e8 36 00 00 00 callq 12fc 12c6: 48 8d 15 3b 2d 00 00 lea 0x2d3b(%rip),%rdx # 4008 12cd: 48 8d 35 84 2e 00 00 lea 0x2e84(%rip),%rsi # 4158 12d4: 48 8d 3d 7b 00 00 00 lea 0x7b(%rip),%rdi # 1356 12db: e8 c0 fd ff ff callq 10a0 12e0: 90 nop 12e1: c9 leaveq 12e2: c3 retq
静态对象(全局对象)初始化和析构。
第21行调用构造函数,
第25行调用回调函数,
第28行调用构造函数。
在这个函数中调用了静态对象(全局对象)的构造函数,并且将其析构函数通过_cxa_atexit函数注册,使其能在exit时调用。
00000000000012e3 : 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 12fa: 5d pop %rbp 12fb: c3 retq
_GLOBAL__sub_I_o1函数负责本编译单元所有全局\静态对象的构造和析构,那么这个函数在哪里被调用的呢?我们下面将查看程序是在哪开始执行的。
实际上在Linux环境下,glibc程序执行的入口地址是_start,这个入口是由ld链接器默认的链接脚本所指定的,当然也可以通过相关参数设定自己的入口。
程序的入口
程序入口:_start
0000000000001100 : 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 111a: 48 8d 0d 7f 02 00 00 lea 0x27f(%rip),%rcx # 13a0 1121: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 11e9 1128: ff 15 b2 2e 00 00 callq *0x2eb2(%rip) # 3fe0 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 : 13a0: f3 0f 1e fa endbr64 13a4: 41 57 push %r15 13a6: 4c 8d 3d bb 29 00 00 lea 0x29bb(%rip),%r15 # 3d68 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 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 13d1: 48 c1 fd 03 sar $0x3,%rbp 13d5: 74 1f je 13f6 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 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 #全局析构函数