··· } .jcr : { KEEP (*(.jcr)) } .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } .dynamic : { *(.dynamic) } .got : { *(.got) *(.igot) } . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); .got.plt : { *(.got.plt) *(.igot.plt) } .data : { *(.data .data.* .gnu.linkonce.d.*) SORT(CONSTRUCTORS) } .data1 : { *(.data1) } _edata = .; PROVIDE (edata = .); . = .; __bss_start = .; .bss : { *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. FIXME: Why do we need it? When there is no .bss section, we don't pad the .data section. */ . = ALIGN(. != 0 ? 64 / 8 : 1); } .lbss : { *(.dynlbss) *(.lbss .lbss.* .gnu.linkonce.lb.*) *(LARGE_COMMON) } . = ALIGN(64 / 8); . = SEGMENT_START("ldata-segment", .); .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : { *(.lrodata .lrodata.* .gnu.linkonce.lr.*) } .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : { *(.ldata .ldata.* .gnu.linkonce.l.*) . = ALIGN(. != 0 ? 64 / 8 : 1); } . = ALIGN(64 / 8); _end = .; PROVIDE (end = .); . = DATA_SEGMENT_END (.); ···
现在执行 hello 文件来看看:
1 2 3 4 5 6 7 8
bss start: 0x558cdfa3c018 bss end: 0x558cdfa3c028 data start: 0x558cdfa3c000 data end: 0x558cdfa3c018 a address: 0x558cdfa3c01c b address: 0x558cdfa3c010 c address: 0x558cdfa3c020 d address: 0x558cdfa3c014
其中 a,c 在 bss 段内(0x558cdfa3c018~0x558cdfa3c028),b,d 在 data 段内(0x558cdfa3c000~0x558cdfa3c018),而我们在源文件中可以看大到 a 和 c 都是未经过初始化而 b 和 d 都是经过初始化的。由此可以证明以上的结论。
像上面那样进行连接测试,经测试发现,不管 main.o 在前还是在后都不会影响对象 j 和 d 的初始化次序。为什么可以这样呢?这个手法的基础在于:
C++ 保证,函数内的 local static 对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。所以如果你以“函数调用”(返回一个 reference 指向 local static 对象)替换“直接访问 non-local static 对象”,你就获得了保证,保证你所获得的那个 reference 将指向一个历经初始化的对象。