From 74560f26f75dda4257dce541ca362a1e763b2971 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 6 Feb 2025 08:39:05 -0500 Subject: [PATCH 1/1] Avoid gcc/clang ABI incompatibility caused by PlacementNew GCC and clang do not use same calling convention for passing empty struct parameters. There is more information about this in https://itanium-cxx-abi.github.io/cxx-abi/cxx-abi-dev/archives/2015-December/002869.html Unfortunately this can create an issue in capnproto if it is built without optimizations in GCC, and the resulting static libraries are used in a clang program, or vice versa. Depending on what order libraries are specified on the linker command line, and whether code compiled with the other compiler is calling any header functions that cause weak a `operator new(unsigned int, kj::_::PlacementNew, void*)` symbol to be defined in its own objects, this can cause the linker to link a GCC-generated `kj::ctor` with a clang-generated `operator new`, and the resulting program to crash due to the compilers using different calling conventions for `operator new`. This problem is difficult to avoid in general, but pretty easy to avoid here by changing `operator new` parameter order so the empty struct parameter is last. This change should be beneficial for capnproto users that may be compiling it without optimizations, and not necessarily using a single compiler to build all their dependencies. The problem does not occur if any optimizations are enabled because `operator new` calls are inlined in that case. --- c++/src/kj/common.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/c++/src/kj/common.h b/c++/src/kj/common.h index b8edde3c..28ab11d6 100644 --- a/c++/src/kj/common.h +++ b/c++/src/kj/common.h @@ -1034,24 +1034,27 @@ private: // We want placement new, but we don't want to #include . operator new cannot be defined in // a namespace, and defining it globally conflicts with the definition in . So we have to -// define a dummy type and an operator new that uses it. +// define a dummy type and an operator new that uses it. The dummy type is intentionally passed +// as the last parameter so clang and GCC ABI calling conventions for empty struct struct parameters +// are compatible, and there are not segfaults trying to call clang operator new/delete from GCC or +// vice versa. namespace _ { // private struct PlacementNew {}; } // namespace _ (private) } // namespace kj -inline void* operator new(size_t, kj::_::PlacementNew, void* __p) noexcept { +inline void* operator new(size_t, void* __p, kj::_::PlacementNew) noexcept { return __p; } -inline void operator delete(void*, kj::_::PlacementNew, void* __p) noexcept {} +inline void operator delete(void*, void* __p, kj::_::PlacementNew) noexcept {} namespace kj { template inline void ctor(T& location, Params&&... params) { - new (_::PlacementNew(), &location) T(kj::fwd(params)...); + new (&location, _::PlacementNew()) T(kj::fwd(params)...); } template