Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The C standard specifies absolutely no "C ABI" standard. The two "standard" calling conventions you cite, cdecl and stdcall, are Wintel conventions.

However, since C is the low-level implementation language for almost every dominant operating system, a given platform's Operating System ABI is almost always locally synonymous with the C ABI on that platform, and people tend to speak of "being compatible with the C ABI" when what they really mean is that they're compatible with the OS's ABI.

But if you move to, say, a Mainframe OS, the OS ABI is dramatically different than the "C ABI" you're imagining from Wintel.



To put it another way: C doesn't have an ABI; C has an API (or a wire protocol, if you prefer.) The abstraction layer of C doesn't exist between the machine-architecture and the C compiler; rather, the abstraction layer of C exists between the C compiler and the user, and takes the form of uncompiled C source code.

The design of C is predicated on an approach to portability that involves shipping source code, not binaries, around, with the destination machine having a compiler for its own architecture. (Given this, it's kind of shocking that things like Docker work at all. Goes to show how large a server-side monopoly those Intel ISAs currently hold.)

---

But wouldn't it be interesting if the "C compiler" were made into a low-level part of the operating system—basically taking the form of a JIT with a persistent cache—and then an executable format were specified, which really was just an archive of C source that was JIT-compiled by the OS when you first execv(3)ed it? Then you would have portable C binaries, because you'd be using the "correct" C ABI: C source-code.

Well, if you tack on a bit of pre-chewing to annotate the C source a bit (and maybe do some arch-neutral optimizations), and you have Apple's current LLVM-IR-based "Bitcode" approach to binaries.


Check out the architecture of TempleOS.


What, like C as a shell script? Gott in Himmel!


How about an interactive C++ interpreter: https://root.cern.ch/cling

Also it's Gott im Himmel ;-) (unless it's a reference I didn't get)


iPhone autocorrect :-(


That seems slightly pedantic. While it's true that there's no cross-platform C ABI, most platforms have a C ABI, and it's well-defined (and defined in terms of C data types) on that platform.

It's sort of like saying that there's no such thing as "rules of the road", because the road makers don't specify how you're supposed to drive on them, and the rules differ by jurisdiction and sometimes you're in a wilderness with no roads. The term is commonly understood to mean "rules for using roads, in the road-containing jurisdiction which you're in". I think the same thing is true of the phrase "C ABI", although if people are coming away thinking there's a cross-platform C ABI, then yes, we should be more precise.


> That seems slightly pedantic. While it's true that there's no cross-platform C ABI, most platforms have a C ABI, and it's well-defined (and defined in terms of C data types) on that platform.

Every platform has an ABI, obviously, and yes it's generally formed in view of C.

This doesn't change the fact that the phrase "The C ABI" is, in a vacuum, meaningless and that "The C ABI on Platform X" is just a convoluted, cart-before-the-horse way of saying "Platform X's ABI"


But what an ABI does is specify a binding between language-level concepts and binary representations. That's why mentioning the language matters. If "Platform X's ABI" says that structs are passed on the stack, or ints are a certain size, or pointers have a certain alignment, or whatever, it (usually) means that C structures are passed on the C stack, C ints are a certain size, C pointers have a certain alignment, etc. Another language can use its own data types and even its own concept of a stack. (For instance, Go uses a C-incompatible stack.) And the reason that we care about "the C ABI" is whether the implementation of a language on platform X has data types, stack usage, etc. that matches the implementation of C on platform X.

Put another way, I'm arguing that "C" is a more useful descriptor than "platform X's", because the sentence "Rust is compatible with the C ABI" is short for "forall X, Rust on platform X is compatible with the C ABI on platform X", and means something different from "Rust is compatible with the SysV ABI" (which implies that it follows the SysV ABI on Windows and OS X, too). And since the typical way of doing that is by interfacing with a C compiler or the C library (... and you could make this exact argument about the phrase "C library", I think), it's worth mentioning C. For instance, Vala and Nim, which both compile to C, are always compatible with the C ABI. If you port them to a mainframe they remain compatible with the C ABI, but not with the native ABI.


C is merely flexible, not in some position of authority where it dictates what platforms must do. C would be ABI-compatible on a platform that wasn't designed for it, too (which, historically, was the case on many platforms it was ported to).


Here is how LibreOffice handles this over Unix, Linux, OS X and Windows:

http://opengrok.libreoffice.org/xref/core/include/sal/types....


Even on a platform that wasn't based on C, the C standard is flexible enough to match pretty much any native ABI. That's a small (and under-used) upside to all of that undefined behavior.


Most of the time, you will need a tiny bit of non-standard stuff in your compiler to do so.

For example, I don't think there is a portable way to let your compiler use Pascal calling conventions, or to pass information in specific registers or in processor flags.

As an extreme example, in classic Mac OS, you could register a function to be called for line breaking in text input boxes that had the ABI "Parameters are passed to the routine in registers A3, A4, and D0, and output is returned in the Z flag of the Status Register." (https://developer.apple.com/legacy/library/documentation/mac..., page 2-31 gives several other special-cased ABI's in Mac OS)

Browsing that, I also encountered "The routine follows the C calling conventions employed by the THINK C software development environment. Arguments are passed on the stack from right to left, and a result is returned in register D0."

(Aside: that compiler must have treated vararg functions differently)

So, apparently, one cannot even count on C compilers to push arguments from left to right. So, if your ABI says "push X first, then y", I don't think you can portably call that function from C.


So, apparently, one cannot even count on C compilers to push arguments from left to right.

Right-to-left is pretty much the most sensible way to implement variadic functions with a stack-based calling convention - the argument that the function uses to interpret the rest of them must be found at a known location relative to the stack "top", and due to how stacks work, that implies the varying portion has to be pushed first, thus right-to-left.

Maybe if C decided to design things a little differently:

    int printf(..., const char *format);
    ...
    printf(5, "There are %d lights.\n");
we would instead have right-to-left being the dominant C calling convention.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: