Support SerenityOS libc for cross compilation
Serenity is straightforward to build: https://github.com/SerenityOS/serenity/blob/master/Documentation/BuildInstructions.md
It should be pretty easy to provide libc in exactly the same way we do for e.g. glibc and FreeBSD.
However, I have no idea if SerenityOS has a stable libc ABI at this point. cc @linusg
However, I have no idea if SerenityOS has a stable libc ABI at this point.
Definitely not, it doesn't really have any stability guarantees - that's kinda the point. Software is more fun when you don't have to think about backwards compat and can make breaking changes atomically across a single monorepo :^)
I'm no longer a maintainer but for the same cultural reasons I don't expect serenity to ever adopt a versioning scheme or releases. Anything older than master doesn't matter.
I guess I can understand that perspective, though it's a bit unfortunate for a project like Zig that would like to support Serenity.
Perhaps as Serenity matures, there'll eventually be a subset of libc that's de facto "stable" (enough) for us to provide support for.
Alternatively, we could just provide stub symbols for all symbols that have ever appeared in Serenity's libc, with no effort to omit removed symbols, and leave it to users to deal with breakage.
Removals are rare, looking through the git history these are the only ones from recent years that stand out:
- https://github.com/SerenityOS/serenity/commit/b16ec1880cec01a182e095a1e14e0c642a8e2173
- https://github.com/SerenityOS/serenity/commit/c1a4a0e1afb2cd71c132b110e8e151ee6185dc2e
- https://github.com/SerenityOS/serenity/commit/85a53ba32bd705b1fad388093326322423d503b1
Additions are more common but usually fit into the category of "missing posix C symbols that every other libc had for decades, needed to build some port".
I don't think shipping a single unversioned serenity libc abilist that is a snapshot of master refreshed at a similar frequency as other libcs would be problematic.
@linusg are there any libraries besides Userland/Libraries/LibC that we should consider to be part of "libc"? What about Userland/Libraries/LibSystem for example?
In the past there were separate LibM and LibPthread but they got merged into LibC so that should be all you need.
LibSystem is the only thing permitted to make syscalls, implemented by a bit of special casing in the dynamic loader: https://github.com/SerenityOS/serenity/blob/362b391726a9292e42f15e7015dae010df171e13/Userland/Libraries/LibELF/DynamicLinker.cpp#L396
LibC links to it, I'm not sure that's relevant to what you're trying to do.
I asked about LibSystem in particular because I see syscall.h gets installed to /usr/include. But if that header is useless for anything but LibC, then I guess it doesn't matter.
Ah, I see, looks like there's two or three things in userland calling syscall() directly (e.g. https://github.com/SerenityOS/serenity/blob/362b391726a9292e42f15e7015dae010df171e13/Userland/Libraries/LibCore/SessionManagement.cpp#L20) but that's clearly an oversight - that's what the non-standard functions in https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibC/serenity.cpp are for.
Another question: Does /usr/lib/Loader.so contain symbols that programs are supposed to link against, as with most other libcs? For example, the dynamic linker contains __tls_get_addr in glibc, and contains a bunch of dl* APIs on FreeBSD.
~~It looks like we'll need LibRegex for regex.h functions, which are regular POSIX.~~ Ah, no, the symbols are still in LibC. We might need some LibRegex headers, however.
Started some of the ABI tooling over here: https://github.com/ziglang/libc-abi-tools/tree/main/serenity
As can be seen from the .abilist files, Serenity's libc could definitely do with a bit more discipline with regards to the application of static or __attribute__((visibility("hidden"))).
Another question: Does
/usr/lib/Loader.socontain symbols that programs are supposed to link against, as with most other libcs? For example, the dynamic linker contains__tls_get_addrin glibc, and contains a bunch ofdl*APIs on FreeBSD.
Yes: https://github.com/SerenityOS/serenity/blob/362b391726a9292e42f15e7015dae010df171e13/Userland/Libraries/LibELF/DynamicLinker.cpp#L653-L670
As can be seen from the
.abilistfiles, Serenity's libc could definitely do with a bit more discipline with regards to the application ofstaticor__attribute__((visibility("hidden"))).
I assume you mean things like s_allocation_enabled leaking to the outside world? Happy to send a PR upstream if you can quickly list what needs fixing 🙂
Yes: https://github.com/SerenityOS/serenity/blob/362b391726a9292e42f15e7015dae010df171e13/Userland/Libraries/LibELF/DynamicLinker.cpp#L653-L670
Are these meant for use by user programs, though, or just libc?
I assume you mean things like
s_allocation_enabledleaking to the outside world? Happy to send a PR upstream if you can quickly list what needs fixing 🙂
- I would say all the symbols starting with a double underscore, except for the
__cxa_*ones, look suspect. - Also
_ctype_anderrno_storage? - And yeah, all the
s_symbols. - On aarch64, there is also a weird
_stacksymbol withSTT_NOTYPE; I couldn't figure out where this one comes from, so I explicitly blacklisted it.nmclaims it's of typeN, i.e. debug info...? - There's a ton of mangled C++ symbols that probably shouldn't be exported. I'm also explicitly blacklisting those by checking for a
_Zprefix.
There's likely more than these though. I wonder if someone upstream would be interested in thoroughly cleaning up the exports? But anyway, it's not the end of the world.
By the way, I also noticed some headers that aren't valid C, but are either named as if they should be, or located in a place that makes it seem like they should be:
/usr/include/bits/dlfcn_integration.h/usr/include/bits/stdio_file_implementation.h/usr/include/mallocdefs.h/usr/include/sys/devices/gpu.h
It might also be interesting to note that libc headers depend, directly or indirectly, on some non-libc headers:
/usr/include/AK/Platform.h/usr/include/AK/Types.h/usr/include/Kernel/API/.../usr/include/Kernel/Arch/mcontext.h(and/usr/include/Kernel/Arch/*/mcontext.h)/usr/include/LibELF/ELFABI.h/usr/include/LibRegex/RegexDefs.h
This is not the end of the world, but it does mean I have to write fragile logic like this to pick out the headers we actually want:
for arch in aarch64 riscv64 x86_64; do
mkdir Build/$arch/Root/usr/include.min
cp Build/$arch/Root/usr/include/*.h Build/$arch/Root/usr/include.min
rm Build/$arch/Root/usr/include.min/{crypt,mallocdefs,syscall}.h
cp -r Build/$arch/Root/usr/include/{arch,arpa,bits,net,netinet,sys} Build/$arch/Root/usr/include.min
rm Build/$arch/Root/usr/include.min/bits/{dlfcn_integration,stdio_file_implementation}.h
rm Build/$arch/Root/usr/include.min/sys/devices/gpu.h
mkdir Build/$arch/Root/usr/include.min/AK
cp Build/$arch/Root/usr/include/AK/{Platform,Types}.h Build/$arch/Root/usr/include.min/AK
mkdir Build/$arch/Root/usr/include.min/Kernel
mkdir Build/$arch/Root/usr/include.min/Kernel/Arch
cp Build/$arch/Root/usr/include/Kernel/Arch/mcontext.h Build/$arch/Root/usr/include.min/Kernel/Arch
mkdir Build/$arch/Root/usr/include.min/Kernel/Arch/$arch
cp Build/$arch/Root/usr/include/Kernel/Arch/$arch/mcontext.h Build/$arch/Root/usr/include.min/Kernel/Arch/$arch
mkdir Build/$arch/Root/usr/include.min/Kernel/API
cp Build/$arch/Root/usr/include/Kernel/API/{archctl_numbers,Ioctl,prctl_numbers,serenity_limits,ttydefaults,ttydefaultschars}.h Build/$arch/Root/usr/include.min/Kernel/API
cp -r Build/$arch/Root/usr/include/Kernel/API/POSIX Build/$arch/Root/usr/include.min/Kernel/API
mkdir Build/$arch/Root/usr/include.min/LibELF
cp Build/$arch/Root/usr/include/LibELF/ELFABI.h Build/$arch/Root/usr/include.min/LibELF
mkdir Build/$arch/Root/usr/include.min/LibRegex
cp Build/$arch/Root/usr/include/LibRegex/RegexDefs.h Build/$arch/Root/usr/include.min/LibRegex
done
I don't think this use case is supported by SerenityOS. I think the issue should be closed until such time as the project decides it wants to support a stable ABI.
I don't think this use case is supported by SerenityOS. I think the issue should be closed until such time as the project decides it wants to support a stable ABI.
It's not clear to me why we would want to support Serenity in the standard library, but not support cross compilation to it? Fundamentally, it's the same thing; we have to rely on there being a libc ABI of some kind, however unstable it might be.
Per @linusg's comment above, removals are rare, and even those functions that were removed seem to have been non-standard, being either a) Serenity-specific extensions, or b) from some userland library (ncurses?) and thus shouldn't have been in libc to begin with.
So I don't really see any harm in supporting cross-compilation by periodically tracking SerenityOS master. It's the same thing we'll be doing in the standard library, no? As long as people stick to the C/POSIX standard subset of libc, they'll probably be fine.
SerenityOS only supports in-tree compilation. That means they will port Zig to it and use that port to compile in-tree SerenityOS applications. That means the libc will always be available. There is no purpose of cross-compilation if there is no stable ABI.
SerenityOS only supports in-tree compilation.
That's not true, a CMake toolchain file has been available for third-party projects for ages. I wrote about this before: https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/
That means they will port Zig to it and use that port to compile in-tree SerenityOS applications.
As far as I know interest in Zig from serenity's side has been exclusively to port existing third-party software to the system. At least that was the initial motivation and is also my own motivation.
There is no purpose of cross-compilation if there is no stable ABI.
I would prefer to be able to run zig build -Dtarget=x86_64-serenity on my Linux host over having to build the SerenityOS Zig port, copying my Zig project onto a serenity machine, and building it there 🙂
As outlined Serenity's lack of stability guarantees is largely because no one wants to deal with backwards compat just for the sake of it, it does not mean things break or even change frequently.
That's not true, a CMake toolchain file has been available for third-party projects for ages. I wrote about this before: https://linus.dev/posts/how-to-build-a-standalone-gui-application-for-serenityos/
Thanks for letting me know. Today I learned. In that case, I retract https://github.com/ziglang/zig/issues/23879#issuecomment-2892031507.
In any case, I suppose we could at least ask the Serenity community to see if there's willingness to commit to a stable libc ABI, if only for the subset of standard C/POSIX APIs. Given issues like https://github.com/SerenityOS/serenity/issues/23335, it seems there's at least some interest in supporting the standard APIs even without an immediate need.
https://github.com/SerenityOS/serenity/issues/25941
@linusg should I wait for an upstream PR re: https://github.com/ziglang/zig/issues/23879#issuecomment-2891991426?
I haven't gotten around to it yet, will try to find some time today or tomorrow.
It might also be interesting to note that libc headers depend, directly or indirectly, on some non-libc headers:
I found some prior art within serenity itself (build integration for https://github.com/SerenityOS/jakt) that confirms this: https://github.com/SerenityOS/serenity/blob/ef0fc880aec444f00a430ac9832176701e9f75ec/Toolchain/BuildJakt.sh#L227-L251
It's not obvious to me how to break up the relationship between regex.h and LibRegex/RegexDefs.h for example but we can definitely file an issue for it and get some opinions.
https://github.com/SerenityOS/serenity/pull/26056 should remove the _stack symbol from AArch64 and make the {sigset,set,long}jmp symbols STT_FUNC.