test: 32-bit Clang `ipc_test` failure at `-O0` #31772

issue fanquake opened this issue on January 31, 2025
  1. fanquake commented at 3:12 PM on January 31, 2025: member

    Noticed as part of this branch #29796, however it can also be reproduced with master (8fa10edcd1706a1f0dc9d8c3adbc8efa3c7755bf) by reproducing the equivalent CI & setting C(XX)FLAGS to -O0:

    make -C depends/ MULTIPROCESS=1 NO_QT=1 NO_WALLET=1 NO_ZMQ=1 NO_USDT=1 CFLAGS="-O0" CXXFLAGS="-O0" DEBUG=1 -j19 HOST=i686-pc-linux-gnu
    cmake -B build --toolchain /root/ci_scratch/depends/i686-pc-linux-gnu/toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER='clang;-m32' -DCMAKE_CXX_COMPILER='clang++;-m32'
    cmake --build build -j18
    
    ./build/src/test/test_bitcoin --run_test=ipc*
    Running 2 test cases...
    terminate called after throwing an instance of 'kj::ExceptionImpl'
      what():  /root/ci_scratch/depends/i686-pc-linux-gnu/include/kj/common.h:1797: failed: expected start <= end && end <= size_; Out-of-bounds ArrayPtr::slice().
    stack: 5ca0dd6d 5c78b5db 5c8cc599 5ca0adc9 5c94fb37 5c950070 5c9cd709 5c9cf58a 5c9d72fd 5c9d025e 5c776f11 5bf6351d 5bf6347d 5bf633cd 5bf6337b 5bf6331d 5bf63194 f7afa4b0 f773dff6 f77d55b7
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
        ??:0: returning here
    unknown location(0): fatal error: in "ipc_tests/ipc_tests": signal: SIGABRT (application abort requested)
    test/ipc_tests.cpp(12): last checkpoint: "ipc_tests" test entry
    test_bitcoin: common/args.cpp:578: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed.
    unknown location(0): fatal error: in "ipc_tests/parse_address_test": signal: SIGABRT (application abort requested)
    test/ipc_tests.cpp(20): last checkpoint: "parse_address_test" fixture ctor
    
    *** 2 failures are detected in the test module "Bitcoin Core Test Suite"
    
  2. fanquake added the label Tests on Jan 31, 2025
  3. bitcoin deleted a comment on Feb 3, 2025
  4. fanquake commented at 5:58 PM on February 3, 2025: member
  5. ryanofsky commented at 2:21 AM on February 4, 2025: contributor

    I tried those steps except I left off the -DCMAKE_C_COMPILER='clang;-m32' -DCMAKE_CXX_COMPILER='clang++;-m32' part and the test passed without a problem.

    Is the clang part important? It seems odd to use gcc for the depends build and clang for the bitcoin build, and when when I tried the adding the clang arguments specified, cmake complained about not being able to find libstdc++, which maybe makes sense because, I don't know if it is supposed to work with the gnu library.

    If there are any easier steps to reproduce this maybe using docker, or just a CI run I can look at that would be helpful. I ran into countless problems just getting i686 default build to work at all on my machine, so it is hard to know what things may be particular to the configuration this is happening in.

  6. fanquake commented at 9:49 AM on February 4, 2025: member

    If there are any easier steps to reproduce this maybe using docker,

    Running time env -i HOME="$HOME" PATH="$PATH" USER="$USER" bash -c 'FILE_ENV="./ci/test/00_setup_env_i686_multiprocess.sh" ./ci/test_run_all.sh' with the branch from #29796.

    or just a CI run I can look at that would be helpful.

    https://github.com/bitcoin/bitcoin/pull/29796/checks?check_run_id=36485685971

  7. ryanofsky commented at 2:05 PM on February 4, 2025: contributor

    Thanks was able to get a stack trace by running cd /ci_container_base/ci/scratch/build-i686-pc-linux-gnu, gdb -ex run --args ./src/test/test_bitcoin -t ipc_tests, and bt in container:

    [#0](/bitcoin-bitcoin/0/)  0xf7f4b5b9 in __kernel_vsyscall ()
    [#1](/bitcoin-bitcoin/1/)  0xf79e6037 in ?? () from /lib32/libc.so.6
    [#2](/bitcoin-bitcoin/2/)  0xf7994c51 in raise () from /lib32/libc.so.6
    [#3](/bitcoin-bitcoin/3/)  0xf797c2b7 in abort () from /lib32/libc.so.6
    [#4](/bitcoin-bitcoin/4/)  0xf7d4bf71 in ?? () from /lib32/libstdc++.so.6
    [#5](/bitcoin-bitcoin/5/)  0xf7d65a97 in ?? () from /lib32/libstdc++.so.6
    [#6](/bitcoin-bitcoin/6/)  0xf7d4b8f9 in std::terminate() () from /lib32/libstdc++.so.6
    [#7](/bitcoin-bitcoin/7/)  0xf7d65ddc in __cxa_throw () from /lib32/libstdc++.so.6
    [#8](/bitcoin-bitcoin/8/)  0x597d7e12 in kj::ExceptionCallback::RootExceptionCallback::onFatalException (this=0x5c8344b0, exception=...) at /usr/src/kj/exception.c++:1107
    [#9](/bitcoin-bitcoin/9/)  0x597d6085 in kj::throwFatalException (exception=..., ignoreCount=1) at /usr/src/kj/exception.c++:1194
    [#10](/bitcoin-bitcoin/10/) 0x597d05e5 in kj::_::Debug::Fault::fatal (this=0xf75cbc28) at /usr/src/kj/debug.c++:371
    [#11](/bitcoin-bitcoin/11/) 0x597cf19a in kj::_::inlineRequireFailure (file=0x59e9a314 "/ci_container_base/depends/i686-pc-linux-gnu/include/kj/common.h", line=1797, expectation=0x59e9a2f7 "start <= end && end <= size_", 
        macroArgs=0x59e9a2d4 "\"Out-of-bounds ArrayPtr::slice().\"", message=0x59e9a2b0 "Out-of-bounds ArrayPtr::slice().") at /usr/src/kj/common.c++:36
    [#12](/bitcoin-bitcoin/12/) 0x5954afc8 in kj::ArrayPtr<char const>::slice (this=0xf75cbd54, start=1498659230, end=13) at /usr/src/kj/common.h:1797
    [#13](/bitcoin-bitcoin/13/) 0x5968bd88 in kj::StringPtr::slice (this=0xf75cbd54, start=1498659230) at /usr/src/kj/string.h:734
    [#14](/bitcoin-bitcoin/14/) 0x597cc1e4 in kj::CidrRange::CidrRange (this=0x5a9ddc80 <kj::_::reservedCidrs()::result>, pattern=...) at /usr/src/kj/cidr.c++:53
    [#15](/bitcoin-bitcoin/15/) 0x597105fe in kj::_::reservedCidrs () at /usr/src/kj/async-io.c++:3007
    [#16](/bitcoin-bitcoin/16/) 0x59710b37 in kj::_::NetworkFilter::NetworkFilter (this=0x5c838b70) at /usr/src/kj/async-io.c++:3038
    [#17](/bitcoin-bitcoin/17/) 0x5978e80c in kj::(anonymous namespace)::SocketNetwork::SocketNetwork (this=0x5c838b68, lowLevel=...) at /usr/src/kj/async-io-unix.c++:1742
    [#18](/bitcoin-bitcoin/18/) 0x5979068d in kj::(anonymous namespace)::AsyncIoProviderImpl::AsyncIoProviderImpl (this=0x5c838b60, lowLevel=...) at /usr/src/kj/async-io-unix.c++:1974
    [#19](/bitcoin-bitcoin/19/) 0x59798404 in kj::heap<kj::(anonymous namespace)::AsyncIoProviderImpl, kj::(anonymous namespace)::LowLevelAsyncIoProviderImpl&> () at /usr/src/kj/memory.h:609
    [#20](/bitcoin-bitcoin/20/) 0x59791361 in kj::setupAsyncIo () at /usr/src/kj/async-io-unix.c++:2058
    [#21](/bitcoin-bitcoin/21/) 0x595373c2 in mp::EventLoop::EventLoop(char const*, std::function<void (bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, void*) (this=0xf75cc110, 
        exe_name=0x59b257f6 "IpcPipeTest", log_fn=..., context=0x0) at /usr/src/mp/proxy.cpp:158
    [#22](/bitcoin-bitcoin/22/) 0x58a4064e in IpcPipeTest()::$_0::operator()() const (this=0x5c8542d4) at ./test/ipc_test.cpp:60
    [#23](/bitcoin-bitcoin/23/) 0x58a405ae in std::__invoke_impl<void, IpcPipeTest()::$_0>(std::__invoke_other, IpcPipeTest()::$_0&&) (__f=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61
    [#24](/bitcoin-bitcoin/24/) 0x58a404fe in std::__invoke<IpcPipeTest()::$_0>(IpcPipeTest()::$_0&&) (__fn=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96
    [#25](/bitcoin-bitcoin/25/) 0x58a404ac in std::thread::_Invoker<std::tuple<IpcPipeTest()::$_0> >::_M_invoke<0u>(std::_Index_tuple<0u>) (this=0x5c8542d4)
        at /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292
    [#26](/bitcoin-bitcoin/26/) 0x58a4044e in std::thread::_Invoker<std::tuple<IpcPipeTest()::$_0> >::operator()() (this=0x5c8542d4) at /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299
    [#27](/bitcoin-bitcoin/27/) 0x58a402c5 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<IpcPipeTest()::$_0> > >::_M_run() (this=0x5c8542d0)
        at /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244
    [#28](/bitcoin-bitcoin/28/) 0xf7d98d21 in ?? () from /lib32/libstdc++.so.6
    [#29](/bitcoin-bitcoin/29/) 0xf79e4157 in ?? () from /lib32/libc.so.6
    [#30](/bitcoin-bitcoin/30/) 0xf7a78e08 in ?? () from /lib32/libc.so.6
    
  8. ryanofsky commented at 2:21 PM on February 4, 2025: contributor

    Stack trace seems to be showing something going wrong in capnproto code, but unclear what the cause is. The crash is happening in the CidrRange::CidrRange constructor here:

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/cidr.c%2B%2B#L53

    which is being called on a static list of address patterns:

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/async-io.c%2B%2B#L2999-L3007

    All of the patterns look valid so no reason there should be a parsing exception like seems to be happening. It seems like this might be a more strange compiler / build issue

  9. ryanofsky commented at 2:29 PM on February 4, 2025: contributor

    gdb shows invalid results being returned from pattern.findFirst('/') call where pattern is 192.0.0.0/24

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/cidr.c%2B%2B#L51C48-L51C57

    [#14](/bitcoin-bitcoin/14/) 0x5971f1e4 in kj::CidrRange::CidrRange (this=0x5a930c80 <kj::_::reservedCidrs()::result>, pattern=...) at /usr/src/kj/cidr.c++:53
    (gdb) p pattern
    $6 = {content = {<kj::DisallowConstCopyIfNotConst<char const>> = {<No data fields>}, ptr = 0x59e13a45 "192.0.0.0/24", size_ = 13}}
    (gdb) p slashPos
    $7 = 1497950621
    (gdb) p pattern.findFirst('/')
    $8 = {ptr = {isSet = true, {value = 2155537998}}}
    
  10. ryanofsky commented at 7:48 PM on February 4, 2025: contributor

    I thought I tracked down the problem, and some change I made to the test caused it to pass, but rerunning the the fix in a new container, it no longer works, so I have to go back.

    Here were notes I was about to post about potential problem & fix. In any case I suspect problem would be avoided if we just consistently used gcc or clang for this build and didn't try to mix them.


    Test failure seems to be caused by the container using incompatible ABI versions for the depends build and the main build. The depends build is using gcc with ABI version 18:

    gcc -E -x c++ - -dM <<< "" | grep ABI
    #define __GXX_ABI_VERSION 1018
    

    The bitcoin build is using clang with ABI version 2:

    clang++ -E -x c++ - -dM <<< "" | grep ABI
    #define __GXX_ABI_VERSION 1002
    

    In clang, the ABI version is determined when the compiler is built, but in gcc you can control it with the -fabi-version option, so the following change seems to fix the build:

    --- a/depends/hosts/linux.mk
    +++ b/depends/hosts/linux.mk
    @@ -1,5 +1,5 @@
     linux_CFLAGS=-pipe -std=$(C_STANDARD)
    -linux_CXXFLAGS=-pipe -std=$(CXX_STANDARD)
    +linux_CXXFLAGS=-pipe -std=$(CXX_STANDARD) -fabi-version=2
     
     ifneq ($(LTO),)
     linux_AR = $(host_toolchain)gcc-ar
    --- a/depends/packages/libmultiprocess.mk
    +++ b/depends/packages/libmultiprocess.mk
    @@ -27,3 +27,5 @@ endef
     define $(package)_stage_cmds
       $(MAKE) DESTDIR=$($(package)_staging_dir) install-lib
     endef
    +
    +$(package)_cxxflags += -fabi-version=11
    
    

    First part of the diff setting -fabi-version=2 in the depends build is the main fix. The second part of the diff setting -fabi-version=11 is a workaround for an issue that happened specifically with the multiprocess package. Because when -fabi-version is set to 10 or below there is a compile error:

    <details><summary>error: no matching function for call to ‘std::__uniq_ptr_data</summary> <p>

    [ 20%] Building CXX object CMakeFiles/mputil.dir/src/mp/util.cpp.o
    In file included from /usr/include/c++/13/bits/shared_ptr_base.h:59,
                     from /usr/include/c++/13/bits/shared_ptr.h:53,
                     from /usr/include/c++/13/condition_variable:45,
                     from /usr/include/c++/13/future:41,
                     from /ci_container_base/depends/work/build/i686-pc-linux-gnu/libmultiprocess/07c917f7ca910d66abc6d3873162fc9061704074-722fc6d9234/include/mp/util.h:11,
                     from /ci_container_base/depends/work/build/i686-pc-linux-gnu/libmultiprocess/07c917f7ca910d66abc6d3873162fc9061704074-722fc6d9234/src/mp/util.cpp:6:
    /usr/include/c++/13/bits/unique_ptr.h: In instantiation of ‘constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr() [with _Del = std::__future_base::_Result_base::_Deleter; <template-parameter-2-2> = void; _Tp = std::__future_base::_Result_base; _Dp = std::__future_base::_Result_base::_Deleter]’:
    /usr/include/c++/13/future:340:34:   required from here
    /usr/include/c++/13/bits/unique_ptr.h:305:11: error: no matching function for call to ‘std::__uniq_ptr_data<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter, true, true>::__uniq_ptr_data()’
      305 |         : _M_t()
          |           ^~~~~~
    /usr/include/c++/13/bits/unique_ptr.h:241:40: note: candidate: ‘template<class _Del> std::__uniq_ptr_data<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter, true, true>::__uniq_ptr_data(std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::pointer, _Del&&) [inherited from std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>]’
      241 |       using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
          |                                        ^~~~~~~~~~~~~~~
    /usr/include/c++/13/bits/unique_ptr.h:241:40: note:   template argument deduction/substitution failed:
    /usr/include/c++/13/bits/unique_ptr.h:305:11: note:   candidate expects 2 arguments, 0 provided
      305 |         : _M_t()
          |           ^~~~~~
    /usr/include/c++/13/bits/unique_ptr.h:241:40: note: candidate: ‘std::__uniq_ptr_data<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter, true, true>::__uniq_ptr_data(std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::pointer) [inherited from std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>]’
      241 |       using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
          |                                        ^~~~~~~~~~~~~~~
    /usr/include/c++/13/bits/unique_ptr.h:241:40: note:   candidate expects 1 argument, 0 provided
    /usr/include/c++/13/bits/unique_ptr.h:242:7: note: candidate: ‘std::__uniq_ptr_data<_Tp, _Dp, <anonymous>, <anonymous> >::__uniq_ptr_data(std::__uniq_ptr_data<_Tp, _Dp, <anonymous>, <anonymous> >&&) [with _Tp = std::__future_base::_Result_base; _Dp = std::__future_base::_Result_base::_Deleter; bool <anonymous> = true; bool <anonymous> = true]’
      242 |       __uniq_ptr_data(__uniq_ptr_data&&) = default;
          |       ^~~~~~~~~~~~~~~
    /usr/include/c++/13/bits/unique_ptr.h:242:7: note:   candidate expects 1 argument, 0 provided
    make[4]: *** [CMakeFiles/mputil.dir/build.make:76: CMakeFiles/mputil.dir/src/mp/util.cpp.o] Error 1
    

    </p> </details>

  11. maflcko commented at 8:28 PM on February 4, 2025: member

    Interesting that this did not result in a compile or link failure instead.

    I switched to clang in commit fad0f21c3caba129106799fe6c14aff323ef99f2 to avoid OOM with g++, (which was before switching to 32-bit in commit fae0295a799499268caca9c385ac4d7061543980), but now that the CI machines have more memory, it should be fine if this CI task takes more memory and time.

    Happy to review a pull, if someone submits one.

  12. ryanofsky commented at 9:28 PM on February 4, 2025: contributor

    Can confirm switching from clang to gcc does seem to fix this. -Wno-error=documentation also had to be dropped because gcc does not support it.

    --- a/ci/test/00_setup_env_i686_multiprocess.sh
    +++ b/ci/test/00_setup_env_i686_multiprocess.sh
    @@ -10,15 +10,12 @@ export HOST=i686-pc-linux-gnu
     export CONTAINER_NAME=ci_i686_multiprocess
     export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
     export CI_IMAGE_PLATFORM="linux/amd64"
    -export PACKAGES="llvm clang g++-multilib"
    -export DEP_OPTS="DEBUG=1 MULTIPROCESS=1"
    +export PACKAGES="g++-multilib"
    +export DEP_OPTS="DEBUG=1 MULTIPROCESS=1 NO_QT=1"
     export GOAL="install"
     export TEST_RUNNER_EXTRA="--v2transport"
     export BITCOIN_CONFIG="\
      -DCMAKE_BUILD_TYPE=Debug \
    - -DCMAKE_C_COMPILER='clang;-m32' \
    - -DCMAKE_CXX_COMPILER='clang++;-m32' \
    - -DCMAKE_CXX_FLAGS='-Wno-error=documentation' \
      -DAPPEND_CPPFLAGS='-DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE' \
     "
     export BITCOIND=bitcoin-node  # Used in functional tests
    

    Interesting that this did not result in a compile or link failure instead.

    I was just seeing a lot of strange things here. Theoretically, I think it should be fine to use gcc and clang together. Some other things I noticed: when I ran gdb I could step into the findFirst methods and see it looking for the '/' character and pos pointing to the right place but then the return value was wrong (a very large number instead of the position of the character). Also in I couldn't even tell which findFirst overload I was in because line number did not seem to correspond to the source file.

    When I added the -fabi-version flags and rebuilt I saw the ipc tests run and passed, and the -fabi-version fix worked twice in two different containers when I applied it manually, but did not seem to work when it was already applied at the beginning of the build. So I think something else I was doing in my rebuild steps, like reconfiguring cmake was causing the problem to go away. So I don't know. I want to debug more but I think I already spent way too much time on this.

  13. ryanofsky commented at 12:04 AM on February 5, 2025: contributor

    I tried to repeat previous steps rebuilding depends and test_bitcoin in container to figure out what I was doing that caused the test to stop crashing, but it seems to crash reliably, so I can't work out what I other changes I might have made while trying -fabi-version flags in #31772 (comment) that would have caused it not to crash.

    Additionally, I went back to original build and debugged it with GDB, and could easily step through and see where the bug is happening when I run with:

    gdb -ex 'b findFirst' -ex run --args ./src/test/test_bitcoin -t ipc_tests
    

    Everything works well up until reaching the ArrayPtr<const char>::findFirst method:

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/common.h#L1882-L1890

    The method also seems to work fine until it reaches the return pos - ptr; line. Because the function returns a Maybe value, this calls Maybe constructor:

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/common.h#L1389

    which has a NullableValue member and calls NullableValue constructor:

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/common.h#L1158

    which calls a function called ctor:

    https://github.com/capnproto/capnproto/blob/b34ec28cceaf15b1082b74b50f03f770873c3636/c%2B%2B/src/kj/common.h#L1061

    and unfortunately that function seems to be completely broken and not do anything. The ctor function is supposed to construct a T value inside the NullableValue<T> object, but it fails to do that. In this case T is a size_t object so it is supposed to assign the pos - ptr size that was computed into the Maybe<size_t> object that is being returned. But it doesn't do this. So the Maybe<size_t> object is only half-initialized as {isSet = true, {value = 1497966205}}, where the value 1497966205 is just the preexisting value it held before it was constructed, and is much longer than the size of the string being searched, so the code later throws an exception when there is an attempt to slice the "192.0.0.0/24" string at that position.

    This bug seems like it is is probably a compiler bug, but not one is necessarily going to happen reliably because if the unitialized memory location started of as 0 instead 1497966205, crash would not happen. So I'm not sure switching bitcoin compiler from clang to gcc really fixes the problem, or just makes it appear not to happen. And I"m not sure what I was doing before that caused the problem to disappear as well. It seems like there might be just be a problem with this version of gcc and -O0 and this piece of code.

    Next steps might be to look at generated assembly and confirm compiler is really producing buggy code or try to reproduce a minimal test case. Another thing we could try to do is update to a new version of gcc. Version here seems to be gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

    Here is disassembly of relevant functions:

    <details><summary>disassembly</summary> <p>

    (gdb) disassemble /s
    Dump of assembler code for function _ZNK2kj8ArrayPtrIKcE9findFirstERS1_:
    /usr/src/kj/common.h:
    1883    inline Maybe<size_t> ArrayPtr<const char>::findFirst(const char& c) const {
       0x5977156c <+0>:     push   %ebp
       0x5977156d <+1>:     mov    %esp,%ebp
       0x5977156f <+3>:     push   %ebx
       0x59771570 <+4>:     sub    $0x24,%esp
       0x59771573 <+7>:     call   0x566e8da0 <__x86.get_pc_thunk.bx>
       0x59771578 <+12>:    add    $0x121eff0,%ebx
       0x5977157e <+18>:    mov    0x8(%ebp),%eax
       0x59771581 <+21>:    mov    %eax,-0x1c(%ebp)
       0x59771584 <+24>:    mov    0xc(%ebp),%eax
       0x59771587 <+27>:    mov    %eax,-0x20(%ebp)
       0x5977158a <+30>:    mov    0x10(%ebp),%eax
       0x5977158d <+33>:    mov    %eax,-0x24(%ebp)
    => 0x59771590 <+36>:    mov    %gs:0x14,%eax
       0x59771596 <+42>:    mov    %eax,-0xc(%ebp)
       0x59771599 <+45>:    xor    %eax,%eax
    
    1884      const char* pos = reinterpret_cast<const char*>(memchr(ptr, c, size_));
       0x5977159b <+47>:    mov    -0x20(%ebp),%eax
       0x5977159e <+50>:    mov    0x4(%eax),%ecx
       0x597715a1 <+53>:    mov    -0x24(%ebp),%eax
       0x597715a4 <+56>:    movzbl (%eax),%eax
       0x597715a7 <+59>:    movsbl %al,%edx
       0x597715aa <+62>:    mov    -0x20(%ebp),%eax
       0x597715ad <+65>:    mov    (%eax),%eax
       0x597715af <+67>:    sub    $0x4,%esp
       0x597715b2 <+70>:    push   %ecx
       0x597715b3 <+71>:    push   %edx
       0x597715b4 <+72>:    push   %eax
       0x597715b5 <+73>:    call   0x5664a880 <memchr@plt>
       0x597715ba <+78>:    add    $0x10,%esp
       0x597715bd <+81>:    mov    %eax,-0x10(%ebp)
    
    1885      if (pos == nullptr) {
       0x597715c0 <+84>:    cmpl   $0x0,-0x10(%ebp)
       0x597715c4 <+88>:    jne    0x597715d8 <_ZNK2kj8ArrayPtrIKcE9findFirstERS1_+108>
    
    1886        return nullptr;
       0x597715c6 <+90>:    sub    $0x8,%esp
       0x597715c9 <+93>:    push   $0x0
       0x597715cb <+95>:    push   -0x1c(%ebp)
       0x597715ce <+98>:    call   0x595224a6 <_ZN2kj5MaybeIjEC2EDn>
       0x597715d3 <+103>:   add    $0x10,%esp
       0x597715d6 <+106>:   jmp    0x597715f9 <_ZNK2kj8ArrayPtrIKcE9findFirstERS1_+141>
    
    1887      } else {
    1888        return pos - ptr;
       0x597715d8 <+108>:   mov    -0x20(%ebp),%eax
       0x597715db <+111>:   mov    (%eax),%eax
       0x597715dd <+113>:   mov    -0x10(%ebp),%edx
       0x597715e0 <+116>:   sub    %eax,%edx
       0x597715e2 <+118>:   mov    %edx,%eax
       0x597715e4 <+120>:   mov    %eax,-0x14(%ebp)
       0x597715e7 <+123>:   sub    $0x8,%esp
       0x597715ea <+126>:   lea    -0x14(%ebp),%eax
       0x597715ed <+129>:   push   %eax
       0x597715ee <+130>:   push   -0x1c(%ebp)
       0x597715f1 <+133>:   call   0x595224d0 <_ZN2kj5MaybeIjEC2EOj>
       0x597715f6 <+138>:   add    $0x10,%esp
    
    1889      }
    1890    }
       0x597715f9 <+141>:   mov    -0xc(%ebp),%eax
       0x597715fc <+144>:   sub    %gs:0x14,%eax
       0x59771603 <+151>:   je     0x5977160a <_ZNK2kj8ArrayPtrIKcE9findFirstERS1_+158>
       0x59771605 <+153>:   call   0x597cf290 <__stack_chk_fail_local>
       0x5977160a <+158>:   mov    -0x1c(%ebp),%eax
       0x5977160d <+161>:   mov    -0x4(%ebp),%ebx
       0x59771610 <+164>:   leave
       0x59771611 <+165>:   ret    $0x4
    End of assembler dump.
    (gdb) disassemble /s _ZN2kj5MaybeIjEC2EOj
    Dump of assembler code for function _ZN2kj5MaybeIjEC2EOj:
    /usr/src/kj/common.h:
    1389      Maybe(T&& t): ptr(kj::mv(t)) {}
       0x595224d0 <+0>:     push   %ebp
       0x595224d1 <+1>:     mov    %esp,%ebp
       0x595224d3 <+3>:     push   %esi
       0x595224d4 <+4>:     push   %ebx
       0x595224d5 <+5>:     call   0x566e8da0 <__x86.get_pc_thunk.bx>
       0x595224da <+10>:    add    $0x146e08e,%ebx
       0x595224e0 <+16>:    mov    0x8(%ebp),%esi
       0x595224e3 <+19>:    sub    $0xc,%esp
       0x595224e6 <+22>:    push   0xc(%ebp)
       0x595224e9 <+25>:    call   0x59502e4c <_ZN2kj2mvIjEEOT_RS1_>
       0x595224ee <+30>:    add    $0x10,%esp
       0x595224f1 <+33>:    sub    $0x8,%esp
       0x595224f4 <+36>:    push   %eax
       0x595224f5 <+37>:    push   %esi
       0x595224f6 <+38>:    call   0x5952b7d8 <_ZN2kj1_13NullableValueIjEC2EOj>
       0x595224fb <+43>:    add    $0x10,%esp
       0x595224fe <+46>:    nop
       0x595224ff <+47>:    lea    -0x8(%ebp),%esp
       0x59522502 <+50>:    pop    %ebx
       0x59522503 <+51>:    pop    %esi
       0x59522504 <+52>:    pop    %ebp
       0x59522505 <+53>:    ret
    End of assembler dump.
    (gdb) disassemble /s _ZN2kj1_13NullableValueIjEC2EOj
    Dump of assembler code for function _ZN2kj1_13NullableValueIjEC2EOj:
    /usr/src/kj/common.h:
    1156      inline NullableValue(T&& t)
       0x5952b7d8 <+0>:     push   %ebp
       0x5952b7d9 <+1>:     mov    %esp,%ebp
       0x5952b7db <+3>:     push   %ebx
       0x5952b7dc <+4>:     sub    $0x4,%esp
       0x5952b7df <+7>:     call   0x566e8da0 <__x86.get_pc_thunk.bx>
       0x5952b7e4 <+12>:    add    $0x1464d84,%ebx
    
    1157          : isSet(true) {
       0x5952b7ea <+18>:    mov    0x8(%ebp),%eax
       0x5952b7ed <+21>:    movb   $0x1,(%eax)
    
    1158        ctor(value, kj::mv(t));
       0x5952b7f0 <+24>:    sub    $0xc,%esp
       0x5952b7f3 <+27>:    push   0xc(%ebp)
       0x5952b7f6 <+30>:    call   0x59502e4c <_ZN2kj2mvIjEEOT_RS1_>
       0x5952b7fb <+35>:    add    $0x10,%esp
       0x5952b7fe <+38>:    mov    0x8(%ebp),%edx
       0x5952b801 <+41>:    add    $0x4,%edx
       0x5952b804 <+44>:    sub    $0x8,%esp
       0x5952b807 <+47>:    push   %eax
       0x5952b808 <+48>:    push   %edx
       0x5952b809 <+49>:    call   0x595314f8 <_ZN2kj4ctorIjIjEEEvRT_DpOT0_>
       0x5952b80e <+54>:    add    $0x10,%esp
    
    1159      }
       0x5952b811 <+57>:    nop
       0x5952b812 <+58>:    mov    -0x4(%ebp),%ebx
       0x5952b815 <+61>:    leave
       0x5952b816 <+62>:    ret
    End of assembler dump.
    (gdb) disassemble /s _ZN2kj4ctorIjIjEEEvRT_DpOT0_
    
    Dump of assembler code for function _ZN2kj4ctorIjIjEEEvRT_DpOT0_:
    /usr/src/kj/common.h:
    1060    inline void ctor(T& location, Params&&... params) {
       0x595314f8 <+0>:     push   %ebp
       0x595314f9 <+1>:     mov    %esp,%ebp
       0x595314fb <+3>:     push   %esi
       0x595314fc <+4>:     push   %ebx
       0x595314fd <+5>:     call   0x566e8da0 <__x86.get_pc_thunk.bx>
       0x59531502 <+10>:    add    $0x145f066,%ebx
    
    1061      new (_::PlacementNew(), &location) T(kj::fwd<Params>(params)...);
       0x59531508 <+16>:    sub    $0x4,%esp
       0x5953150b <+19>:    push   0x8(%ebp)
       0x5953150e <+22>:    push   %eax
       0x5953150f <+23>:    push   $0x4
       0x59531511 <+25>:    call   0x58a0ec00 <_ZnwjN2kj1_12PlacementNewEPv>
       0x59531516 <+30>:    add    $0x10,%esp
       0x59531519 <+33>:    mov    %eax,%esi
       0x5953151b <+35>:    test   %esi,%esi
       0x5953151d <+37>:    je     0x59531531 <_ZN2kj4ctorIjIjEEEvRT_DpOT0_+57>
       0x5953151f <+39>:    sub    $0xc,%esp
       0x59531522 <+42>:    push   0xc(%ebp)
       0x59531525 <+45>:    call   0x59502e70 <_ZN2kj3fwdIjEEOT_RNS_8NoInfer_IS1_E4TypeE>
       0x5953152a <+50>:    add    $0x10,%esp
       0x5953152d <+53>:    mov    (%eax),%eax
       0x5953152f <+55>:    mov    %eax,(%esi)
    
    1062    }
       0x59531531 <+57>:    nop
       0x59531532 <+58>:    lea    -0x8(%ebp),%esp
       0x59531535 <+61>:    pop    %ebx
       0x59531536 <+62>:    pop    %esi
       0x59531537 <+63>:    pop    %ebp
       0x59531538 <+64>:    ret
    End of assembler dump.
    (gdb) 
    Dump of assembler code for function _ZN2kj4ctorIjIjEEEvRT_DpOT0_:
    /usr/src/kj/common.h:
    1060    inline void ctor(T& location, Params&&... params) {
       0x595314f8 <+0>:     push   %ebp
       0x595314f9 <+1>:     mov    %esp,%ebp
       0x595314fb <+3>:     push   %esi
       0x595314fc <+4>:     push   %ebx
       0x595314fd <+5>:     call   0x566e8da0 <__x86.get_pc_thunk.bx>
       0x59531502 <+10>:    add    $0x145f066,%ebx
    
    1061      new (_::PlacementNew(), &location) T(kj::fwd<Params>(params)...);
       0x59531508 <+16>:    sub    $0x4,%esp
       0x5953150b <+19>:    push   0x8(%ebp)
       0x5953150e <+22>:    push   %eax
       0x5953150f <+23>:    push   $0x4
       0x59531511 <+25>:    call   0x58a0ec00 <_ZnwjN2kj1_12PlacementNewEPv>
       0x59531516 <+30>:    add    $0x10,%esp
       0x59531519 <+33>:    mov    %eax,%esi
       0x5953151b <+35>:    test   %esi,%esi
       0x5953151d <+37>:    je     0x59531531 <_ZN2kj4ctorIjIjEEEvRT_DpOT0_+57>
       0x5953151f <+39>:    sub    $0xc,%esp
       0x59531522 <+42>:    push   0xc(%ebp)
       0x59531525 <+45>:    call   0x59502e70 <_ZN2kj3fwdIjEEOT_RNS_8NoInfer_IS1_E4TypeE>
       0x5953152a <+50>:    add    $0x10,%esp
       0x5953152d <+53>:    mov    (%eax),%eax
       0x5953152f <+55>:    mov    %eax,(%esi)
    
    1062    }
       0x59531531 <+57>:    nop
       0x59531532 <+58>:    lea    -0x8(%ebp),%esp
       0x59531535 <+61>:    pop    %ebx
       0x59531536 <+62>:    pop    %esi
       0x59531537 <+63>:    pop    %ebp
       0x59531538 <+64>:    ret
    End of assembler dump.
    (gdb) disassemble /s _ZnwjN2kj1_12PlacementNewEPv
    Dump of assembler code for function _ZnwjN2kj1_12PlacementNewEPv:
    /ci_container_base/depends/i686-pc-linux-gnu/include/kj/common.h:
    1051    inline void* operator new(size_t, kj::_::PlacementNew, void* __p) noexcept {
       0x58a0ec00 <+0>:     endbr32
       0x58a0ec04 <+4>:     push   %ebp
       0x58a0ec05 <+5>:     mov    %esp,%ebp
       0x58a0ec07 <+7>:     push   %ebx
       0x58a0ec08 <+8>:     sub    $0x14,%esp
       0x58a0ec0b <+11>:    call   0x58a0ec10 <_ZnwjN2kj1_12PlacementNewEPv+16>
       0x58a0ec10 <+16>:    pop    %eax
       0x58a0ec11 <+17>:    add    $0x1f81958,%eax
       0x58a0ec17 <+23>:    mov    %eax,-0x14(%ebp)
       0x58a0ec1a <+26>:    mov    0xc(%ebp),%eax
       0x58a0ec1d <+29>:    mov    0x8(%ebp),%eax
       0x58a0ec20 <+32>:    mov    %gs:0x14,%eax
       0x58a0ec26 <+38>:    mov    %eax,-0x8(%ebp)
    
    1052      return __p;
       0x58a0ec29 <+41>:    mov    0xc(%ebp),%eax
       0x58a0ec2c <+44>:    mov    %eax,-0x10(%ebp)
       0x58a0ec2f <+47>:    mov    %gs:0x14,%eax
       0x58a0ec35 <+53>:    mov    -0x8(%ebp),%ecx
       0x58a0ec38 <+56>:    cmp    %ecx,%eax
       0x58a0ec3a <+58>:    jne    0x58a0ec49 <_ZnwjN2kj1_12PlacementNewEPv+73>
       0x58a0ec40 <+64>:    mov    -0x10(%ebp),%eax
       0x58a0ec43 <+67>:    add    $0x14,%esp
       0x58a0ec46 <+70>:    pop    %ebx
       0x58a0ec47 <+71>:    pop    %ebp
       0x58a0ec48 <+72>:    ret
       0x58a0ec49 <+73>:    mov    -0x14(%ebp),%ebx
       0x58a0ec4c <+76>:    call   0x56649250 <__stack_chk_fail@plt>
    End of assembler dump.
    (gdb) disassemble /s _ZN2kj3fwdIjEEOT_RNS_8NoInfer_IS1_E4TypeE
    Dump of assembler code for function _ZN2kj3fwdIjEEOT_RNS_8NoInfer_IS1_E4TypeE:
    /ci_container_base/depends/i686-pc-linux-gnu/include/kj/common.h:
    700     template<typename T> constexpr T&& fwd(NoInfer<T>& t) noexcept { return static_cast<T&&>(t); }
       0x59502e70 <+0>:     push   %ebp
       0x59502e71 <+1>:     mov    %esp,%ebp
       0x59502e73 <+3>:     call   0x584df28a <__x86.get_pc_thunk.ax>
       0x59502e78 <+8>:     add    $0x148d6f0,%eax
       0x59502e7d <+13>:    mov    0x8(%ebp),%eax
       0x59502e80 <+16>:    pop    %ebp
       0x59502e81 <+17>:    ret
    End of assembler dump.
    

    </p> </details>

    When I fed this to chatgpt (https://chatgpt.com/share/67a2aa92-e100-800a-b5b3-999982d1a648) it claimed to find a bug in the dissembly where operator new function is not interpreting its parameters correctly, and I could confirm this with gdb (at least to the best of my understanding, I am not that familiar with assembly and calling conventions). But gdb definitely showed operator new returning the wrong address (source address not destination address), which explained why the destination was not being updated and contained a garbage value.

    So I think there is pretty good evidence that this version of gcc contains a bug with -O0 and is miscompiling the code. Again I'm not sure if we care about this or not. It's an older version of gcc so might be logical to just update it. Or just not compile this code with -O0.

  14. maflcko commented at 9:45 AM on February 5, 2025: member

    This bug seems like it is is probably a compiler bug, but not one is necessarily going to happen reliably because if the unitialized memory location started of as 0 instead 1497966205, crash would not happen. So I'm not sure switching bitcoin compiler from clang to gcc really fixes the problem, or just makes it appear not to happen. And I"m not sure what I was doing before that caused the problem to disappear as well. It seems like there might be just be a problem with this version of gcc and -O0 and this piece of code.

    Next steps might be to look at generated assembly and confirm compiler is really producing buggy code or try to reproduce a minimal test case. Another thing we could try to do is update to a new version of gcc. Version here seems to be gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

    Interesting. It would be nice to reduce this, if someone wants to spend more time on this. However, I am not familiar with libmp and the build process around it, so if someone manages to produce one (or two) cpp files (even if large) with the corresponding compiler flags, I am happy to take it from there and minimize further.

  15. ryanofsky commented at 6:50 PM on February 5, 2025: contributor

    Interesting. It would be nice to reduce this

    So I reduced the failure down to a standalone case, but it could be reduced further. Now it is unclear to me if there is a compiler bug here or not, because this is caused by a very specific interaction between the two compiler versions we are using:

    • gcc version Ubuntu 13.3.0-6ubuntu2~24.04
    • clang version 18.1.3 (1ubuntu1)

    and maybe the specific flags we are using. The bug happens because the linker links together the

    • _ZN2kj4ctorIjIjEEEvRT_DpOT0_ function (void kj::ctor<unsigned int, unsigned int>(unsigned int&, unsigned int&&))

    generated by GCC in the capnproto static libraries

    • _ZnwjN2kj1_12PlacementNewEPv function (operator new(unsigned int, kj::_::PlacementNew, void*))

    generated by clang in the libbitcoin_ipc_test.a library.

    This happens due to linker command line order. The ipc_test library comes before the kj static libraries in the command line, but it only contains an operator new symbol, not a ctor symbol, while kj static libraries contain both symbols. The link will prefer the first symbol definition it sees so it uses the operator new from bitcoin together with the ctor from libkj and this does not work because gcc ctor calls clang operator new with a calling convention it is not expecting (as described by chatgpt above).

    Here's the reduced test case I have which reproduces the bug with the same code and flags as the CI build:

    cat > test.h <<EOS
    #include <cstddef>
    template <typename T> struct NoInfer_ { typedef T Type; };
    template <typename T> using NoInfer = typename NoInfer_<T>::Type;
    
    template<typename T> constexpr T&& fwd(NoInfer<T>& t) noexcept { return static_cast<T&&>(t); }
    
    struct PlacementNew {};
    
    void* operator new(size_t, PlacementNew, void* __p) noexcept;
    
    template <typename T, typename... Params>
    inline void ctor(T& location, Params&&... params) {
      new (PlacementNew(), &location) T(fwd<Params>(params)...);
    }
    EOS
    
    cat > test_gcc.cpp <<EOS
    #include "test.h"
    template void ctor<size_t, size_t>(size_t&, size_t&&);
    EOS
    
    cat > test_clang.cpp <<EOS
    #include "test.h"
    #include <iostream>
    
    void* operator new(size_t, PlacementNew, void* __p) noexcept {
      return __p;
    }
    
    size_t f() {
        size_t i = 10;
        size_t j = 20;
        ctor(i, std::move(j));
        return i;
    }
    
    int main() {
        std::cout << "i = " << f() << std::endl;
    }
    EOS
    
    g++ -m32 -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG -pipe -std=c++20 -O0 -g -fPIC -Wall -Wextra -Wno-strict-aliasing -Wno-sign-compare -Wno-unused-parameter -pthread -c test_gcc.cpp -o test_gcc.o
    clang++ -m32 -Wno-error=documentation -O0 -ftrapv -O0 -g3 -g3 -fstack-protector-all -fcf-protection=full -fstack-clash-protection -fPIE -c test_clang.cpp -o test_clang.o
    clang++ -m32 -Wno-error=documentation -O0 -ftrapv -O0 -g3 -g3 -fstack-protector-all -fcf-protection=full -fstack-clash-protection -Wl,-z,relro -Wl,-z,now -Wl,-z,separate-code -fPIE -pie test_gcc.o test_clang.o
    ./a.out
    

    If the test case were compiled properly it would print 20, but due to the calling convention problem here it prints 10, because the line ctor(i, std::move(j)); is supposed to assign j (20) to i, but doesn't do that because operator new returns a garbage value.

    I confirmed that adding -fabi-version=2 to gcc command line above does not fix the problem (the program still prints 10 instead of 20). But I did not experiment further to see which of the compiler flags we are passing may be causing the incompatibility, or if the flags are irrelevant and gcc and clang just disagree on the calling convention for this operator new.

    I am not sure if there is a general solution for this problem. If linker order were changed so that kj libraries were always specified first before the bitcoin library, that would prevent crashing inside kj libraries, but then it might cause a similar crash in the bitcoin code.

    All of this suggests that if you are using depends, and you want to change the compiler or pass different flags in bitcoin, you really need to make the same changes in both builds. Mixing different flags and compiler versions might not be a good idea.

  16. ryanofsky commented at 7:06 PM on February 5, 2025: contributor

    Following seem to be minimal flags to reproduce this issue. None of the other flags except for -fPIC was making any difference, and without -fPIC the program just segfaults instead of printing the wrong value:

    g++ -m32 -fPIC -c test_gcc.cpp -o test_gcc.o
    clang++ -m32 -fPIC -c test_clang.cpp -o test_clang.o
    clang++ -m32 -fPIC test_gcc.o test_clang.o
    ./a.out
    

    So it seems like this version of gcc and clang just always (regardless of flags) assume incompatible calling conventions for operator new and can't be used together here.

    Fix for the bug should just be to use either clang or gcc for this CI build and not try to use both.

  17. ryanofsky commented at 2:31 AM on February 6, 2025: contributor

    This also seems to be a known issue: "It has come to my attention that GCC and clang generate incompatible code for passing an argument of an empty class type." https://itanium-cxx-abi.github.io/cxx-abi/cxx-abi-dev/archives/2015-December/002869.html

  18. maflcko commented at 8:23 AM on February 6, 2025: member

    I wonder if there is a way to detect those at compile or link time. Other than that, printing a warning when mixing two compilers when compiling with depends may be useful?

  19. fanquake commented at 11:41 AM on February 6, 2025: member

    Other than that, printing a warning when mixing two compilers when compiling with depends may be useful?

    Why only depends though? If the issue is generally mixing compilers/flags, then using Clang + system libs on any (gcc-based) Linux system (potentially) has the same problem. It's currently the case that we use Clang + (GCC built) system libs in at least the ASAN,fuzz,valgrind CIs, and Clang + GCC depends libs in the 32-bit job.

  20. ryanofsky commented at 1:37 PM on February 6, 2025: contributor

    Why only depends though? If the issue is generally mixing compilers/flags, then using Clang + system libs on any (gcc-based) Linux system (potentially) has the same problem.

    Yes this issue is not specific to depends, and could theoretically could happen if, for example an ubuntu library package was compiled with gcc and exposed a function with an empty struct parameter, and you tried to use the library with clang.

    The point of having an ABI is to prevent issues like this and allow different compilers to interoperate, but this is a corner case where something hasn't been standardized. Also, in this case, issue goes away if either package is compiled with any optimization (even -O is sufficient) because this makes the call to operator new inlined. And this issue could also be masked by having a different linker command line order that would cause linker to choose compatible ctor and operator new symbol definitions instead of incompatible ones.

    Given corner case nature of this problem, I'm thinking it would be good to send a patch to upstream capnproto to avoid the issue by changing parameter order. Following patch seems to fix the issue in our CI job:

    diff --git a/depends/packages/capnp.mk b/depends/packages/capnp.mk
    index 0c211cbc455d..00ccf08acf4b 100644
    --- a/depends/packages/capnp.mk
    +++ b/depends/packages/capnp.mk
    @@ -5,6 +5,12 @@ $(package)_download_file=$(native_$(package)_download_file)
     $(package)_file_name=$(native_$(package)_file_name)
     $(package)_sha256_hash=$(native_$(package)_sha256_hash)
     
    +$(package)_patches = abifix.patch
    +
    +define $(package)_preprocess_cmds
    +  patch -p2 < $($(package)_patch_dir)/abifix.patch
    +endef
    +
     define $(package)_set_vars :=
       $(package)_config_opts := -DBUILD_TESTING=OFF
       $(package)_config_opts += -DWITH_OPENSSL=OFF
    diff --git a/depends/patches/capnp/abifix.patch b/depends/patches/capnp/abifix.patch
    new file mode 100644
    index 000000000000..1386aadc7452
    --- /dev/null
    +++ b/depends/patches/capnp/abifix.patch
    @@ -0,0 +1,35 @@
    +diff --git a/c++/src/kj/common.h b/c++/src/kj/common.h
    +index 237c41d3..dc2e6381 100644
    +--- a/c++/src/kj/common.h
    ++++ b/c++/src/kj/common.h
    +@@ -1041,24 +1041,26 @@ private:
    + 
    + // We want placement new, but we don't want to #include <new>.  operator new cannot be defined in
    + // a namespace, and defining it globally conflicts with the definition in <new>.  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 to avoid an ABI issues caused by GCC and clang using incompatible calling
    ++// conventions for passing empty struct parameters.
    + 
    + 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 <typename T, typename... Params>
    + inline void ctor(T& location, Params&&... params) {
    +-  new (_::PlacementNew(), &location) T(kj::fwd<Params>(params)...);
    ++  new (&location, _::PlacementNew()) T(kj::fwd<Params>(params)...);
    + }
    + 
    + template <typename T>
    
  21. ryanofsky commented at 2:08 PM on February 6, 2025: contributor
  22. fanquake referenced this in commit f6bf4d2834 on Feb 7, 2025
  23. fanquake referenced this in commit b2833160a2 on Feb 7, 2025
  24. fanquake commented at 10:23 AM on February 7, 2025: member

    Submitted patch in https://github.com/capnproto/capnproto/pull/2235

    Thanks. Pulled that into #29796.

  25. fanquake referenced this in commit b6b2cde84f on Feb 10, 2025
  26. fanquake referenced this in commit f49d38e755 on Feb 18, 2025
  27. fanquake referenced this in commit 2e2192a4c7 on Feb 20, 2025
  28. fanquake referenced this in commit 4fb838fb16 on Mar 5, 2025
  29. fanquake referenced this in commit 364db53143 on Mar 5, 2025
  30. fanquake referenced this in commit 3caef00008 on Mar 5, 2025
  31. fanquake referenced this in commit fe283860b1 on Mar 5, 2025
  32. fanquake referenced this in commit 1ef22ce335 on Mar 12, 2025
  33. fanquake closed this on Mar 13, 2025

  34. fanquake referenced this in commit a5a582d852 on Mar 13, 2025

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-01 06:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me