Binary compatibility

In general, C++ objects built with IBM® Open XL C/C++ for AIX® 17.1.2 are binary compatible with C++ objects built with IBM XL C/C++ for AIX 16.1.0 invoked by xlclang++.

The exception is that object files created by IBM XL C/C++ for AIX 16.1.0 with the -qpdf1 option, when the compiler is invoked by xlclang or xlclang++, need to be recompiled. This is because the object files make calls to a different PDF library from the ones used in IBM Open XL C/C++. Refer to Profile Guided Optimization (PGO) for more information.

The implementation of the C++11 language standard requires an update to the std library libc++, and causes a breakage in C++ binary compatibility. Therefore, C++ object files built with IBM Open XL C/C++ for AIX 17.1.2 are not directly interoperable with C++ object files generated by IBM XL C/C++ for AIX 16.1.0 that is invoked by xlC or earlier releases. You must recompile your classic programs with IBM Open XL C/C++ for AIX 17.1.2 to solve such binary incompatibility and link object files successfully.
Note: However, the incompatibility does not prohibit coexistence of C++ object files that do not pass objects or exceptions. Refer to the Compatibility with Classic XL C++ object files section for details.
C object files built with IBM Open XL C/C++ for AIX 17.1.2 are binary compatible with C object files built with IBM XL C/C++ for AIX 16.1.0 and earlier releases when both of the following conditions are satisfied:
  • Symbol names with external linkage contain only the dollar sign and characters from the basic character set.
  • Bitfields in C programs are declared only with the types allowed by the C standard regardless of any types allowed as implementation-defined.

Compatibility with Classic XL C++ object files

Refer to the following types of object files as using libc++ ABI, which denotes the C++ standard library implementation on which the object files are based:
  • C++ object files built with IBM Open XL C/C++ for AIX. These object files utilize interfaces provided by C++ runtime libraries libc++.a, libc++abi.a, and libunwind.a, which are from the libc++.rte, libc++abi.rte, and libunwind.rte fileset, respectively.
  • C++ object files built with IBM XL C/C++ for AIX 16.1.0 that is invoked by xlclang++. These object files utilize interfaces provided by C++ runtime libc++.a, which is from the libc++.rte fileset.
Similarly, refer to the following type of object files as using libC ABI:
  • C++ object files built with IBM XL C/C++ for AIX 16.1.0 that is invoked by xlC or earlier releases. The object files utilize interfaces provided by C++ runtime libC.a, which is from the xlC.rte fileset.

Different mangled names
The names of C++ functions and other entities are mangled using different name mangling schemes in libc++ ABI and libC ABI to prevent linking of incompatible code. libc++ ABI utilizes the CXA mangling scheme that is defined in the Itanium C++ ABI specification, while libC ABI uses the IBM proprietary mangling scheme. Consequently, the corresponding symbols for a C++ function in these ABIs are different; for example, the symbol of C++ function func() is _Z4funcv in libc++ ABI, whereas it is func__Fv in libC ABI. In the CXA mangling scheme, mangled names have the _Z prefix.

Different object model and layout
The C++ object model and layout differ between libc++ ABI and libC ABI. Accessing C++ objects from one C++ ABI in code that is compiled in another C++ ABI results in undefined behavior, except where the type is subject to rules for compatibility with C.

Different exception handling
The implementation of exception handling differs between libc++ ABI and libC ABI. There are exception handling limitations when exceptions are thrown from the libc++ ABI side and caught or unwound through the libC ABI side, and vice versa. Refer to Exception compatibility for details.

Due to these differences, libc++ ABI and libC ABI are incompatible and can be considered as different languages, even though both are referred to as C++. However, they can still coexist in the same application with limitations. In such cases, there can be instances of different C++ runtimes coexisting in the same process space without interacting with each other. For example, input and output are buffered separately and are not visible to the other runtime.

Libraries built with coexisting shared objects

Although libc++ ABI and libC ABI are incompatible, you can package their shared objects into a single library so that the library can be used for an application built with either libc++ ABI or libC ABI if all of the following conditions are met:
  • The libc++ ABI or libC ABI shared objects in the library are mutually exclusive.
  • Each shared object in the library that presents a C++ API supports only one of libc++ ABI and libC ABI.
  • Exported symbols from these ABIs are disjoint.
  • There are no cross references between libc++ ABI and libC ABI.

If the libc++ ABI and libC ABI shared objects have cross references, or if there is an executable file referencing symbols from two types of shared objects, the shared objects will be linked with the resulting executable file. This results in two different C++ runtime implementations coexisting in the same process space, which might cause unexpected behavior. To confirm the load dependencies of an executable, use the ldd command.

Mangled C++ symbols in libc++ ABI and libC ABI are disjoint. To prevent symbol name overlapping across ABIs of C/C++ shared objects, it is recommended that symbols such as global variables and C functions be renamed to encode the ABI in the symbol names. For example, you can map a function name to different names either by changing the identifier or by using an asm label. In the following example, name externCFunctionV2 is used for the externCFunction function in libc++ ABI and its original name externCFunction is used in libC ABI.
#if defined(_AIX) && defined(__clang__)
  extern "C" void externCFunction(void) asm("externCFunctionV2");
#else
  extern "C" void externCFunction(void);
#endif

Export only the desired symbols from these shared objects and ensure that exported symbols across C++ ABIs in your shared objects do not overlap. To learn about how to export symbols, refer to Symbol exports and visibilities. When a C++ ABI is no longer needed for linking new applications but you want to retain load compatibility, a shared object can be made load-only with the strip -e command.

Unlike shared objects, where exported symbols are controlled by export lists or attribute visibility, having static archive members for both libc++ ABI and libC ABI in the same archive library might result in unexpected behavior. For example, static constructors for both libc++ ABI and libC ABI are executed by the resulting executable, which might not be expected. It is recommended not to include static archive members for both libc++ ABI and libC ABI in the same archive library unless the behavior is well understood.

Example
The following example shows how to build a dual-ABI library from a single source to support both libc++ ABI and libC ABI:
$ cat build_example.sh
#!/usr/bin/ksh
rm -f xlc.shr.o ibmclang.shr.o libfunc.a xlc.a.out ibmclang.a.out
xlC -qmkshrobj func.cpp -o xlc.shr.o -bE:xlc.exp
ibm-clang++_r -shared func.cpp -o ibmclang.shr.o -bE:ibmclang.exp
ar -v -q libfunc.a xlc.shr.o ibmclang.shr.o
xlC main.cpp -o xlc.a.out libfunc.a -blibpath:.:/usr/lib
ibm-clang++_r main.cpp -o ibmclang.a.out libfunc.a -blibpath:.:/usr/lib

$ cat func.hpp
#include <iostream>

#if defined(_AIX) && defined(__clang__)
extern "C" void bar(void) asm("bar2");
#else
extern "C" void bar(void);
#endif

void func(void);

$ cat func.cpp
#include "func.hpp"

#if defined(_AIX) && defined(__clang__)
#define BUILD_COMPILER "build compiler is ibm-clang++_r"
#else
#define BUILD_COMPILER "build compiler is xlC"
#endif

void func(void)
{
  std::cout << "func(): " << BUILD_COMPILER << std::endl;
}

extern "C" void bar(void)
{
  std::cout << "bar(): " << BUILD_COMPILER << std::endl;
}
$ cat main.cpp
#include "func.hpp"

int main() {
  func();
  bar();
  return 0;
}
$ cat xlc.exp
#!
func__Fv
bar
$ cat ibmclang.exp
#!
_Z4funcv
bar2
$ build_example.sh
$ xlc.a.out
func(): build compiler is xlC
bar(): build compiler is xlC
$ ibmclang.a.out
func(): build compiler is ibm-clang++_r
bar(): build compiler is ibm-clang++_r
$
In this example, the main program built with either the xlC or ibm-clang++_r command can call both the func and bar functions from the dual-ABI library. Shared object ibmclang.shr.o in libfunc.a is built using the IBM Open XL C/C++ compiler. The shared object can be used by both IBM Open XL C/C++ for AIX and IBM XL C/C++ for AIX 16.1.0 invoked by xlclang++. Because ibmclang.shr.o has dependencies on the C++ runtime of the IBM Open XL C/C++ compiler, applications generated by IBM XL C/C++ for AIX 16.1.0 invoked by xlclang++ require the appropriate version of the IBM Open XL C/C++ for AIX runtime to be available on the system. Similarly, ibmclang.shr.o can also be built using IBM XL C/C++ for AIX 16.1.0 invoked by xlclang++ and used by both IBM Open XL C/C++ for AIX and IBM XL C/C++ for AIX 16.1.0 invoked by xlclang++.
You can confirm the load dependencies of your executable using the AIX command ldd. The following is the output from using ldd on xlc.a.out and ibmclang.a.out in the example above:
$ ldd xlc.a.out
xlc.a.out needs:
     ...
     /usr/lib/libC.a(shr.o)
     ...
     /usr/lib/libC.a(shrcore.o)
     /usr/lib/libC.a(ansi_32.o)
     /usr/lib/libC.a(ansicore_32.o)
$ ldd ibmclang.a.out
ibmclang.a.out needs:
     ...
     /usr/lib/libc++.a(shr2.o)
     /usr/lib/libc++abi.a(libc++abi.so.1)
     /usr/lib/libunwind.a(libunwind.so.1)
     /usr/lib/libc++.a(libc++.so.1)
     ...
$

According to the output, xlc.a.out has dependencies on the C++ runtime of the classic XL C/C++ compiler, which is libC.a, but it does not have dependencies on the C++ runtime of the Open XL C/C++ compiler, which are libc++.a, libc++abi.a, and libunwind.a. On the other hand, ibmclang.a.out has dependencies on libc++.a, libc++abi.a, and libunwind.a but does not have dependencies on libC.a.