Consider the following minimal example, consisting of three files:
foo.h
:
#pragma once
#include <memory>
struct X {
uint64_t i = 0xdeadbeefdeadbeefULL;
};
void foo();
foo.cxx
:
#include "foo.h"
void foo() {
std::make_shared<X>();
}
main.cxx
:
#include <memory>
#include "foo.h"
template std::shared_ptr<X> std::make_shared();
int main() {
foo();
}
And then compile the two translation units with different versions of 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
Note that I'm specifically compiling with the old ABI.
Running the resulting executable dies (it would not if both TUs were compiled with the same version of 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)
This is true for gcc 7 and gcc 8 as well, but not true for gcc 5.4. This is specific to std::make_shared
. What is going on here? I can't find any information about an ABI break, nor do I understand what kind of change could break this example.
C++11 support in GCC 4.8 was still experimental, so two objects compiled with -std=c++11
can only be linked together if they are both compiled by GCC 4.8.x
See https://stackoverflow.com/a/49119902/981959 for a more complete explanation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With