Glibc wrappers for (nearly all) Linux system calls
LWN.net needs you!The GNU C Library (glibc) is a famously conservative project. In the past, that conservatism created a situation where there is no way to directly call a number of Linux system calls from a glibc-using program. As glibc has relaxed a bit in recent years, its developers have started to reconsider adding wrapper functions for previously inaccessible system calls. But, as the discussion shows, adding these wrappers is still not as straightforward as one might think.Without subscribers, LWN would simply not exist. Please consider signing up for a subscription and helping to keep LWN publishing.
A C programmer working with glibc now would look in vain for for a straightforward way to invoke a number of Linux system calls, including futex(), gettid(), getrandom(), renameat2(), execveat(), bpf(), kcmp(), seccomp(), and a number of others. The only way to get at these system calls is via the syscall() function. Over the years, there have been requests to add wrappers for a number of these system calls; in some cases, such as gettid() and futex(), the requests were summarily rejected by the (at-the-time) glibc maintainer in fairly typical style. More recently these requests have been reopened and others have been entertained, but there have been no system-call wrappers added since glibc 2.15, corresponding roughly to the 3.2 kernel.
On the face of it, adding a new system-call wrapper should be a simple exercise. The kernel has already defined an API for the system call, so it is just a matter of writing a simple function that passes the caller's arguments through to the kernel implementation. Things quickly get more complicated than that, though, for a number of reasons, but they all come down to one root cause: glibc is not just a wrapper interface for kernel-supplied functionality. Instead, it provides a (somewhat standard-defined) API that is meant to be consistent and independent of any specific operating system.
There are provisions for adding kernel-specific functions to glibc now; those functions will typically fail (with errno set to ENOSYS) when called on a kernel that does not support them. Examples of such functions include the Linux-specific epoll_wait() and related system calls. As a general rule, though, the glibc developers, as part of their role maintaining the low-level API for the GNU system, would like to avoid kernel-specific additions.
This concern has had the effect of keeping a lot of Linux system-call wrappers out of the GNU C Library. It is not necessarily that the glibc developers do not want that functionality, but figuring out how a new function would fit into the overall GNU API is not a straightforward task. The ideal interface may not (from the glibc point of view) be the one exposed by the Linux kernel, so another may need to be designed. Issues like error handling, thread safety, support on non-Linux systems, and POSIX-thread cancellation points can complicate things considerably. In many cases, it seems that few developers have wanted to run the gauntlet of getting new system-call wrappers into the library, even if the overall attitude toward such wrappers has become markedly more friendly in recent years.
Back in May 2015, Joseph Myers proposed relaxing the rules just a little bit, at least in cases when the functionality provided by a wrapper might be of general interest. In such cases, Joseph suggested, there would be no immediate need to provide support for other operating-system kernels unless somebody found the desire and the time to do the work.
Roland McGrath is, by his own admission, the hardest glibc developer to convince about the value of adding Linux-specific system calls to the library. He still does not see a clear case for adding many Linux system-call wrappers to the core library; it is only clear, he said, when the system call is to be a part of the GNU API:
I propose that we rule out adding any symbols to the core libc ABIs that are not entering the OS-independent GNU API.
Roland does not seem to believe that glibc should entirely refuse to support system calls that don't meet the above criterion, though. Instead, he suggested creating another library specifically for them. It would be called something like "libinux-syscalls" (so that one would link with "-linux-syscalls"). Functions relegated to this library should be simple wrappers, without internal state, with the idea that supporting multiple versions of the library would be possible.
There was some discussion on the details of this idea, but the core of it seems to be relatively uncontroversial. Also uncontroversial is the idea that glibc need not provide wrappers for system calls that are obsolete, that cannot be used without interfering with glibc (set_thread_area() is an example), or those that are expected to have a single caller (such as create_module()). So Carlos O'Donell has proposed a set of rules that would clear the way for the immediate addition of operating-system-independent system calls into the core and the addition of a system-dependent library for the rest.
Of course, "immediate" is a relative term. Any system-call wrappers will still need to be properly implemented and documented, with test cases and more. There is also, in some cases, the more fundamental question of what the API should look like. Consider the case of the futex() system call, which provides access to a fast mutual-exclusion mechanism. As defined by the kernel, futex() is a multiplexer interface, with a single entry point providing access to a range of different operations.
Torvald Riegel made the case that exposing this multiplexer interface would do a disservice to glibc users:
He proposed exposing a different API based around several functions with
names like futex_wake() and futex_wait(); he also posted
a patch implementing this interface.
Joseph, while not disagreeing with that
interface, insisted that the C library should provide direct access to
the raw system call, saying: "The fact that, with hindsight, we might
not have designed an API the way it was in fact designed does not mean we
should embed that viewpoint in the choice of APIs provided to
users
". In the end, the two seemed to agree that both types of
interface should, in some cases, be provided. If the C library can provide
a useful higher-level interface, that may be appropriate to add, but more
direct access to the system call as provided by the kernel should be there
too.
The end result of all this is that we are likely to see a break in the
logjam that has kept new system-call wrappers out of glibc. Some new
wrappers could even conceivably show up in the 2.23 release, which can be
expected sometime around February 2016. Even if the attitude and rules
have changed, though, this is still glibc we are talking about, so catching
up with the kernel may take a while yet. But one can take comfort in the
fact that a path is now visible, even if it may yet be a slow one.
