|
| 1 | +From 74560f26f75dda4257dce541ca362a1e763b2971 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Ryan Ofsky < [email protected]> |
| 3 | +Date: Thu, 6 Feb 2025 08:39:05 -0500 |
| 4 | +Subject: [PATCH 1/1] Avoid gcc/clang ABI incompatibility caused by |
| 5 | + PlacementNew |
| 6 | + |
| 7 | +GCC and clang do not use same calling convention for passing empty struct |
| 8 | +parameters. There is more information about this in |
| 9 | +https://itanium-cxx-abi.github.io/cxx-abi/cxx-abi-dev/archives/2015-December/002869.html |
| 10 | + |
| 11 | +Unfortunately this can create an issue in capnproto if it is built without |
| 12 | +optimizations in GCC, and the resulting static libraries are used in a clang |
| 13 | +program, or vice versa. |
| 14 | + |
| 15 | +Depending on what order libraries are specified on the linker command line, and |
| 16 | +whether code compiled with the other compiler is calling any header functions |
| 17 | +that cause weak a `operator new(unsigned int, kj::_::PlacementNew, void*)` |
| 18 | +symbol to be defined in its own objects, this can cause the linker to link a |
| 19 | +GCC-generated `kj::ctor` with a clang-generated `operator new`, and the |
| 20 | +resulting program to crash due to the compilers using different calling |
| 21 | +conventions for `operator new`. |
| 22 | + |
| 23 | +This problem is difficult to avoid in general, but pretty easy to avoid here by |
| 24 | +changing `operator new` parameter order so the empty struct parameter is last. |
| 25 | + |
| 26 | +This change should be beneficial for capnproto users that may be compiling it |
| 27 | +without optimizations, and not necessarily using a single compiler to build all |
| 28 | +their dependencies. |
| 29 | + |
| 30 | +The problem does not occur if any optimizations are enabled because `operator |
| 31 | +new` calls are inlined in that case. |
| 32 | +--- |
| 33 | + c++/src/kj/common.h | 11 +++++++---- |
| 34 | + 1 file changed, 7 insertions(+), 4 deletions(-) |
| 35 | + |
| 36 | +diff --git a/c++/src/kj/common.h b/c++/src/kj/common.h |
| 37 | +index b8edde3c..28ab11d6 100644 |
| 38 | +--- a/c++/src/kj/common.h |
| 39 | ++++ b/c++/src/kj/common.h |
| 40 | +@@ -1034,24 +1034,27 @@ private: |
| 41 | + |
| 42 | + // We want placement new, but we don't want to #include <new>. operator new cannot be defined in |
| 43 | + // a namespace, and defining it globally conflicts with the definition in <new>. So we have to |
| 44 | +-// define a dummy type and an operator new that uses it. |
| 45 | ++// define a dummy type and an operator new that uses it. The dummy type is intentionally passed |
| 46 | ++// as the last parameter so clang and GCC ABI calling conventions for empty struct struct parameters |
| 47 | ++// are compatible, and there are not segfaults trying to call clang operator new/delete from GCC or |
| 48 | ++// vice versa. |
| 49 | + |
| 50 | + namespace _ { // private |
| 51 | + struct PlacementNew {}; |
| 52 | + } // namespace _ (private) |
| 53 | + } // namespace kj |
| 54 | + |
| 55 | +-inline void* operator new(size_t, kj::_::PlacementNew, void* __p) noexcept { |
| 56 | ++inline void* operator new(size_t, void* __p, kj::_::PlacementNew) noexcept { |
| 57 | + return __p; |
| 58 | + } |
| 59 | + |
| 60 | +-inline void operator delete(void*, kj::_::PlacementNew, void* __p) noexcept {} |
| 61 | ++inline void operator delete(void*, void* __p, kj::_::PlacementNew) noexcept {} |
| 62 | + |
| 63 | + namespace kj { |
| 64 | + |
| 65 | + template <typename T, typename... Params> |
| 66 | + inline void ctor(T& location, Params&&... params) { |
| 67 | +- new (_::PlacementNew(), &location) T(kj::fwd<Params>(params)...); |
| 68 | ++ new (&location, _::PlacementNew()) T(kj::fwd<Params>(params)...); |
| 69 | + } |
| 70 | + |
| 71 | + template <typename T> |
0 commit comments