About Musl
musl.libc.org
musl.libc.org
I don't get the sense that they think they are infallible, just that they are right far more often than not. You can't achieve the sort of spartan aesthetic that you see in the musl codebase by handling things like everyone else. The ability to distill things to their essence betrays a very high level of understanding.
As they say in the streets, the dope sells itself.
On the other hand you have musl, and we can all appreciate its design goals, but then it is similarly maintained by people that believe they and their work are utterly infallible. Hence why to this date they have not made it possible to detect musl and would rather cite you a POSIX standards meeting from 1980 than fix an incompatibility everyone else handles differently. Just ctrl+F musl on http://landley.net/toybox/ for some insight.
The last time I felt like smashing the desk because of musl was when I realized you can't stacktrace a thread in a syscall because they refuse to annotate their 10 lines of assembler doing the syscall with the required CFI directives. They would rather wait for someone to write a 200 line AWK script that does it ([1]) than fix debugging on platforms like arm64.
1: https://github.com/bminor/musl/blob/master/tools/add-cfi.x86...
If you are talking about eglibc, it's been merged back for years.
Debian carries a patch since 2006: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=272265
Why is my Pidgin thinking I'm offline, 2007: https://developer.pidgin.im/ticket/2825
Firefox 2008: https://bugzilla.mozilla.org/show_bug.cgi?id=214538
Chef, 2015: https://github.com/chef/chef/issues/2894
Someone stumbled on this brokenness in Rust in 2017: https://github.com/rust-lang/rust/issues/41570
And then drums Wed Aug 2 19:12:20 2017 glibc 2.26 released with a patch. Maybe programs can rely on this behavior starting in 2027.
It got turned on by default for all locks, broke quite a few of applications (which were using locks wrong, but still), then the instructions ended up being broken in every Intel architecture to date that has ever implemented them [1].
Most of the distros put in some patches to disable the use of TSX, but the 'no-lock-elision' flag provided by glibc didn't actually disable all attempts at lock elision. So the end result is that the glibc in Ubuntu 16.04 tries to use TSX for rwlocks, but not mutexes. Which is unfortunate, because the mechanism to opt-out of the use of TSX in glibc was only implemented for mutexes, but not rwlocks.
TSX only ever provided performance benefits in specific use cases, and at some point glibc upstream became sane and turned it all off by default and provided opt-in mechanisms instead.
[1]: skylake errata: "Using Intel TSX Instructions May Lead to Unpredictable System Behavior", https://www.intel.com/content/dam/www/public/us/en/documents...
2. The pthread implementation of mutex using TSX behaved differently from the prior implementation in cases of misuse. This was deemed acceptable because applications that were impacted were using mutexes illegally, but the result was that a ton of software in all the distros started crashing randomly (IIRC the scenario was a double unlock. pre-TSX glibc didn't complain, post-TSX the program crashed or something).
3. TSX was not generally beneficial. The glibc implementation did something like try using a transaction 3 or 5 times before giving up. The worst-case performance of starting the transaction, going as far into the critical section as possible, aborting, repeat 2x, then finally taking the lock normally is much worse than just taking the lock.
One example of this problem is in detecting C11 thread support. This is a feature of the C library, not the compiler, but it's the compiler that sets C11 feature flags. For threads, you can only detect that they aren't supported by __STDC_NO_THREADS__. But of course since this can't be defined retroactively in old versions of musl, you can't use it to detect C11 thread support in musl. And since you can't detect musl at all, you can't even just assume the version of musl is recent enough and turn it on unconditionally.
The musl team seems to advocate guessing at supported features by detecting with test compilation (a.k.a. configure scripts.) They don't believe in depending on specific versions of musl because features can be backported or disabled. Okay fine, but then why not give us explicit means to detect it? Why not just define MUSL_C11_THREADS or something so we can detect it at compile-time instead of during a slow, fuzzy, non-portable configure step?
Does anyone know of a workaround for this? Does musl define any kind of feature macro that can be used to detect C11 thread support?
So firstly, a common use-case of threads is for improving performance. Using a job system is a good mechanism for this that doesn't strongly couple the architecture to the details of number of threads, or etc. Often this can be done with a fork-join model, where when you have a big chunk of work to do, you can create a bunch of jobs to do some work, then either wait on them immediately, or go off and do some other work while you wait, either approach works since it's possible to have a job system that will do work on the waiting thread.
At this point it's then pretty simple to either build in a fallback either directly into the job system, where you either do the work immediately upon launching it, or upon waiting on it. or it's trivial to detect presence of threads in the work code, and replace the job code with a simple for loop or something similar.
This is far from the only style of system I've seen that can work optionally with threads, but it's one I've seen working to good effect.
Hahah wow that's some class A entertainment. I feel for you and yours!
If the projects existence is causing issues for some people, they have a right to complain, even if it is creating a free benefit for someone else.
Why would someone contribute to a project they think is inferior?
Basically, I don’t understand your objection.
What ? This does not make sense. Being free does not give any passes.
For example imagine that you maintain a popular application, you start receiving issues that your application doesn't work with musl. You try to fix it, but musl doesn't provide a reliable way to detect itself. You try to submit a patch but it is rejected.
You absolutely have right to complain, lack of musl would prevent you from wasting time on making your application work with it.
https://github.com/llvm/llvm-project/tree/master/libc/src/st...
https://devblogs.microsoft.com/cppblog/the-great-c-runtime-c...
You mean Roland? What did he do?
Ulrich Drepper
EDIT: Actually, searching for "Ulrich Drepper asshole" did turn up some stuff. I'd still be interested, as the stuff I found was rather general.
Ulrich made tons of bad technical decisions, and mistreated everyone who disagreed with him, was the reason Debian forked glibc for a long time, etc. There are some famous bugzillas that are utterly unexplainable.
https://sourceware.org/bugzilla/show_bug.cgi?id=4980
(2007) "gethostbyname() etc break for /etc/hosts with both ::1 and 127.0.0.1 localhost entries" Initially closed by Drepper as "WONTFIX" or "NOTABUG" (can't find state history and it's been a while!) with dismissive comments, continuing to do so after people patiently explained how this was a real issue. It was eventually resolved (I don't know by whom)
Musl also removed access to Linux functionality because the author disagrees with the kernel's design:
"linux's sched_* syscalls actually implement the TPS (thread scheduling) functionality, not the PS (process scheduling) functionality which the sched_* functions are supposed to have. omitting support for the PS option (and having the sched_* interfaces fail with ENOSYS rather than omitting them, since some broken software assumes they exist) seems to be the only conforming way to do this on linux."
LOL. Do you really want to depend on a libc whose author thinks this way? Who removes functionality that the kernel provides because in his opinion the kernel's design is wrong?
Where did you get this? A rationale is given at [1], and it's basically:
1) dlclose() being a no-op is allowed by POSIX.
2) It's common for unloading and reloading libraries to trigger subtle/latent bugs, so making dlclose() a no-op leads to more reliable operation in the common case.
3) Managing thread-local storage in combination with unloading libraries is a complex problem, and arguably requires libc to either weaken error handling or leak memory.
This is certainly consistent with an opinionated-standards-lawyer-curmudgeon view of the world, but it seems pretty far from just saying that you're not allowed to do it because you shouldn't.
[1] https://wiki.musl-libc.org/functional-differences-from-glibc...
Excluding an interface is better than providing a buggy one, IMO.
[1] What glibc needs to do is finally merge libpthread into libc proper. The amount of stub functions, conditional logic, mutex reimplementation, and other chicanery glibc implements in an attempt to support runtime loading of libpthread[2] makes the code extremely difficult to navigate and debug. Which IME goes a long way toward explaining why there are years of unfixed races in glibc.
[2] By runtime I mean loading libpthread as a dependency of a shared library where the main binary wasn't linked against libpthread.
Name one.
And yes, pthreads should just be part of libc. Separating them is artificial and awkward.
I also point in that report to three other similar races which I didn't have the time to properly document (because, seriously, they're so numerous and the backlog so long, it's hard not to be cynical).[1]
Look, I like glibc. I applaud their focus on ABI compatibility. glibc is burdened by tremendous technical debt, much of it blameless. And the project doesn't see the investment and attention it should. musl by contrast is a breath of fresh air in terms of correctness and simplicity, but they achieve that in part by taking a different approach than glibc. And that approach isn't necessarily better, it's just different, and they each have their costs.
Pick your poison.
[1] And I don't like drive-by complaints. If I submit a ticket for a project I try to back it with substantial evidence and suggested remedies, such as this Python2 + FIPS OpenSSL bug analysis I submitted last year: https://bugs.launchpad.net/ubuntu/+source/python2.7/+bug/183... This is especially important for projects like glibc which are drowning in open bug reports.
Okay, so the only bug you've managed to come up with during a discussion of dlclose has nothing to do with dlclose. I am very much unconvinced that dlclose has fundamental correctness problems.
> musl by contrast is a breath of fresh air in terms of correctness and simplicity, but they achieve that in part by taking a different approach than glibc. And that approach isn't necessarily better, it's just different, and they each have their costs.
Yes, musl has a different approach. And this approach makes musl unsuitable for anything I might want to do. The maintainer is too opinionated and thinks he can dictate software architecture to libc users. The glibc maintainers don't think so, not as much.
1. libgobject (and anything depends on it, i.e. anything GTK) uses pthread_key_create to allocate thread local storage slots, but since threads can't be joined during dlclose(), there are no safe timing when we can call corresponding pthread_key_delete. It leads to bug reports like this: https://gitlab.gnome.org/GNOME/glib/issues/1311, and ultimately, GTK developers said they explicitly does not support dlclose()-ing their library because they can't see how is it possible: https://bugzilla.gnome.org/show_bug.cgi?id=733065. They suggest people dlopen() their library with RTLD_NODELETE, and good luck on those who indirectly depends on libgobject.
2. On Android, libcxx (the C++ runtime lib) does the same thing, it intentionally leaks pthread_key if you unload it. What's worse is the amount of available pthread_key is limited to 100, so after 100 iterations of dlopen()/dlclose() your process would simply crash. And, one of our significant customers insisted our library must pass their "while true dlopen dlclose" test, because hey, this is supported by libc, right? Mind you, Android does not use glibc, it uses bionic.
These two instance make me believe that it is not possible to support true dlclose() because, well, the two most used libc implementation tried and failed.
The functionality is still available via pthread sched functions as specified by POSIX, so I'd argue that functionality was not removed, just moved:
https://git.musl-libc.org/cgit/musl/commit/?id=1e21e78bf7a5
IMHO this makes sense for a libc that explicitly emphasizes POSIX-correctness. POSIX defines these functions, and it is against the spec to have process scheduling functions work as thread scheduling functions (which are a separate set of functions in POSIX).
edit: Just realized that this might prevent changing scheduling parameters of another process by PID, though.
musl does expose the extent of functionality provided by the kernel through the corresponding pthread scheduling functions.
> Who removes functionality that the kernel provides because in his opinion the kernel's design is wrong?
The kernel doesn't provide the necessary functionality. The functionality that the kernel does provide is still available in musl.
I can understand that this is probably frustrating (I'm sure I would be frustrated if I had to deal with it), but I can respect musl's decision not to have sched_* do the wrong thing.
Particularly because this actually ensures that programs linked to musl are 100% portable (modulo bugs), rather than introducing subtle platform incompatibilities due to slightly differing platform semantics.
Solaris, Illumos, the *BSDs, Windows -- all tried to emulate the Linux kernel ABI, and could not do a good enough job, so they all pretty much abandoned the project. The Linux kernel ABI won and that's that.
Well, if we could convince devs that static linking is bad... But even then, the Linux kernel ABI leaks for all sorts of things, starting with /proc.
And yes, static linking is bad, for a variety of reasons, the biggest of which is that static linking is stuck with 1970s semantics that suck because accidental symbol interposition happens all too easily. That doesn't mean that static linking couldn't grow ELF-like semantics, but if it hasn't over the past 40+ years... Still, even assuming that, there would be other reasons not to love static linking. If we could convince people to ship only dynamically-linked executables, then the ABI to emulate would be smaller, but still huge.
It also has nothing to do with ABIs - the idea of a standard is to provide a common subset of functionality supported by all platforms that it targets.
Nothing about musl or any other libc implementation prevents developers from including the linux headers and using whatever linux-specific functionality they like.
I think the fact that they tried to emulate the ABI is a much stronger argument for the ABI than the fact that they abandoned doing so…
Besides the system calls, the ioctls, and the fcntls, including all the various driver ioctls, there's also /proc. /proc is quite large an interface.
musl can't remove the functionality provided by the kernel. You're still free to use syscall(3) or otherwise invoke the kernel routine yourself.
May I assume you abstained from using glibc prior to 2.30 on account of glibc not providing a wrapper for the gettid syscall? More apropos: do you dislike how glibc and musl both change the behavior of Linux setuid syscall to make it standards-conformant--process-global instead of thread-local? Should they simply provide the Linux semantics rather than the standard semantics? Has the kernel no culpability here for not providing the necessary interfaces for implementing the standard-compliant semantics?
Yes, and they are every bit as fallible as anyone. Here is an example of a pretty pedestrian bug: https://github.com/build2/build2/issues/50
And that's a pity, really, since otherwise it looks like a reasonable implementation. But this dogmatic "we know better" attitude now triggers an allergic reaction.
This software seems to be using Linux-specific functionality through the C library. Wouldn't it be better to use Linux system calls directly in that case?
I think it's weird how Linux has no user space library like the kernel DLLs found in Windows. Everyone assumes the C library is the interface to the kernel, even the Linux kernel manuals. Then people end up being burned when the C library maintainers quote POSIX standards at them or omit new system calls people want.
I wrote a liblinux² when I first realized this but it's currently unfinished. Eventually the kernel developers made an amazing nolibc.h file³ for their own tools but it doesn't seem to be used outside the kernel itself. Wish they'd publish it as an official Linux system call library!
¹ https://git.musl-libc.org/cgit/musl/commit/?id=1e21e78bf7a5
² https://github.com/matheusmoreira/liblinux
³ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
In my opinion, Linux is the only operating system that did it right. The system call binary interface is clearly documented¹ at the processor architecture² level so it can be used from any language. Since everything in user space is replaceable, people could actually get rid of C and rewrite everything in Rust (or anything else) if they wanted to.
If seen my share of old x64 binaries refusing to run on modern linux distro, spouting some sort of undecipherable glibc error message on startup (something along the lines of "version `GLIBC blah' not found)
Unfortunately, I've seen the exact opposite in production: older binary running atop newer glibc and crashing with that kind of message.
glibc abi compatibility is and has been a joke.
It actually bit me on the ass a couple weeks ago when installing a rust binary instead of using cargo --install because it shaved ~10 minutes off running a CI pipeline. Had to fall back because the rust bin wasn't linked against musl libc when it was compiled for unknown Linux targets, and the authors don't distribute a musl compatible binary.
That this message implies that not the node binary was not found but libc.so is missing, is just mind boggling. Furthermore, installing libc-compat doesn't help, you will actually have to set NVM to download nodejs from the "unofficial-builds" repository. What a load of bollocks.
"Attention to correctness
musl was the first Linux libc to have mutexes safe to use inside reference-counted objects, the first to have condvars where newly-arrived waiters can't steal wake events from previous waiters, and the first to have working thread cancellation without race conditions producing resource-leak or double-close. All of these are requirements of the specification that were ignored by other implementations, and getting them right was a consequence of careful reading of those specifications.
musl's entire development history has been a process of reading specifications, seeking clarifications when corner cases aren't adequately covered, and proceeding with extreme caution when implementing functionality that's underspecified."
It is very good to adhere to specs when possible, but not always.
I really wish it worked as I like the lean&mean&precise approach they've been using!
Your problem lay elsewhere, unless you were using a really old version of musl that lacked the SO_REUSEPORT definition.
The best way of using Python though, is to create a virtualenv (since Python 3, venv module is built in which makes it straight forward, python -m venv <directory>) and install packages inside of it. This gives you control to use exact versions of packages you need in your applications and makes your application not tied to the OS, so system upgrades and Python version updates are much easier to perform.
Is this true for drivers, too? like the coupling of cuda versions to tensorflow versions?
If you have any dependencies that depend on system libraries there are two options:
- the libraries can be compiled statically, they call them manylinux wheels, this generally works well, except if your dependencies are overlapping with python or other packages' dependencies. Most commonly this happen if package depends on openssl. If the compiled-in openssl is different than the version on the system in certain circumstances python might crash. This is for example why psycopg2 was initially distributed as manylinux but now they opt for the second method (they still provide psycopg2-binary but they discourage its use; most time it works fine but it is a problem on certain distros)
- make python create bindings on installation, this makes installation process do small compilation to create bindings between system library and python, this makes the packages robust, but tied to the OS. In my experience this is not an issue, but it can be annoying, because you still don't have 100% control of dependencies.
This issue is what made me investigate Nix[2] package manager, because it gives you full control of all dependencies down to libc making everything fully reproducible, so you can control the exact version of python to use and all system and python dependencies.
[1] in one of my previous jobs on a team that I joined they were running their applications on CentOS 5 that was already EOL, because rebuilding RPMs to CentOS 7 (at the time the newest version) was a lot of work, another issue was that they were bound to python package version that came with the system, they could create their own RPMs, but no one did it because maintaining that was adding more work. I spent time converting the python code to be packaged using setuptools. Once that was done switching the OS was trivial. We finally also could use the latest versions of many of our dependencies.
[2] This is IMO good article which describes tooling that makes Nix much more enjoyable to use: https://christine.website/blog/how-i-start-nix-2020-03-08
Oh. I've been saying mew-sel this whole time.
It probably lacks a lot of the "enterprise" cruft in CentOS/RHEL or Debian, but that's a good thing for everyone else.
My biggest issue is the lack of ASan, or ever HWAsan. valgrind is nice, but quite slow. I know Rich has been working on a new allocator that will make ASan easier to implement, but it's still a few months away.
The docker file is full of comments like the following:
# libc6-compat needed by outdated grpc-tools # we get # sh: node_modules/grpc-tools/bin/protoc: not found # can be removed if we remove grpc-tools and assume an existing protoc for dev # i.e. developers would independently intall protoc in order to generate # grpc-web # we install separate from the packages above to avoid a failure noticed on # 2019-11-01 # apk update && apk add protobuf grpc gcompat libc6-compat # ERROR: unsatisfiable constraints: # musl-1.1.20-r4: # breaks: libc6-compat-1.1.24-r0[musl=1.1.24-r0] # satisfies: musl-utils-1.1.20-r4[musl=1.1.20-r4] #RUN apk add libc6-compat
Bottom line is that it's awesome to have something lean like Alpine, but that can hardly justify the effort to maintain it. It feels like Linux two decades ago.
# libc6-compat needed by outdated grpc-tools
# we get
# sh: node_modules/grpc-tools/bin/protoc: not found
# can be removed if we remove grpc-tools and assume an existing protoc for dev
# i.e. developers would independently intall protoc in order to generate
# grpc-web
# we install separate from the packages above to avoid a failure noticed on
# 2019-11-01
# apk update && apk add protobuf grpc gcompat libc6-compat
# ERROR: unsatisfiable constraints:
# musl-1.1.20-r4:
# breaks: libc6-compat-1.1.24-r0[musl=1.1.24-r0]
# satisfies: musl-utils-1.1.20-r4[musl=1.1.20-r4]
#RUN apk add libc6-compat
formatted that for you# libc6-compat needed by outdated grpc-tools
# we get
# sh: node_modules/grpc-tools/bin/protoc: not found
# can be removed if we remove grpc-tools and assume an existing protoc for dev
# i.e. developers would independently intall protoc in order to generate
# grpc-web
# we install separate from the packages above to avoid a failure noticed on
# 2019-11-01
# apk update && apk add protobuf grpc gcompat libc6-compat
# ERROR: unsatisfiable constraints: # musl-1.1.20-r4:
# breaks: libc6-compat-1.1.24-r0[musl=1.1.24-r0]
# satisfies: musl-utils-1.1.20-r4[musl=1.1.20-r4]
#RUN apk add libc6-compat
Directly touching syscalls instead of using libc sucks because a) it kills LD_PRELOAD hooks and b) it makes porting to new architectures extremely time-consuming. (e.g. I'm porting lots of stuff to FreeBSD/aarch64 — most languages were rather trivial to port, Go took months and several people)
[0] To k, every other language/library/program is too big.
That but also because lots of code in glibc would simply not work in kernel space
Python wheels compiled using the manylinux infrastructure actually do work on Alpine + musl.
That said I wouldn't recommend running Python on musl as I have seen incredibly weird behaviour including corruption of Python's low integer cache resulting in some truely awful consequences. i.e amounts of currency suddenly being 10x what they should be.
@jpgvm Do you mean they work if you manually install them? b/c pip won't install glibc wheels on alpine.
Edit: Yes: https://thenewstack.io/alpine-linux-heart-docker/