On 7/13/2026 11:07 AM, Ziyang Zhang wrote: > On Mon, 13 Jul 2026 11:03:23 -0700, Pierrick Bouvier wrote: >> On 7/13/2026 10:56 AM, Ziyang Zhang wrote: >>> 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? >>> >> >> That's a reasonable default, and Lorelei can be updated for this without >> having to update anything on QEMU side (or its documentation). >> >> If I had to choose, I would still do static linking for guest and host >> thunks, because I don't think we can handle exceptions correctly anyway. >> But the most important is that current example already works out of the >> box, which makes this series ready for upstream. >> >> Regards, >> Pierrick > > Thank you, and thanks for the thorough review across all the revisions. > Your feedback made both the plugin and the walkthrough much better than > where they started. >
I know it can be frustrating to address all the picky comments, but in this case, I really think it makes Lorelei better and easier to use, and not just to accommodate my personal taste. Hopefully it will help to make it more popular and integrated with other emulators also! > The libstdc++ discussion was valuable too. I'll keep static linking for > the thunks in mind, and the exception-across-the-boundary case is a fair > point I want to revisit properly. Since all of that lives on the Lorelei > side, we can iterate on it without touching QEMU. > Yes, the important was to have the simplest example possible, without requiring any external dependency. I hope you can use it on Lorelei side as a minimal CI test to keep things working as they are. > Thanks, > Ziyang Zhang > Congrats again for your work on Lorelei! You're welcome to share your paper here once it will be published :). Cheers, Pierrick
