Search code examples
c++gcclinkerboost-asiold

linker error defined in discarded section with boost asio awaitable_operators


Include details about your goal

I want to build this project in a docker container.

Describe expected and actual results

When build in release mode in the docker container the linker reports an error:

Deploying '<unknown> Dockerfile: Dockerfile'…
Building image…
Preparing build context archive…
[==================================================>]151/151 files
Done

Sending build context to Docker daemon…
[==================================================>] 145.5kB
Done

Step 1/10 : FROM conanio/gcc13-ubuntu16.04
 ---> 4ed52a5da755
Step 2/10 : COPY src /home/conan/cxx_template/src
 ---> Using cache
 ---> 4f985f400a74
Step 3/10 : COPY CMakeLists.txt /home/conan/cxx_template
 ---> Using cache
 ---> 428b77440f02
Step 4/10 : COPY conanfile.py /home/conan/cxx_template
 ---> Using cache
 ---> baf0737e4cc9
Step 5/10 : COPY main.cxx /home/conan/cxx_template
 ---> Using cache
 ---> ba6f9aed868f
Step 6/10 : WORKDIR /home/conan/cxx_template
 ---> Using cache
 ---> 88ed7f05cf32
Step 7/10 : RUN sudo chown -R conan /home/conan  && conan remote add artifactory http://195.128.100.39:8081/artifactory/api/conan/conan-local && conan install . --output-folder=build --settings build_type=Release  --settings compiler.cppstd=gnu20 --build=missing
 ---> Using cache
 ---> 3ca072904555
Step 8/10 : WORKDIR /home/conan/cxx_template/build
 ---> Using cache
 ---> 053ee0731c7e
Step 9/10 : RUN cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -D CMAKE_BUILD_TYPE=Release
 ---> Using cache
 ---> c57340482eed
Step 10/10 : RUN cmake --build .
 ---> Running in 0bf73846ac2f
Scanning dependencies of target cxx_template
[ 25%] Building CXX object src/CMakeFiles/cxx_template.dir/hello.cxx.o
/home/conan/cxx_template/src/hello.cxx: In function 'boost::asio::awaitable<void> helloWorld()':
/home/conan/cxx_template/src/hello.cxx:11:1: warning: no return statement in function returning non-void [-Wreturn-type]
   11 | }
      | ^
[ 50%] Linking CXX static library libcxx_template.a
[ 50%] Built target cxx_template
Scanning dependencies of target run_server
[ 75%] Building CXX object CMakeFiles/run_server.dir/main.cxx.o
[100%] Linking CXX executable run_server
`_ZN5boost4asio12experimental19awaitable_operatorsaaEPZNS2_aaINS0_15any_io_executorEEENS0_9awaitableIvT_EES7_S7_E108_ZN5boost4asio12experimental19awaitable_operatorsaaINS0_15any_io_executorEEENS0_9awaitableIvT_EES7_S7_.Frame.destroy' referenced in section `.rodata.cst8' of src/libcxx_template.a(hello.cxx.o): defined in discarded section `.text._ZN5boost4asio12experimental19awaitable_operatorsaaEPZNS2_aaINS0_15any_io_executorEEENS0_9awaitableIvT_EES7_S7_E108_ZN5boost4asio12experimental19awaitable_operatorsaaINS0_15any_io_executorEEENS0_9awaitableIvT_EES7_S7_.Frame.destroy[_ZN5boost4asio12experimental19awaitable_operatorsaaINS0_15any_io_executorEEENS0_9awaitableIvT_EES7_S7_]' of src/libcxx_template.a(hello.cxx.o)
`_ZN5boost4asio6detail20co_spawn_entry_pointEPZNS1_20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm0ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEEE464_ZN5boost4asio6detail20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm0ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEE.Frame.destroy' referenced in section `.rodata.cst8' of src/libcxx_template.a(hello.cxx.o): defined in discarded section `.text._ZN5boost4asio6detail20co_spawn_entry_pointEPZNS1_20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm0ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEEE464_ZN5boost4asio6detail20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm0ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEE.Frame.destroy[_ZN5boost4asio6detail20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm0ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEE]' of src/libcxx_template.a(hello.cxx.o)
`_ZN5boost4asio6detail20co_spawn_entry_pointEPZNS1_20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm1ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEEE464_ZN5boost4asio6detail20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm1ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEE.Frame.destroy' referenced in section `.rodata.cst8' of src/libcxx_template.a(hello.cxx.o): defined in discarded section `.text._ZN5boost4asio6detail20co_spawn_entry_pointEPZNS1_20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm1ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEEE464_ZN5boost4asio6detail20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm1ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEE.Frame.destroy[_ZN5boost4asio6detail20co_spawn_entry_pointINS0_12experimental6detail25parallel_group_op_handlerILm1ENS3_18wait_for_one_errorENS1_26awaitable_async_op_handlerIFvSt5arrayImLm2EENSt15__exception_ptr13exception_ptrESB_ENS0_15any_io_executorEEEJNS0_24deferred_async_operationIFvSB_ENS1_17initiate_co_spawnISD_EEJNS1_21awaitable_as_functionIvSD_EEEEESL_EEESD_SK_EENS0_9awaitableINS1_28awaitable_thread_entry_pointET0_EEPNSN_IvSP_EENS1_14co_spawn_stateIT_SP_T1_vEE]' of src/libcxx_template.a(hello.cxx.o)
collect2: error: ld returned 1 exit status
CMakeFiles/run_server.dir/build.make:84: recipe for target 'run_server' failed
make[2]: *** [run_server] Error 1
CMakeFiles/Makefile2:76: recipe for target 'CMakeFiles/run_server.dir/all' failed
make[1]: *** [CMakeFiles/run_server.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
Error response from daemon: The command '/bin/sh -c cmake --build .' returned a non-zero code: 2
Failed to deploy '<unknown> Dockerfile: Dockerfile': Can't retrieve image ID from build stream

When I build this project not in the docker container it builds in release and debug.
When I build this project in the docker container in debug mode it builds.
When I build this project in the docker container in release mode it does not build and the linker reports above error.

I expect it to build in release and in debug in the container and outside of the container. Or to not build in release outside of the docker container and in the docker container.

Describe what you’ve tried
I reduced my actual project and created a "minimum, reproducible example".
I searched in the internet for "defined in discarded section" but I did not understand what exactly is wrong in my code and how to debug/fix this problem.

how to reproduce:

git clone -b defined_in_discarded_section  https://github.com/werto87/cxx_template.git && cd cxx_template && docker build .

EDIT1 (reacting on Alan Birtles first comment) trying to make the question self contained and not rely on external links

project

project structure:

[walde@laptop cxx_template]$ tree
.
├── CMakeLists.txt
├── conanfile.py
├── Dockerfile
├── main.cxx
└── src
    ├── CMakeLists.txt
    ├── hello.cxx
    └── hello.hxx

CMakeLists.txt

cmake_minimum_required(VERSION 3.15.7)
set(CMAKE_CXX_STANDARD 20)
project(myproject)
add_subdirectory(src)
add_executable(run_server main.cxx)
target_link_libraries(run_server cxx_template)

conanfile.py

from conan import ConanFile


class Project(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeToolchain", "CMakeDeps"

    def configure(self):
        self.options["boost"].header_only = True

    def requirements(self):
        self.requires("boost/1.84.0")

Dockerfile

FROM conanio/gcc13-ubuntu16.04

COPY src /home/conan/cxx_template/src
COPY CMakeLists.txt /home/conan/cxx_template
COPY conanfile.py /home/conan/cxx_template
COPY main.cxx /home/conan/cxx_template


WORKDIR /home/conan/cxx_template

RUN sudo chown -R conan /home/conan && conan install . --output-folder=build --settings build_type=Release  --settings compiler.cppstd=gnu20 --build=missing

WORKDIR /home/conan/cxx_template/build

RUN cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -D CMAKE_BUILD_TYPE=Release

RUN cmake --build .

main.cxx

#include "src/hello.hxx"
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
int
main ()
{
  using namespace boost::asio::experimental::awaitable_operators;
  auto ioContext = boost::asio::io_context{};
  boost::asio::ip::tcp::socket socket (ioContext);
  auto test = socket.async_connect ({}, boost::asio::use_awaitable) && socket.async_connect ({}, boost::asio::use_awaitable);
  abc ();
}

src/CMakeLists.txt

add_library(cxx_template hello.cxx)
find_package(Boost)
target_link_libraries(cxx_template PUBLIC
        pthread
        boost::boost
        )
target_include_directories(cxx_template INTERFACE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
        $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>)
target_compile_features(cxx_template PUBLIC cxx_std_20)

src/hello.cxx

#include "hello.hxx"
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>

boost::asio::awaitable<void> helloWorld() {
  using namespace boost::asio::experimental::awaitable_operators;
  auto ioContext = boost::asio::io_context{};
  boost::asio::ip::tcp::socket socket(ioContext);
  auto test = socket.async_connect({}, boost::asio::use_awaitable) &&
              socket.async_connect({}, boost::asio::use_awaitable);
}

int abc() { return 42; }

src/hello.hxx

#pragma once
#include <boost/asio/awaitable.hpp>
int abc();

docker gcc version

conan@4ebb90d4c85a:~/matchmaking_proxy/build$ gcc --version
gcc (GCC) 13.1.0

EDIT2: removed not needed add remote in docker file


Solution

  • So, there are two key components:

    • two separate linker objects (the main program and the library) instantiate a set of template instances
    • the one from the library is discarded because it is unused

    Key to reproducing this is that the exact instantiations of deferred async operations match in both the main program and the library implementation details. This, to me, seems like most likely a compiler issue.

    I've reworded the main/lib sources to remove a-typical things:

    • the coro lacked a return statement, anyways, so that was invoking UB)
    • non of the parallel-group deferred operations where actually awaited, and I worried that somehow this caused more symbols to be discarded than otherwise, so I added those.
    • File main.cxx

       #include "src/hello.hxx"
       #include <boost/asio.hpp>
       #include <boost/asio/experimental/awaitable_operators.hpp>
      
       int main() {
           namespace asio = boost::asio;
           using asio::ip::tcp;
           using namespace asio::experimental::awaitable_operators;
      
           asio::io_context ioc;
           tcp::socket      s(ioc);
      
           auto token = asio::use_awaitable;
           co_spawn(
               ioc,
               (s.async_connect({}, token) && s.async_connect({}, token)),
               asio::detached);
           abc();
       }
      
    • File src/hello.cxx

       #include "hello.hxx"
       #include <boost/asio.hpp>
       #include <boost/asio/experimental/awaitable_operators.hpp>
      
       boost::asio::awaitable<void> helloWorld() {
           namespace asio = boost::asio;
           using asio::ip::tcp;
           using namespace asio::experimental::awaitable_operators;
      
           tcp::socket s(co_await asio::this_coro::executor);
           auto token = asio::use_awaitable;
           co_await (s.async_connect({}, token) && s.async_connect({}, token));
      
           co_return;
       }
      
       int abc() { return 42; }
      

    Veryifying the Hypothesis

    If you change the token in one of the two translation units to e.g.

    auto token = as_tuple(asio::use_awaitable);
    

    the linker is satisfied

    Workaround

    Like I said, I think this likely is a compiler bug to do with c++20 coroutine machinery. However, since creating your own token does work around it, I'd consider duplicating the implementation:

    struct my_token_t : boost::asio::use_awaitable_t<> { } static inline constexpr my_token;
    template <typename... Sigs>
    struct boost::asio::async_result<my_token_t, Sigs...> : async_result<use_awaitable_t<>, Sigs...> {};
    

    Now the conflict is avoided: https://github.com/werto87/cxx_template/pull/1

    Oops. I spoke too soon. I accidentally misinterpreted the build output, probably forgot that hello.cxx wasn't saved. It appears that the offending template instantiations are more indirectly related to the token, and more has to be duplicated to hide the issue. I will see whether I can make this happen. Still consider reporting a compiler issue, or if you cannot get traction, report it at https://github.com/chriskohlhoff/asio/issues

    Way Simpler Mitigation

    Not even a workaround: https://github.com/werto87/cxx_template/pull/2 you can avoid the conflicting symbols being emitted at all, by making them file-static. E.g. make helloWorld static or enclose it in an anonymous namespace:

    
    namespace /*anon*/ {
        /*static*/ boost::asio::awaitable<void> helloWorld() {
            namespace asio = boost::asio;
            using asio::ip::tcp;
            using namespace asio::experimental::awaitable_operators;
    
            tcp::socket s(co_await asio::this_coro::executor);
            auto token = asio::use_awaitable;
            co_await (s.async_connect({}, token) && s.async_connect({}, token));
    
            co_return;
        }
    }
    

    This builds in your container.