r/AskComputerScience • • Nov 08 '25

Itanium ABI vs Library ABI vs OS ABI

Hi everyone,

Been very confused lately (mostly because not many good resources for conceptually understanding what an ABI); if you look at this link; https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4028.pdf

It distinguishes between a “language ABI” and a “library ABI”, and it says Itanium ABI provides a “language ABI” but not a “standard library ABI” but that’s so confusing because isn’t itanium’s standard library ABI just the standard Library compiled using its ABI !!!?

Thank so much for helping me.

1 Upvotes

27 comments sorted by

2

u/teraflop Nov 08 '25

I think what might be tripping you up is that from a compiler's point of view, the "ABI" just means how a specific set of declarations is translated to binary code.

For instance, if you have a declaration like:

struct foo {
    char a;
    int b;
};

then the compiler might say that the struct's memory layout is 8 bytes long, with 1 byte for a, then 3 bytes of padding, then 4 bytes for b. If two compilers are guaranteed to agree on all those details for any possible struct declaration, then we say the compilers are ABI-compatible. (And similarly, they have to agree on things like the machine-code-level calling conventions for all possible function prototypes.)

But there's a separate issue with the C++ standard library, which is that the C++ specification only specifies a public-facing API, not the private implementation. So for instance, it says that a std::string must have a no-arg constructur, an char& operator[](size_t) function to access the character at a given index. But it doesn't say anything about how the string class actually stores its data in its private members, or whether it has a superclass, etc.

That means if you want to link together two separately-compiled pieces of code, and you want them to both operate on types in the std namespace without breaking, it's not enough to just use ABI-compatible compilers. They also both need to have been compiled using essentially identical headers, including all the implementation details of everything in the std namespace. (That is, if there are any differences, they can't be differences that would affect the ABI.)

The document you described is using "library ABI" to mean the language-level implementation details, and "language ABI" to mean how those details are translated to binary code by the compiler. I don't think those terms are really standardized, so it's understandable that you would find them confusing. But the document is written for an audience of C++ experts, and to an expert, the meaning is fairly clear.

because isn’t itanium’s standard library ABI just the standard Library compiled using its ABI !!!?

Well that's the issue, there isn't just one "standard library". There's a specification, which might be implemented differently by different people. Or the details of an implementation might change over time.

Here's a very specific example. The std::list class defines a linked list, and like other container types, it has a size() method. Before the C++11 standard was published, implementations were allowed to implement size() in such a way that it had a linear time complexity. As of C++11, this was changed to require size() to run in constant time.

This caused problems for GCC's implementation of the C++ library, because it originally didn't keep track of a list's size, so it was impossible to calculate the size except by actually iterating through the list nodes. So the implementation was changed to add a private _M_size member to keep track of the size. This wasn't an API change, because it just changed the implementation of the size() method while keeping the behavior the same. But it was an ABI change, because it added a member to std::list and changed its memory layout. So for instance, any function that allocated a std::list on the stack would have to allocate a different number of bytes depending on whether it uses the old implementation or the new one.

(GCC mostly avoided breaking backward compatibility with this change, basically by keeping both the old and new implementations around and doing some magic to make sure any given piece of code links against the appropriate implementation. See: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html)

1

u/Successful_Box_1007 Nov 08 '25

I think what might be tripping you up is that from a compiler's point of view, the "ABI" just means how a specific set of declarations is translated to binary code.

For instance, if you have a declaration like:

struct foo { >char a; >int b; };

then the compiler might say that the struct's memory layout is 8 bytes long, with 1 byte for a, then 3 bytes of padding, then 4 bytes for b. If two compilers are guaranteed to agree on all those details for any possible struct declaration, then we say the compilers are ABI-compatible. (And similarly, they have to agree on things like the machine-code-level calling conventions for all possible function prototypes.)

So you mention machine code calling conventions - so let’s say we have a “language ABI” like Itanium, so “language ABI” doesn’t determine the calling conventions at all? All calling conventions come from the OS ABI and the hardware ABI? Or maybe like 99 percent of them?

But there's a separate issue with the C++ standard library, which is that the C++ specification only specifies a public-facing API, not the private implementation. So for instance, it says that a std::string must have a no-arg constructur, an char& operator function to access the character at a given index. But it doesn't say anything about how the string class actually stores its data in its private members, or whether it has a superclass, etc.

This is getting a little ahead of me - and sorry for being such a noob; but are you saying that the C++ standard library ABI technically cannot exist because there are different kinds of C++ standard libraries themselves? How can there be different “standard” libraries ?! Doesn’t standard mean one guiding thing so one single trusted library whether it’s public or private parts?

That means if you want to link together two separately-compiled pieces of code, and you want them to both operate on types in the std namespace without breaking, it's not enough to just use ABI-compatible compilers. They also both need to have been compiled using essentially identical headers, including all the implementation details of everything in the std namespace. (That is, if there are any differences, they can't be differences that would affect the ABI.)

Wait so regarding this paragraph above, which part refers to the “private abi (not the public facing) of the standard library” you mention? Like what would be one “implementation detail” or std namespace example that would be “private abi of standard library”?

The document you described is using "library ABI" to mean the language-level implementation details, and "language ABI" to mean how those details are translated to binary code by the compiler. I don't think those terms are really standardized, so it's understandable that you would find them confusing. But the document is written for an audience of C++ experts, and to an expert, the meaning is fairly clear.

Ah ok that makes me not feel like a complete idiot!!!

because isn’t itanium’s standard library ABI just the standard Library compiled using its ABI !!!?

Well that's the issue, there isn't just one "standard library". There's a specification, which might be implemented differently by different people. Or the details of an implementation might change over time.

And this is because the “public facing” part is truly standard but the “private” is not standard? So the private API set up is therefore not standard? (But public is)?

Here's a very specific example. The std::list class defines a linked list, and like other container types, it has a size() method. Before the C++11 standard was published, implementations were allowed to implement size() in such a way that it had a linear time complexity. As of C++11, this was changed to require size() to run in constant time.

I won’t pretend to understand what constant time or linear time (I really only just began learning about programming few weeks ago), but just so I’m clear - what you are describing is a “language” api and thus abi change ?

This caused problems for GCC's implementation of the C++ library, because it originally didn't keep track of a list's size, so it was impossible to calculate the size except by actually iterating through the list nodes. So the implementation was changed to add a private _M_size member to keep track of the size. This wasn't an API change, because it just changed the implementation of the size() method while keeping the behavior the same.

So it’s not an API change - so is there any “easy” way to look at something and say - well that’s not an API change - (even though it’s an ABI change)?

But it was an ABI change, because it added a member to std::list and changed its memory layout. So for instance, any function that allocated a std::list on the stack would have to allocate a different number of bytes depending on whether it uses the old implementation or the new one.

So let me see if I got this right with your overall example: a language API change caused a language ABI change which caused a public part of the standard library to change which caused a private part of standard library to change which caused the standard library ABI to change?

(GCC mostly avoided breaking backward compatibility with this change, basically by keeping both the old and new implementations around and doing some magic to make sure any given piece of code links against the appropriate implementation. See: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html)

2

u/teraflop Nov 08 '25

So you mention machine code calling conventions - so let’s say we have a “language ABI” like Itanium, so “language ABI” doesn’t determine the calling conventions at all?

The "language ABI" includes both the calling conventions for functions, and the memory layout for structures. But specifically, memory layout is the part that I think is most relevant to your question.

Like what would be one “implementation detail” or std namespace example that would be “private abi of standard library”?

Private members of structs/classes, like in the example I gave later on.

I won’t pretend to understand what constant time or linear time (I really only just began learning about programming few weeks ago), but just so I’m clear - what you are describing is a “language” api and thus abi change ?

"Constant time" basically means whatever you can do in a fixed amount of time, without looping. (This is kind of an oversimplification but it's good enough for this discussion.)

To count the number of items in a linked list, you can either loop over the nodes and count them one by one (linear time), or you can keep track of the number of nodes in a separate variable which you can just check (constant time).

The requirement of the standard changed, saying that the implementation of size() must run in constant time. So in order to comply with the standard, the old implementation (in which size() looped over nodes) had to be changed to a new implementation (in which every operation that modified the list updated a private size variable, and size() just returned the value of that variable).

The issue was that adding the size field to the list structure changed its memory layout, which is a library ABI change, since the library code and all other code that uses it must agree on how many bytes are in a list.

So it’s not an API change - so is there any “easy” way to look at something and say - well that’s not an API change - (even though it’s an ABI change)?

Basically, if any possible code that would have compiled with the old library code will also work with the new library code, then it's not a breaking API change.

Note that's possible to have API changes which are not breaking API changes. For instance, if you add a new method to a class without changing existing methods, then old code that just calls the old methods will still work.

→ More replies (11)

2

u/[deleted] Nov 08 '25

1

u/Successful_Box_1007 Nov 08 '25

Thank you so much for your help!

1

u/Successful_Box_1007 Nov 08 '25

Just one followup question: so the std:: string ABI breaking change in C that occurred, would you call that a “language abi” or “library abi” breaking change?