Date: Mon, 25 Apr 2022 16:22:12 -0700 From: Mark Millard <marklmi@yahoo.com> To: jbo@insane.engineer, FreeBSD Hackers <freebsd-hackers@freebsd.org> Cc: joerg@bec.de Subject: Re: llvm & RTTI over shared libraries Message-ID: <D04AB3CC-8359-4037-B6A7-6C43C01D9A77@yahoo.com> In-Reply-To: <079B1A26-DA8B-4158-8FD4-28EE1374CF1F@yahoo.com> References: <079B1A26-DA8B-4158-8FD4-28EE1374CF1F@yahoo.com>
next in thread | previous in thread | raw e-mail | index | archive | help
On 2022-Apr-25, at 15:39, Mark Millard <marklmi@yahoo.com> wrote: > =E2=80=A2 <jbo_at_insane.engineer> wrote on > =E2=80=A2 Date: Mon, 25 Apr 2022 13:01:48 UTC : >=20 >> I've created a small minimal test case which reproduces the problem = (attached). >> The key points here are: >> - CMake based project consisting of: >> - The header-only interface for the plugin and the types = (test-interface). >> - The main executable that loads the plugin (test-core). >> - A plugin implementation (plugin-one). >> - Compiles out-of-the-box on FreeBSD 13/stable with both lang/gcc11 = and devel/llvm14. >> - It uses the exact mechanism I use to load the plugins in my actual = application. >>=20 >> stdout output when compiling with lang/gcc11: >>=20 >> t is type int >> t is type string >> done. >>=20 >>=20 >> stdout output when compiling with lang/llvm14: >>=20 >> could not cast t >> could not cast t >> done. >>=20 >>=20 >> Unfortunately, I could not yet figure out which compiler/linker flags = llvm requires to implement the same behavior as GCC does. I understand = that eventually I'd be better of rewriting the necessary parts to = eliminate that problem but this is not a quick job. >>=20 >> Could somebody lend me a hand in figuring out which compiler/linker = flags are necessary to get this to work with llvm? >=20 > The GCC default behavior is technically wrong. GCC allows being = configured to > do the correct thing --at the cost of ABI mismatches vs. what they = originally > did. (At least that is how I understand what I read in the code.) >=20 > To my knowledge LLVM does not allow clang++ being configured to do the = wrong > thing: it never had the ABI messed up and so did not face the = self-compatibility > question. (Bug-for-bug clang++ vs. g++ compatibility has not been the = major > goal.) Looks like I may have got that wrong, although, like gcc11, it is is more of a when-building-the-C++-library time frame operation than a when-copmiling/linking to use the C++ library time frame thing. Also, nothing says the same strings would be used by clang++ vs. g++, possibly the comparisons might not agree across toolchains when string comparisons are used. . . ./contrib/llvm-project/libcxx/include/typeinfo has the following material for !defined(_LIBCPP_ABI_MICROSOFT): // = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D = // // Implementations // = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D = // // = ------------------------------------------------------------------------- = // // Unique // (_LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION =3D 1) // = ------------------------------------------------------------------------- = // // This implementation of type_info assumes a unique copy of the RTTI = for a // given type inside a program. This is a valid assumption when abiding = to the // Itanium ABI = (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components). // Under this assumption, we can always compare the addresses of the = type names // to implement equality-comparison of type_infos instead of having to = perform // a deep string comparison. // = --------------------------------------------------------------------------= // // NonUnique // (_LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION =3D 2) // = --------------------------------------------------------------------------= // // This implementation of type_info does not assume there is always a = unique // copy of the RTTI for a given type inside a program. For various = reasons // the linker may have failed to merge every copy of a types RTTI // (For example: -Bsymbolic or llvm.org/PR37398). Under this assumption, = two // type_infos are equal if their addresses are equal or if a deep string // comparison is equal. // = --------------------------------------------------------------------------= // // NonUniqueARMRTTIBit // (_LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION =3D 3) // = --------------------------------------------------------------------------= // // This implementation is specific to ARM64 on Apple platforms. // // This implementation of type_info does not assume always a unique copy = of // the RTTI for a given type inside a program. When constructing the = type_info, // the compiler packs the pointer to the type name into a uintptr_t and = reserves // the high bit of that pointer, which is assumed to be free for use = under that // ABI. If that high bit is set, that specific copy of the RTTI can't be = assumed // to be unique within the program. If the high bit is unset, then the = RTTI can // be assumed to be unique within the program. // // When comparing type_infos, if both RTTIs can be assumed to be unique, = it // suffices to compare their addresses. If both the RTTIs can't be = assumed to // be unique, we must perform a deep string comparison of the type = names. // However, if one of the RTTIs is guaranteed unique and the other one = isn't, // then both RTTIs are necessarily not to be considered equal. // // The intent of this design is to remove the need for weak symbols. = Specifically, // if a type would normally have a default-visibility RTTI emitted as a = weak // symbol, it is given hidden visibility instead and the non-unique bit = is set. // Otherwise, types declared with hidden visibility are always = considered to have // a unique RTTI: the RTTI is emitted with linkonce_odr linkage and is = assumed // to be deduplicated by the linker within the linked image. Across = linked image // boundaries, such types are thus considered different types. // This value can be overriden in the __config_site. When it's not = overriden, // we pick a default implementation based on the platform here. > I have a nearly-minimalist change to your example that makes it result = in: >=20 > # ./test-core > t is type_int > t is type_string > done. >=20 > under clang. I pasted a diff -ruN in the message later below but that = may > lead to white space not being fully preserved. (I could send it to you = in > another form if it proved needed.) >=20 > Basically I avoid inline definitions of: >=20 > virtual ~type_base(); > virtual ~type_int(); > virtual ~type_string(); >=20 > Also, these are deliberately(!) the first non-inline virtual > member functions in the 3 types. Where the implementations > are placed controls were the type_info is put for the 3 types. > (Not a language definition issue but a fairly common > implementation technique.) >=20 > I also make the place with the implementation be a tiny .so > that both test-core and libplugin-one.so are bound to. This > makes them use the same type_info definitions instead of > having multiple competing ones around, sort of a form of > single-definition-rule (unique addresses in the process). > With the single definition rule followed, RTTI works just > fine. >=20 > I do warn that this is the first direct adjustment of cmake > material that I've ever done. So if anything looks odd for > how I did the cmake aspects, do not be surprised. I'm not > cmake literate. >=20 > For reference: >=20 > # find clang_test_dist_m_m/ -print > clang_test_dist_m_m/ > clang_test_dist_m_m/plugins > clang_test_dist_m_m/plugins/CMakeLists.txt > clang_test_dist_m_m/plugins/plugin_one > clang_test_dist_m_m/plugins/plugin_one/CMakeLists.txt > clang_test_dist_m_m/plugins/plugin_one/plugin.cpp > clang_test_dist_m_m/shared_types_impl > clang_test_dist_m_m/shared_types_impl/CMakeLists.txt > clang_test_dist_m_m/shared_types_impl/types_impl.cpp > clang_test_dist_m_m/core > clang_test_dist_m_m/core/dlclass.hpp > clang_test_dist_m_m/core/CMakeLists.txt > clang_test_dist_m_m/core/main.cpp > clang_test_dist_m_m/CMakeLists.txt > clang_test_dist_m_m/interface > clang_test_dist_m_m/interface/plugin.hpp > clang_test_dist_m_m/interface/types.hpp > clang_test_dist_m_m/interface/CMakeLists.txt >=20 > where the diff -ruN is . . . >=20 > diff -ruN clang_test_dist/ clang_test_dist_m_m/ | more > diff -ruN clang_test_dist/CMakeLists.txt = clang_test_dist_m_m/CMakeLists.txt > --- clang_test_dist/CMakeLists.txt 2022-04-19 13:38:59.000000000 = -0700 > +++ clang_test_dist_m_m/CMakeLists.txt 2022-04-25 12:51:03.448582000 = -0700 > @@ -5,4 +5,5 @@ >=20 > add_subdirectory(core) > add_subdirectory(interface) > +add_subdirectory(shared_types_impl) > add_subdirectory(plugins) > diff -ruN clang_test_dist/core/CMakeLists.txt = clang_test_dist_m_m/core/CMakeLists.txt > --- clang_test_dist/core/CMakeLists.txt 2022-04-19 13:38:59.000000000 = -0700 > +++ clang_test_dist_m_m/core/CMakeLists.txt 2022-04-25 = 13:18:52.539921000 -0700 > @@ -19,9 +19,12 @@ > PRIVATE > test-interface > dl > + PUBLIC > + shared-types-impl > ) >=20 > add_dependencies( > ${TARGET} > + shared-types-impl > plugin-one > ) > diff -ruN clang_test_dist/interface/types.hpp = clang_test_dist_m_m/interface/types.hpp > --- clang_test_dist/interface/types.hpp 2022-04-19 13:38:59.000000000 = -0700 > +++ clang_test_dist_m_m/interface/types.hpp 2022-04-25 = 14:48:52.534159000 -0700 > @@ -7,18 +7,20 @@ >=20 > struct type_base > { > - virtual ~type_base() =3D default; > + virtual ~type_base(); > }; >=20 > struct type_int : > type_base > { > + virtual ~type_int(); > int data; > }; >=20 > struct type_string : > type_base > { > + virtual ~type_string(); > std::string data; > }; >=20 > diff -ruN clang_test_dist/plugins/plugin_one/CMakeLists.txt = clang_test_dist_m_m/plugins/plugin_one/CMakeLists.txt > --- clang_test_dist/plugins/plugin_one/CMakeLists.txt 2022-04-19 = 13:38:59.000000000 -0700 > +++ clang_test_dist_m_m/plugins/plugin_one/CMakeLists.txt = 2022-04-25 13:19:20.188778000 -0700 > @@ -12,3 +12,14 @@ > PRIVATE > plugin.cpp > ) > + > +target_link_libraries( > + ${TARGET} > + PUBLIC > + shared-types-impl > +) > + > +add_dependencies( > + ${TARGET} > + shared-types-impl > +) > diff -ruN clang_test_dist/shared_types_impl/CMakeLists.txt = clang_test_dist_m_m/shared_types_impl/CMakeLists.txt > --- clang_test_dist/shared_types_impl/CMakeLists.txt 1969-12-31 = 16:00:00.000000000 -0800 > +++ clang_test_dist_m_m/shared_types_impl/CMakeLists.txt = 2022-04-25 12:55:29.760985000 -0700 > @@ -0,0 +1,15 @@ > +set(TARGET shared-types-impl) > +add_library(${TARGET} SHARED) > + > +target_compile_features( > + ${TARGET} > + PRIVATE > + cxx_std_20 > +) > + > +target_sources( > + ${TARGET} > + PRIVATE > + types_impl.cpp > +) > + > diff -ruN clang_test_dist/shared_types_impl/types_impl.cpp = clang_test_dist_m_m/shared_types_impl/types_impl.cpp > --- clang_test_dist/shared_types_impl/types_impl.cpp 1969-12-31 = 16:00:00.000000000 -0800 > +++ clang_test_dist_m_m/shared_types_impl/types_impl.cpp = 2022-04-25 14:49:23.599440000 -0700 > @@ -0,0 +1,5 @@ > +#include "../interface/types.hpp" > + > +interface::type_base::~type_base() {} > +interface::type_int::~type_int() {} > +interface::type_string::~type_string() {} >=20 >=20 > That is all there is to the changes. >=20 =3D=3D=3D Mark Millard marklmi at yahoo.com
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?D04AB3CC-8359-4037-B6A7-6C43C01D9A77>