https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68200
Bug ID: 68200 Summary: g++ 5.2 optimizes out pointer assignment in libstdc++ mt_allocator freelist destructor, causing crash at global-dtor time Product: gcc Version: 5.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: oremanj at mit dot edu Target Milestone: --- PR 52064 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52604) added a guard assignment to libstdc++-v3/src/c++98/mt_allocator.cc (_M_thread_freelist = 0), so that attempts to use the freelist after it had been destroyed would not access invalid memory. This resolved issues with global-scope STL containers that used mt_allocator being destroyed after the mt_allocator freelist was destroyed. ~__freelist() { if (_M_thread_freelist_array) { __gthread_key_delete(_M_key); ::operator delete(static_cast<void*>(_M_thread_freelist_array)); _M_thread_freelist = 0; } } The setting of the _M_thread_freelist member can validly be optimized away, because accessing the object after its destructor has run is undefined behavior, and the _M_thread_freelist assignment can't be detected other than by doing that. Since PR 52064 was resolved in 2012, it appears that gcc has become smarter about optimizing this case, and the guard assignment no longer is compiled. I get this assembly: 0x7ffff055d4f0 <(anonymous namespace)::__freelist::~__freelist()>: + 0: cmpq $0x0,0x8(%rdi) + 5: je 0x7ffff055d510 <(anonymous namespace)::__freelist::~__freelist()+32> + 7: push %rbx + 8: mov %rdi,%rbx +11: mov 0x18(%rdi),%edi +14: callq 0x7ffff054d670 <pthread_key_delete@plt> +19: mov 0x8(%rbx),%rdi +23: pop %rbx +24: jmpq 0x7ffff054b9f0 <_ZdlPv@plt> +29: nopl (%rax) +32: repz retq It does seem like the existing code is taking advantage of undefined behavior, so maybe a larger change is in order, but changing the assignment to happen through a volatile pointer - const_cast<_Thread_record *volatile &>(_M_thread_freelist) = 0 - resolves the issue.