libstdc ++的make_shared布局是否在gcc 4.x和gcc 6.x之间发生了变化?

Bar*_*rry 6 c++ gcc g++ abi libstdc++

请考虑以下最小示例,包含三个文件:

foo.h:

#pragma once
#include <memory>

struct X {
    uint64_t i = 0xdeadbeefdeadbeefULL;
};

void foo();
Run Code Online (Sandbox Code Playgroud)

foo.cxx:

#include "foo.h"

void foo() {
    std::make_shared<X>();
}
Run Code Online (Sandbox Code Playgroud)

main.cxx:

#include <memory>
#include "foo.h"

template std::shared_ptr<X> std::make_shared();

int main() {
    foo();
}
Run Code Online (Sandbox Code Playgroud)

然后使用不同版本的gcc编译两个翻译单元:

$ g++-4.8.2 -g -std=c++11 -O0 -c foo.cxx -o foo.o
$ g++-6.2.0 -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0 -O0 -g main.cxx foo.o -fsanitize=address -fno-omit-frame-pointer
Run Code Online (Sandbox Code Playgroud)

请注意,我是专门用旧的ABI编译的.

运行生成的可执行文件(如果两个TU都使用相同版本的gcc编译,则不会):

==33535==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300000eff8 at pc 0x000000401dcf bp 0x7fffffffd7f0 sp 0x7fffffffd7e8
WRITE of size 8 at 0x60300000eff8 thread T0
    #0 0x401dce in X::X() (.../a.out+0x401dce)
    #1 0x402758 in _ZN9__gnu_cxx13new_allocatorI1XE9constructIS1_IEEEvPT_DpOT0_ /.../gcc-4.8.2/include/c++/4.8.2/ext/new_allocator.h:120
    #2 0x402721 in _ZNSt16allocator_traitsISaI1XEE12_S_constructIS0_IEEENSt9enable_ifIXsrNS2_18__construct_helperIT_IDpT0_EEE5valueEvE4typeERS1_PS6_DpOS7_ /.../gcc-4.8.2/include/c++/4.8.2/bits/alloc_traits.h:254
    #3 0x4026fc in _ZNSt16allocator_traitsISaI1XEE9constructIS0_IEEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS1_PT_DpOS4_ /.../gcc-4.8.2/include/c++/4.8.2/bits/alloc_traits.h:393
    #4 0x4026a6 in std::_Sp_counted_ptr_inplace<X, std::allocator<X>, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<>(std::allocator<X>) /.../gcc-4.8.2/include/c++/4.8.2/bits/shared_ptr_base.h:399
    #5 0x4025d4 in _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI1XSaIS2_ELNS_12_Lock_policyE2EEE9constructIS5_IKS3_EEEvPT_DpOT0_ /.../gcc-4.8.2/include/c++/4.8.2/ext/new_allocator.h:120
    #6 0x402572 in _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI1XSaIS1_ELN9__gnu_cxx12_Lock_policyE2EEEE12_S_constructIS5_IKS2_EEENSt9enable_ifIXsrNS7_18__construct_helperIT_IDpT0_EEE5valueEvE4typeERS6_PSC_DpOSD_ /.../gcc-4.8.2/include/c++/4.8.2/bits/alloc_traits.h:254
    #7 0x40253a in _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI1XSaIS1_ELN9__gnu_cxx12_Lock_policyE2EEEE9constructIS5_IKS2_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS6_PT_DpOSA_ /.../gcc-4.8.2/include/c++/4.8.2/bits/alloc_traits.h:393
    #8 0x40249b in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<X, std::allocator<X>>(std::_Sp_make_shared_tag, X*, std::allocator<X> const&) /.../gcc-4.8.2/include/c++/4.8.2/bits/shared_ptr_base.h:502
    #9 0x4023b1 in std::__shared_ptr<X, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<X>>(std::_Sp_make_shared_tag, std::allocator<X> const&) /.../gcc-4.8.2/include/c++/4.8.2/bits/shared_ptr_base.h:957
    #10 0x402375 in std::shared_ptr<X>::shared_ptr<std::allocator<X>>(std::_Sp_make_shared_tag, std::allocator<X> const&) /.../gcc-4.8.2/include/c++/4.8.2/bits/shared_ptr.h:316
    #11 0x4022c4 in std::shared_ptr<X> std::allocate_shared<X, std::allocator<X>>(std::allocator<X> const&) /.../gcc-4.8.2/include/c++/4.8.2/bits/shared_ptr.h:598
    #12 0x402241 in _ZSt11make_sharedI1XIEESt10shared_ptrIT_EDpOT0_ /.../gcc-4.8.2/include/c++/4.8.2/bits/shared_ptr.h:614
    #13 0x4021cf in foo() /.../foo.cxx:4
    #14 0x400fd0 in main /.../main.cxx:7
    #15 0x7ffff6208b34 in __libc_start_main (/lib64/libc.so.6+0x21b34)
    #16 0x400ef8  (/.../a.out+0x400ef8)
Run Code Online (Sandbox Code Playgroud)

对于gcc 7和gcc 8也是如此,但对于gcc 5.4则不然.这是具体的std::make_shared.这里发生了什么?我找不到任何有关ABI中断的信息,也不了解哪种更改可能会破坏这个例子.

Jon*_*ely 4

GCC 4.8 中的 C++11 支持仍处于实验阶段,因此编译的两个对象-std=c++11只有在均由 GCC 4.8.x 编译的情况下才能链接在一起

有关更完整的说明,请参阅/sf/answers/3438393171/ 。