A problem caused by inline namespace in libcxx

We are compiling a program with a ready-made clang as well as the option ‘-stdlib=libc++’. Then we get a cc.o file. Its symbol has an extra “__1” version as the inline namespace.

e.g

nm CMakeFiles/perf_data_proto.dir/perf_data.pb.cc.o | grep AssignDescriptors
                 U _ZN6google8protobuf8internal17AssignDescriptorsEPFPKNS1_15DescriptorTableEvEPNSt3__19once_flagERKNS0_8MetadataE
c++filt _ZN6google8protobuf8internal17AssignDescriptorsEPFPKNS1_15DescriptorTableEvEPNSt3__19once_flagERKNS0_8MetadataE
google::protobuf::internal::AssignDescriptors(google::protobuf::internal::DescriptorTable const* (*)(), std::__1::once_flag*, google::protobuf::Metadata const&)

However, if we need to use other third-party public libraries next for linking, there wil be conflicts. The third-party library was made using libstdc++ without “__1” in its symbol.

e.g.

The “AssignDescriptors” is defined by libprotoc package in libprotoc.so.

nm /usr/lib/libprotoc.so.31 | grep AssignDescriptors
                 U _ZN6google8protobuf8internal17AssignDescriptorsEPFPKNS1_15DescriptorTableEvEPSt9once_flagRKNS0_8MetadataE
c++filt _ZN6google8protobuf8internal17AssignDescriptorsEPFPKNS1_15DescriptorTableEvEPSt9once_flagRKNS0_8MetadataE
google::protobuf::internal::AssignDescriptors(google::protobuf::internal::DescriptorTable const* (*)(), std::once_flag*, google::protobuf::Metadata const&

Under this circumstance, we will have ‘undefined reference’ linking failures.

In function `quipper::PerfDataProto_PerfEventAttr::GetMetadata() const':
perf_data.pb.cc:(.text+0x3400): undefined reference to `google::protobuf::internal::AssignDescriptors(google::protobuf::internal::DescriptorTable const* (*)(), std::__1::once_flag*, google::protobuf::Metadata const&)'

Has anyone ever encounted with this issue? How can we avoid this problem?

You shouldn’t try to use multiple C++ implementations between ABI boundaries. That will result in undefined behaviour with very weird effects. There is no guarantee that the ABI of libc++'s std::once_flag is the same as libstdc++'s std::once_flag. In fact, the ABIs are different. libc++ has an unsigned long while libstdc++ uses an int. IOW the ABI namespace saved you from some very hard to debug behaviour.

1 Like

But the versioning ABI namespace only addressed ABI boundaries when C++ libraries are both libc++. If I have to use both libc++ and libstdc++ in a mixed way, what can save me from undefined behaviour? I don’t think this scene is rare. The example in my question is quite a typical issue.

In the thread Option to disable inline namespacing completely? - Runtimes / C++ - LLVM Discussion Forums, someone wants to add an option to disable inline namespacing in libc++, though his intention is to use shorthand mangling to improve code size. I think what he planned to do can effectively solve the ABI problem between libc++ and libstdc++. Do you know how this working is going?

It doesn’t just save you from ABI problems when both libraries are libc++, since libstdc++ doesn’t use an inline namespace in it’s default configuration and uses __8 if the inline namespace is enabled, specifically to make sure the names of libc++ and libstdc++ are never the same.

Not using them together. That’s the only option. If the names in your example were the same it would result in UB, since it violates the ODR (and not in a benign way).

The ABI problem isn’t solved if the inline namespace isn’t there. The ABIs are still different. It would just result in weird runtime behaviour instead of link time errors, which is much worse.

1 Like

I can understand the weird results ABI differences may cause. However, what I mentioned in my example is actually quite common. If there is a third-party library already made with libstdc++ and we cannot modify its source code or recompile it with libc++, does it mean it can never been used it along with libc++? Are there any suggestions on how to address this issue if encountered with relevant requirement?

If you have the c++ library in your ABI there isn’t really a way to make it work. The only way would be to create wrappers for the library which compile against libstdc++ and only expose your own types. Then you can link against that and use libc++ in your program.

That would not solve any problem at all. It would allow you to link the two incompatible libraries, and you’d have big big problems. ABI is a lot more than the mangled name of functions.

In fact, I am quite against the ability to remove the inline namespace for optimization purposes because it creates the possibility that unexpecting users will fall into that exact trap.

If you need to link against a third-party library that has been compiled against standard library X and it exposes APIs from that standard library (such as std::once_flag in your example), then you need to compile your code against the standard library X as well. If you’re on Linux, most distributions use libstdc++ by default, so you may want to stick to -stdlib=libstdc++ for compatibility with your ecosystem. The same problem would happen on macOS if you tried linking against libstdc++. Since libc++ is the default on macOS, you’d be fighting against the current. It’s usually better to just use what your system vendor provides, unless you are ready to essentially recompile your complete stack of dependencies with whatever ABI choices you want to make.

1 Like