On Mon, 13 Jul 2026 10:53:56 -0700, Pierrick Bouvier wrote:
On 7/13/2026 9:34 AM, Ziyang Zhang wrote:
On Mon, 13 Jul 2026 08:31:18 -0700, Pierrick Bouvier wrote:
The problem is that if that the guest program, or the host library (host
libhello) may depend on libstdc++. If they do and if they depend on a
(newer) symbol not present in lorelei libstdc++, we break ABI. Same goes
for libgcc.

By not doing this, you'll be forced to provide a lorelei devkit
everytime a new libstdc++ is published, or a distribution updates its
gcc.

The only solution is to embed libstd++ and libgcc statically, or rely on
host libraries. If you choose the second option, you know have the
opposite problem. So I think the only way to solve it is really static
linking, even if it will slightly increase binaries size.


The devkit already ships libstdc++ and libgcc anyway: its own clang/
LoreTLC link them dynamically, and we bundle them so the toolchain even
starts. So bundling libstdc++ for the thunk flow adds no new dependency,
it just reuses one the devkit must carry. Static-linking the thunks
wouldn't make the devkit any smaller or more self-contained either,
since clang still needs the dynamic libstdc++; we'd only be adding
static copies on top of it.


There is a difference between the toolchain dependencies, and thunks
dependency. For the first one, it's perfectly find to provide libstdc++,
for the second, it can create conflicts at runtime that may be very hard
to diagnose and solve.

On the ABI risk itself: libstdc++ keeps a backward-compatible ABI, and
the copy we bundle is recent, so a thunked host library only breaks if
it was built against a strictly newer libstdc++ and references a new
symbol. Even then it needs no new devkit, because the resolution order
is the integrator's to choose. On a host newer than our bundle, putting
the system libstdc++ ahead of the devkit's on LD_LIBRARY_PATH lets the
host library bind the newest one, while clang and the Lorelei runtime
keep working against it by backward compatibility. So a gcc update on
the host doesn't force us to reship.


Sure can choose indeed. However, he has to find where such libraries are
located on his system, and prepend that to LD_LIBRARY_PATH. Not the most
user-friendly thing to guess and debug IMHO.

There's also an active reason not to static-link. Both the Lorelei
runtime and the generated thunk libraries depend on libstdc++. If we
statically link libstdc++ into a thunk while the host library it
dispatches to links the system libstdc++ dynamically, the process ends
up with two libstdc++ copies on the host side, right across the boundary
where C++ objects and exceptions pass between the thunk and the real
library. That can break:

- Exceptions: one thrown in the host library may not be caught in the
thunk. The two copies have different std::type_info for the same type,
so the match fails and it hits std::terminate.
- dynamic_cast / typeid across the boundary: they assume a single
type_info per type, so they can start returning null or misidentifying
types.
- Duplicated global state: two sets of the iostream objects, the locale/
facets, and the exception emergency buffer.


That's a good point, but does lorelei even provide a solution for that?
I guess that exception raised from an host library and sent back to
guest binary with target specific types will not result in what is
expected at the moment.

Based on the above reasons, I prefer to do that than statically link and
grow the big artifact. Does that hold for you?


Ok.

Thanks,
Ziyang Zhang


Regards,
Pierrick


Wait a second. I missed something.

Sorry, last time I answered without actually checking the libstdc++ situation on the four distros. You were right to push on it, so I looked properly.

You were right that the x86_64 sysroot is the crux: libc6-amd64-cross gives /usr/x86_64-linux-gnu/ a libc but no libstdc++, so the guest side has no system libstdc++ to fall back on. That's the side we should keep providing ourselves, and the devkit already ships the x86_64 libstdc++ next to the guest runtime for exactly that. It's only a default, though: if a guest executable needs a newer libstdc++, the user can put a newer x86_64 libstdc++.so ahead of ours on the guest LD_LIBRARY_PATH (the -E one) and it takes precedence, while our runtime keeps working against it by backward compatibility. So we are not locking anyone to our version.

The host side is different, there libstdc++ is reliably present. I checked each distro's stock minimal base image, with nothing installed on top:

Debian 12: libstdc++6 12.2.0-14+deb12u1
Ubuntu 24.04: libstdc++6 14.2.0-4ubuntu2~24.04.1
Fedora 40: libstdc++-14.2.1-3.fc40
Arch (2024-08 snapshot): libstdc++.so.6 from gcc-libs 14.2.1
It ships out of the box on all four, and the C compiler pulls it in on Debian, Ubuntu and Arch.

So the split I'd propose: provide libstdc++ only on the guest side (the x86_64 sysroot has none), and on the host side rely on the system's, so the host thunk and the real host library both bind the host's own libstdc++ rather than a copy we ship. That directly addresses the ABI point you raised: nothing we ship shadows the host's libstdc++, so a thunk never forces an older libstdc++ onto a host library built against a newer one.

Does that split sound right to you?


Reply via email to