On 8 Aug 2023, at 02:20, Mark Millard <[email protected]> wrote: > > On Aug 6, 2023, at 14:40, Christoph Moench-Tegeder <[email protected]> > wrote: >> >> while updating our kicad port I'm facing major problems with >> dynamic_cast on FreeBSD 13.2/amd64 - issues are seen with both base >> clang and devel/llvm15, and I guess we're rather talking libc++ here. >> Specifically, dynamic_cast fails when casting from a base to a derived >> type ("downcast" as in C++ lingo?). I already know about "--export-dynamic" >> but that does not help here - and as far as I read other platform's >> build scripts, no other platform requires any kind of "special >> handling" for dynamic_cast to work - what am I holding wrong here, >> or what's missing from the picture? >> >> But for the gory details: here's exhibit A, the code in question: >> https://gitlab.com/kicad/code/kicad/-/blob/7.0/include/settings/settings_manager.h?ref_type=heads#L110 >> That m_settings is declared as >> std::vector<std::unique_ptr<JSON_SETTINGS>> m_settings; >> and contains objects of types derived from JSON_SETTINGS (there's >> the intermediate type APP_SETTINGS_BASE, and specific types like >> KICAD_SETTINGS etc.) The function GetAppSettings<KICAD_SETTING>() is >> called, so that the find_if() in there should return that one >> KICAD_SETTINGS object from m_settings as only that should >> satisfy dynamic_cast - but in fact, no object is found. >> That also happens if I unwind the find_if() and build a simple >> for-loop (as one did, back in the last millenium). >> >> If I point gdb at that m_settings (with "set print object on" and >> "set print vtbl on"), I do find my KICAD_SETTINGS object smack >> in the middle of m_settings: >> >> (gdb) print *(m_settings[5]) >> $18 = (KICAD_SETTINGS) {<APP_SETTINGS_BASE> = {<JSON_SETTINGS> = { >> _vptr$JSON_SETTINGS = 0xed1578 <vtable for KICAD_SETTINGS+16>, >> >> so the type info isn't totally lost here. >> >> When testing this, CXXFLAGS passed via cmake are rather minimal, >> like "-std=c++17 -O0 -fstack-protector-strong -fno-strict-aliasing >> -Wl,--export-dynamic" (and cmake sprinkles some "-g" and a few >> other standard flags into the mix), LDFLAGS ("CMAKE_...LINKER_FLAGS") >> are set up similarly) (I have some inkling that these cmakefiles >> in this project are not always very strict on compiling vs linking). >> >> I had similar issues with dynamic_cast before, as witnessed here: >> https://cgit.freebsd.org/ports/tree/cad/kicad/files/patch-job_use_dynamic_cast_for_updating >> but now that I'm facing the current problem, I have a strong feeling >> that my diagnosis back than was rather bullshit. >> >> Help? > > May be the following type of thing from > > https://www.gnu.org/software/gcc/java/faq.html > > might be relevant to your context? I can not > tell from the description. (A different ABI > could have differing details. But the below > could be at least suggestive of considerations.) > > > > dynamic_cast, throw, typeid don't work with shared libraries > > The new C++ ABI in the GCC 3.0 series uses address comparisons, rather than > string compares, to determine type equality. This leads to better > performance. Like other objects that have to be present in the final > executable, these std::type_info objects have what is called vague linkage > because they are not tightly bound to any one particular translation unit > (object file). The compiler has to emit them in any translation unit that > requires their presence, and then rely on the linking and loading process to > make sure that only one of them is active in the final executable. With > static linking all of these symbols are resolved at link time, but with > dynamic linking, further resolution occurs at load time. You have to ensure > that objects within a shared library are resolved against objects in the > executable and other shared libraries. > > • For a program which is linked against a shared library, no additional > precautions are needed. > • You cannot create a shared library with the "-Bsymbolic" option, as that > prevents the resolution described above. > • If you use dlopen to explicitly load code from a shared library, you > must do several things. First, export global symbols from the executable by > linking it with the "-E" flag (you will have to specify this as "-Wl,-E" if > you are invoking the linker in the usual manner from the compiler driver, > g++). You must also make the external symbols in the loaded library available > for subsequent libraries by providing the RTLD_GLOBAL flag to dlopen. The > symbol resolution can be immediate or lazy. > > Template instantiations are another, user visible, case of objects with vague > linkage, which needs similar resolution. If you do not take the above > precautions, you may discover that a template instantiation with the same > argument list, but instantiated in multiple translation units, has several > addresses, depending in which translation unit the address is taken. (This is > not an exhaustive list of the kind of objects which have vague linkage and > are expected to be resolved during linking & loading.) > > If you are worried about different objects with the same name colliding > during the linking or loading process, then you should use namespaces to > disambiguate them. Giving distinct objects with global linkage the same name > is a violation of the One Definition Rule (ODR) [basic.def.odr]. > > For more details about the way that GCC implements these and other C++ > features, please read the C++ ABI specification. Note the std::type_info > objects which must be resolved all begin with "_ZTS". Refer to ld's > documentation for a description of the "-E" & "-Bsymbolic" flags.
Yes, this is a typical problem when type info is replicated across dynamic library boundaries. The best thing to prevent this is to ensure that the key functions for a class (typically constructors and destructors) are only in one translation unit (object file), and that object file is only in one .so file. -Dimitry
signature.asc
Description: Message signed with OpenPGP
