Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 8 Aug 2023 11:33:40 +0200
From:      Dimitry Andric <dim@FreeBSD.org>
To:        Mark Millard <marklmi@yahoo.com>
Cc:        Christoph Moench-Tegeder <cmt@burggraben.net>, toolchain@freebsd.org
Subject:   Re: c++: dynamic_cast woes
Message-ID:  <6B6A7C9C-8F82-443D-BBC3-B2263FEAFD79@FreeBSD.org>
In-Reply-To: <E0109064-9ED3-483F-A7D9-6588EC07BF1F@yahoo.com>
References:  <ZNATMN6sbaxcqE6n@elch.exwg.net> <E0109064-9ED3-483F-A7D9-6588EC07BF1F@yahoo.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--Apple-Mail=_4CE7E93C-CCC3-4E9D-BC88-39466091E81D
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=utf-8

On 8 Aug 2023, at 02:20, Mark Millard <marklmi@yahoo.com> wrote:
>=20
> On Aug 6, 2023, at 14:40, Christoph Moench-Tegeder =
<cmt@burggraben.net> wrote:
>>=20
>> 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?
>>=20
>> 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_m=
anager.h?ref_type=3Dheads#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).
>>=20
>> 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:
>>=20
>> (gdb) print *(m_settings[5])
>> $18 =3D (KICAD_SETTINGS) {<APP_SETTINGS_BASE> =3D {<JSON_SETTINGS> =3D =
{
>>     _vptr$JSON_SETTINGS =3D 0xed1578 <vtable for KICAD_SETTINGS+16>,
>>=20
>> so the type info isn't totally lost here.
>>=20
>> When testing this, CXXFLAGS passed via cmake are rather minimal,
>> like "-std=3Dc++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).
>>=20
>> 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.
>>=20
>> Help?
>=20
> May be the following type of thing from
>=20
> https://www.gnu.org/software/gcc/java/faq.html
>=20
> 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.)
>=20
>=20
>=20
> dynamic_cast, throw, typeid don't work with shared libraries
>=20
> 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.
>=20
>    =E2=80=A2 For a program which is linked against a shared library, =
no additional precautions are needed.
>    =E2=80=A2 You cannot create a shared library with the "-Bsymbolic" =
option, as that prevents the resolution described above.
>    =E2=80=A2 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.
>=20
> 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.)
>=20
> 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].
>=20
> 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


--Apple-Mail=_4CE7E93C-CCC3-4E9D-BC88-39466091E81D
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename=signature.asc
Content-Type: application/pgp-signature;
	name=signature.asc
Content-Description: Message signed with OpenPGP

-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.2

iF0EARECAB0WIQR6tGLSzjX8bUI5T82wXqMKLiCWowUCZNIL9AAKCRCwXqMKLiCW
o0tHAKDbccP5cnDD8hMeB4ImxW+XriVJWQCfXZ3lvm6jsj9CNWv0jq1BAfEhArU=
=u7NC
-----END PGP SIGNATURE-----

--Apple-Mail=_4CE7E93C-CCC3-4E9D-BC88-39466091E81D--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6B6A7C9C-8F82-443D-BBC3-B2263FEAFD79>