It only became something else, when C got standardised, a tiny subset of it landed into ISO C, grew beyond UNIX, and the rest of the UNIX API surface landed at Open Group's POSIX.
Obviously on the UNIX world nothing changed, until GNU/Linux came into scene and decided to act in another way, due to being GNU ecosystem + Linux kernel combo.
Linux has successfully paved the way and demonstrated its practicality. Yes, it made certain unfortunate mistakes along the way, but OpenBSD (or any other OS) can learn from those and provide a better solution.
If you mean a C based OS ABI, there are many other alternatives, like on managed OSes, IBM and Unisys surviving mainframe and micro computers, OS IPC like COM/XPC/AIDL/FIDL/D-BUS.
That is one _heck_ of a TL;DR executive summary! I am quietly awed. I doff my hat to you for this.
Nobody seems to understand how this feature works, even though it's quite simple. The process asks the kernel to only allow system calls from one segment. If you don't like that, don't ask for it.
Calling into system calls in libc is not a hindrance at all for the use of other languages, any more than calling directly into a syscall would be. Cross-language function calls have been a time honored tradition since the first compilers were developed.
Linux's ossified syscall ABI is a quirk of Linux, and not a feature found in most, let alone many, operating systems.
I would say that it is a hindrance though. It's a hindrance even for relatively straight forward C code. You cannot for example use clone and expect the libc to continue working.
https://sourceware.org/bugzilla/show_bug.cgi?id=10311
> If you use clone() you're on your own.
These libraries are the problem, not the solution. They're full of static global data which will get clobbered whenever you do anything more complicated than I/O. They use thread local variables for returning errors codes. It's bad legacy stuff.
And FFS, Ive been using OpenBSD for well over 2 decades now. I'd say any syscall changes done in the OS are minimal compared to eg. linux.
Says who? I certainly wouldn't consider Linux's syscall ABI to be well designed. You know what is well designed? A stable facade that provides minimal overhead between two components, which prevents tight coupling. Like libc.
> You can obviously extend on it and deprecate at your will.
Only by breaking userland, which grinds this process to a crawl, even when it can happen at all. libc provides a better interface here.
> Thing is, call gates are quite more common now
I fail to see the relevance here. The mechanism by which syscalls are made doesn't make it cheaper to ossify your syscall ABI, nor does it improve security. It can certainly reduce the performance hit of a context switch. But, even with call gates, the overhead of a context switch is so high that adding the overhead of calling a wrapped function in libc is hardly going to matter.
> and there is quite some effort into of runtime linking vs static linking
What effort? Yeah, you have ld.so doing the relocation for you at runtime, but this happens once at program load time. Compared to everything else that must occur to load a program, dynamic linking adds minimal overhead.
> In my opinion, libc should not be a broker for privileged operations.
That's an interesting opinion, but beyond an unlisted "plethora of reasons", I don't see any relevant reason for this blanket ban. Having a single path into and out of the system call interface provides a way in which userland protections -- such as syscall pinning, immutable pages, etc., can be added. For defense in depth, this is useful.
> but the most obvious is that languages may not use libc in their linkage.
How does language runtime library linkage matter? What is the real difference to a language whether it calls read() or the read syscall? Either way, it has to marshal arguments through a FFI. Either way, it has to do the same amount of work. Languages must be ported to each operating system they run on. On OpenBSD and MacOS in particular, this porting means calling libc. On most OSes, libc is considered the preferred interface. On Linux, it's not, and because of that, folks assume that Linux's relative uniqueness here is somehow the default.
There is really no point to directly using syscalls. In nearly every case for unistd, socket, and fcntl, the libc wrapper is negligible. You can do all of the same things. There are few, if any, enforced C-isms. Even where they exist, they can be abstracted with a middling to average FFI library. Performance-wise, the significant overhead is making the syscall. Calling a library to do this has little measurable effect.
This is much ado about nothing.
Actually, opposed to libc. Minimal overhead means either pass by register values or using call gates to copy stack frames. Libc just adds a new layer of cruft.
>Having a single path into and out of the system call interface provides a way in which userland protections -- such as syscall pinning, immutable pages, etc., can be added.
None of those are libc-specific, and can also be implemented using privileged kernel implementations. So, nothing new.
>How does language runtime library linkage matter? What is the real difference to a language whether it calls read() or the read syscall? Either way, it has to marshal arguments through a FFI.
The level of indirection. If there is no real difference, why not use the syscall directly? Why use yet another layer of crap to - in the end - perform a call by register?
> Languages must be ported to each operating system they run on.
One thing us the language,another is the runtime. The runtime should be as agnostic as possible, and that actually means using syscalls, not a given libc version.
> There is really no point to directly using syscalls. In nearly every case for unistd, socket, and fcntl, the libc wrapper is negligible. You can do all of the same thing.
You are right, they are almost the same thing. Thats one more motive to skip libc completely, because adds little to nothing no non-c languages.
> There are few, if any, enforced C-isms.
Such as struct (lack of) alignment, string formats and call conventions. Few, but relevant.
Eh. Whatever. Call it libunix if you prefer. If you want, you can literally strip libc of anything you don't like, call it libunix, and OpenBSD's ld.so mechanics will gladly pin syscalls to that.
You were the one trying to justify that layer as a de-facto ABI interface. For some things, it is not actually necessary and - in my opinion - should not be the target as a system ABI compatibility. Its a bit like saying "MSVC++ Runtime should be the de-facto runtime for every Windows application". There is a clear distinction between having a posix-compatible POSIX system (which targets source-code portability, focused mainly on C as the lowest common denominator) or a System V compatible system that provides POSIX compatibility.
> Eh. Whatever. Call it libunix if you prefer.
It is not not "whatever". A language-specific library should not - in principle - be a core dependency of every userland application on a general purpose UNIX system; A given language may implement its own runtime, as well as provide pure statically linked binaries. You may have a different opinion, but it seems a bit childish to discard the fact that not only there are use cases, but this has been somewhat of a problem in the past just because you don't agree with it. No one is stopping you from using libc.
libc is the standard, but there is nothing that prevents you from hacking up libc to make some Frankenstein library that makes you feel better about "avoiding C" for whatever that's worth. Somehow, you think that libc is making your language runtime worse because it's written in C. Somehow, you think that writing an FFI -- as most high level languages do -- into libc is an inconvenience. It will probably blow your mind to realize that most operating systems, and thus most syscall interfaces, are also written in C. :-)
Then you compare libc to the MSVC++ runtime for some odd reason. And yet, if you do write applications that target Windows, you use their system libraries, which are predominantly written in C and provide C functions, regardless of the high level language you choose. These libraries implement the interface to the kernel, much like most operating systems other than Linux.
Actually, its not linux-centric, its System V-centric. And there is no ossification - at least not more than you have on what you're defending - a standard userland ABI runtime. I'm assuming you actually never used older systems - syscall/interrupt-based/call gate based is actually the "the facto standard", not whatever libc-based system you're running.
I also find it amusing you keep talking about linux. Did Linus eat your homework or something? For me it is quite odd, specially considering I've been using OpenBSD since 2.9, FreeBSD since 4.3 and only somewhat recently use linux as a main unix-compatible environment.
> libc is the standard
Says you and nobody else. Case in point, the problem is with a runtime that IS NOT libc-based, and should be supported, because it is targeting a SPECIFIC OS VERSION and not a libc version. Btw, that's why OpenBSD has its support cycles measured in releases, and not libc versions, but I'm assuming you are completely aware of this.
> Somehow, you think that writing an FFI -- as most high level languages do -- into libc is an inconvenience. It will probably blow your mind to realize that most operating systems, and thus most syscall interfaces, are also written in C. :-)
Yes, we - the rational people that think a general-purpose operating system should not be dictating runtimes onto users - we are all stupid, and we never did any OS-related development. We also fail to realize that C is basically the lowest common denominator, that's why its used as a semi-stub language for machine-level stuff. We are very very stupid here in the land "we are not you". Is that it?
> And yet, if you do write applications that target Windows, you use their system libraries, which are predominantly written in C and provide C functions, regardless of the high level language you choose
Well, yes and no. Last time I checked, any 32 bit assembly application using int 21h is honored (haven't tested it on 64 bit), ~40 YEARS ON. Same goes for DPMI calls. That's the "syscall layer" for you. You may also be surprised to find out that Windows is actually a sort of hybrid microkernel (such as macos X family, btw), so you can actually provide a lot more "core functionality" as part of the base OS, such as audio mixing or graphics primitives. and you can use the "fancy approach" and use an available runtime (MSVC++ IS an example of a runtime similar to libc), or use your own.
The last semi-monolithic version of it was Windows ME, and the lowest common denominator for operation is - you guessed it - system interrupts. Your console app developed in 1999 in Windows ME should run fine on a modern Windows version, as it will your OpenBSD console app, as long as you don't depend on libc. In fact, your 1983 MSDOS app should have no problem running in a fairly recent version of Windows without any kind of emulation whatsoever, except for the v86 wrapping mode.
Oh and no, I'd say most windows core libraries are C++, not C. And even there, they're trying to get rid of it for - AT LEAST - 20 years - many internal services in Longhorn were C#. Things evolve, and the sooner we get rid of the C heritage, the better.
> These libraries implement the interface to the kernel, much like most operating systems other than Linux.
Your simplistic view of operating systems based on what OpenBSD is amuses me. As an example, feel free to ping me back when your userland kernel ABI actually implements useful stuff like a unified audio interface (or linux, since you mention it so much). Or volume management. Or actually unified printing experience. You know, all the stuff you actually get from microkernel or hybrid-based kernels that you DON'T have in monolithic kernel design. Have libc doing something that isn't either a runtime library for your specific language or a syscall and you may have a point. Until then, its actually quite more restrictive than using int-based syscalls.
Note: edited for formatting
Actually, my code runs just fine on older versions of Unix, because it uses libc and doesn't attempt to use interrupts, call gates, or whatever hidden interfaces varying between minor releases or different hardware revisions, were available on a specific version and a specific kernel compilation.
> Case in point, the problem is with a runtime that IS NOT libc-based, and should be supported, because it is targeting a SPECIFIC OS VERSION and not a libc version.
The compiled runtime may be specific to a particular libc / *nix version, but the source code is typically agnostic. Detect what it needs and trust the compiler to fill in the details. Or, of course, you could have conditional compilation and custom code for every minor release and *nix flavor out there. Clearly, no, that's not typical.
> and the sooner we get rid of the C heritage
Ah, we get to the crux of the issue. You actually believe that it is language and not engineering process and tooling, that somehow influences safety and security.
> Your console app developed in 1999 in Windows ME should run fine on a modern Windows version...
You're describing something that's more of a bug than a feature. Windows' binary backwards compatibility has been a source of many security vulnerabilities and instances of flaky behavior caused by regressions in the emulation layer. And, yes, it's an emulation layer at this point more than it's an interface or API.
I'll skip the weird strawman about how building portable applications backed by libc is somehow an OpenBSD-ism.
Software interrupts are glacially slow. You may want to familiarize yourself with modern hardware and syscall traps. These may work similarly to a software interrupt, but they have much better performance, even when pinned to libc, libunix, or whatever other library you use instead of littering your code with explicit trap instructions.
OpenBSD has spent a lot of time sanitizing their libc implementation, and they have even split their static C runtime library to further reduce its footprint.
Developers are only really penalized for what they use. read, write, socket, bind, close, open, mmap, etc., really aren't that complex. Already, that's a significant portion of the libc footprint that any other language would use.
I personally see little reason why libc couldn't be further minimized into a libunix that just contained the syscall interface. It doesn't make much of a difference really, but perhaps it would shift the largely misplaced unease that many developers have regarding anything involving C.