Thoroughly Understanding C++ ABI

rramadass1 pts0 comments

Thoroughly Understanding C++ ABI | ykiko's blog

Table of Contents

CPU & OS

Object File Format

Data Representation

Function Calling Convention

C++ Standard

Explicit Object Parameter

Static Operator()

Compiler Specific

De Facto Standard

Workaround

Important Options

Runtime & Library

User Code

extern “C”

Conclusion

This article was translated by AI using Gemini 2.5 Pro from the original Chinese version. Minor inaccuracies may remain.

Application Binary Interface, or ABI as we commonly call it, is a concept that feels both familiar and unfamiliar. Familiar in what sense? It’s often discussed when troubleshooting, frequently mentioned in articles, and sometimes we even have to deal with compatibility issues it causes. Unfamiliar in what sense? If someone asks you what an ABI is, you’ll find that you know what it’s about, but describing it in precise language is quite difficult. In the end, you might just resort to saying, as WIKI does: an ABI is an interface between two binary program modules. Is there a problem with that? No, as a general description, it’s sufficient. But it can feel a bit hollow.

This situation is not uncommon in the field of Computer Science. The author encountered the exact same situation in a previous article discussing reflection. Fundamentally, CS is not a discipline that strives for absolute rigor; many concepts lack strict definitions and are more often conventional understandings. So, instead of getting bogged down in definitions, let’s look at what these so-called binary interfaces actually are and what factors affect their stability.

CPU & OS#

The final executable file ultimately runs on a specific operating system on a specific CPU. If the CPU instruction sets are different, it will certainly lead to binary incompatibility. For example, programs on ARM cannot run directly on x64 processors (unless some virtualization technology is used). What if the instruction sets are compatible? For instance, x64 processors are compatible with the x86 instruction set. Does that mean an x86 program can definitely run on an x64 operating system? This is where the operating system comes into play. Specifically, factors such as Object File Format , Data Representation , Function Calling Convention , and Runtime Library must be considered. These points can be regarded as ABI regulations at the operating system level. We will discuss the fourth point in a dedicated section later. Below, taking the x64 platform as an example, we will discuss the first three points.

x64, x86-64, x86_64, AMD64, and Intel 64 all refer to the 64-bit version of the x86 instruction set.

There are two main common ABIs on the x64 platform :

Windows x64 ABI for 64-bit Windows operating systems

x86-64 System V ABI for 64-bit Linux and various UNIX-like operating systems

Calling a function from a dynamic library can be simply viewed as the following three steps:

Parse the dynamic library according to a certain format.

Look up the function address from the parsed result based on the symbol name.

Pass function parameters and call the function.

Object File Format#

How to parse a dynamic library? This is where the ABI’s regulations on Object File Format come into play. If you want to write your own linker, the final executable file must meet the format requirements of the corresponding platform. Windows x64 uses the PE32+ executable file format, which is the 64-bit version of PE32 (Portable Executable 32-bit). The System V ABI uses the ELF (Executable Linkable Format) executable file format. By using parsing libraries (or writing your own if interested), such as pe-parse and elfio, to parse actual executable files and obtain their symbol tables, we can get the mapping between function names and function addresses.

Data Representation#

After obtaining the function address, the next step is how to call it. Before calling, parameters must be passed, right? When passing parameters, special attention must be paid to the consistency of Data Representation. What does this mean?

Suppose I compile the following file into a dynamic library:

struct X{<br>int a;<br>int b;<br>};

int foo(X x){<br>return x.a + x.b;

Then, a subsequent version upgrade changes the structure content, and the structure definition seen in the user&rsquo;s code becomes:

struct X{<br>int a;<br>int b;<br>int c;<br>};

And then it still tries to link to the dynamic library compiled from the old version code and call its function:

int main(){<br>int n = foo({1, 2, 3});<br>printf("%d\n", n);

Will it succeed? Of course, it will fail. This type of error can be considered a so-called ODR (One Definition Rule) violation. More examples will be discussed in later sections.

The above situation is an ODR violation caused by the user actively changing the code. But what if I don&rsquo;t actively change the code, can I ensure the stability of the structure layout? This is guaranteed by the Data Representation in the ABI. For...

function file format rsquo library executable

Related Articles