GCC C++ Dual ABI

From Alliance Doc
Jump to navigation Jump to search
This site replaces the former Compute Canada documentation site, and is now being managed by the Digital Research Alliance of Canada.

Ce site remplace l'ancien site de documentation de Calcul Canada et est maintenant géré par l'Alliance de recherche numérique du Canada.

Other languages:


The transition from GCC version 4.9 to version 5.1 introduced a major change to its application binary interface (ABI). If all source code including all dependent libraries is recompiled using the same version of the compiler then there will be no issues. If different compilers are used, the ABI change may cause linking to fail. Failure is likely if you are linking to precompiled libraries provided in a vendor's product. If this occurs, you can use GCC's Dual ABI[1] feature to tell GCC to use the old ABI in order for your application to link properly with those legacy libraries, e.g., you would pass -D_GLIBCXX_USE_CXX11_ABI=0 to GCC if using GCC v5.1 or higher to link to libraries built using the older ABI.

In some cases, using -D_GLIBCXX_USE_CXX11_ABI=0 will resolve errors like this one:

 .../extensions.hpp(38): error: “std::list” is ambiguous

The class could be anything, not necessarily std::list.

Application Binary Interface (ABI)[edit]

By default, C++ compilers mangle and export all extern "C++" symbol names using a compiler-specific algorithm. These symbol names comprise part of the the Application Binary Interface (ABI) used to link the compiled code with libraries. Although many compiler vendors make efforts to use the same ABI on a given platform (e.g., Linux), ABI changes are needed from time-to-time. This means any change to a compiler's ABI can result in programs failing to link.

An Example[edit]

The following example illustrates how options passed to GCC v5.1 or higher can affect the ABI used in the generated binaries. In order for these binaries to be successfully linked to form the final program/library all symbol names must match up --if they do not, then linking will fail. This example shows how the ABI's exported symbol names generated by the compiler differ with different compiler settings.

Source Code[edit]

The following C++ program will be compiled to generate the ABI symbol names contained within it:

File : main.cxx

#include <iostream>
#include <string>

int main()
{
  using namespace std;
  string mystring = "Hello World!";
  cout << mystring << endl;
}


The above program will need to be compiled using a number of different settings to show how the ABI calls differ. The following makefile is used to generate such:

File : Makefile

all: \
        main-cxx98.o main-cxx98-newabi.o main-cxx98-oldabi.o \
        main-cxx11.o main-cxx11-newabi.o main-cxx11-oldabi.o \
        diff-cxx98 diff-cxx11 diff-cxx-default

clean:
        rm -f *.o *.ii *.s
        rm -f *.symbols

main-cxx98.o: main.cxx
        $(CXX) -c -o $@ -std=c++98 $<
        nm $@ | cut -c 20- >$@.symbols

main-cxx98-newabi.o: main.cxx
        $(CXX) -c -o $@ -std=c++98 -D_GLIBCXX_USE_CXX11_ABI=1 $<
        nm $@ | cut -c 20- >$@.symbols

main-cxx98-oldabi.o: main.cxx
        $(CXX) -c -o $@ -std=c++98 -D_GLIBCXX_USE_CXX11_ABI=0 $<
        nm $@ | cut -c 20- >$@.symbols

main-cxx11.o: main.cxx
        $(CXX) -c -o $@ -std=c++11 $<
        nm $@ | cut -c 20- >$@.symbols

main-cxx11-newabi.o: main.cxx
        $(CXX) -c -o $@ -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=1 $<
        nm $@ | cut -c 20- >$@.symbols

main-cxx11-oldabi.o: main.cxx
        $(CXX) -c -o $@ -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0 $<
        nm $@ | cut -c 20- >$@.symbols

diff-cxx98: main-cxx98-newabi.o main-cxx98-oldabi.o
        @echo "=============================================================================="
        @echo "Difference between the old and new C++ ABIs with -std=c++98..."
        diff --suppress-common-lines main-cxx98-oldabi.o.symbols main-cxx98-newabi.o.symbols ||true

diff-cxx11: main-cxx11-newabi.o main-cxx11-oldabi.o
        @echo "=============================================================================="
        @echo "Difference between the old and new C++ ABIs with -std=c++11..."
        diff --suppress-common-lines main-cxx11-oldabi.o.symbols main-cxx11-newabi.o.symbols || true

diff-cxx-default: main-cxx98.o main-cxx11.o
        @echo "=============================================================================="
        @echo "Difference between -std=c++98 and -std=c++11 ABI w/o _GLIBCXX_USE_CXX11_ABI..."
        diff --suppress-common-lines main-cxx98.o.symbols main-cxx11.o.symbols || true


NOTE: Before running make, ensure you have loaded a module for GCC v5.1 or higher.

Running the Example[edit]

Executing the make file will demonstrate the differences in the produced ABI symbols with different GCC compiler options. The following is sample output of running make on the above Makefile:

File : Makefile

$ make
c++ -c -o main-cxx98.o -std=c++98  main.cxx
nm main-cxx98.o | cut -c 20- >main-cxx98.o.symbols
c++ -c -o main-cxx98-newabi.o -std=c++98 -D_GLIBCXX_USE_CXX11_ABI=1  main.cxx
nm main-cxx98-newabi.o | cut -c 20- >main-cxx98-newabi.o.symbols
c++ -c -o main-cxx98-oldabi.o -std=c++98 -D_GLIBCXX_USE_CXX11_ABI=0  main.cxx
nm main-cxx98-oldabi.o | cut -c 20- >main-cxx98-oldabi.o.symbols
c++ -c -o main-cxx11.o -std=c++11  main.cxx
nm main-cxx11.o | cut -c 20- >main-cxx11.o.symbols
c++ -c -o main-cxx11-newabi.o -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=1  main.cxx
nm main-cxx11-newabi.o | cut -c 20- >main-cxx11-newabi.o.symbols
c++ -c -o main-cxx11-oldabi.o -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0  main.cxx
nm main-cxx11-oldabi.o | cut -c 20- >main-cxx11-oldabi.o.symbols
==============================================================================
Difference between the old and new C++ ABIs with -std=c++98...
diff --suppress-common-lines main-cxx98-oldabi.o.symbols main-cxx98-newabi.o.symbols || true
7,8c7,8
< _ZNSsC1EPKcRKSaIcE
< _ZNSsD1Ev
---
> _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_
> _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev
14c14
< _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E
---
> _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE
==============================================================================
Difference between the old and new C++ ABIs with -std=c++11...
diff --suppress-common-lines main-cxx11-oldabi.o.symbols main-cxx11-newabi.o.symbols || true
7,8c7,8
< _ZNSsC1EPKcRKSaIcE
< _ZNSsD1Ev
---
> _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_
> _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev
15c15
< _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E
---
> _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE
==============================================================================
Difference between -std=c++98 and -std=c++11 ABI w/o _GLIBCXX_USE_CXX11_ABI...
diff --suppress-common-lines main-cxx98.o.symbols main-cxx11.o.symbols || true
12a13
> _ZStL19piecewise_construct
$


i.e.,

  • in the last difference example if one only uses -std=c++98 or -std=c++11 the ABI does not differ --the compiler will use the default (i.e., newer) ABI
  • in previous two difference examples, the _GLIBCXX_USE_CXX11_ABI is set to 0 (old ABI) or 1 (new ABI) along with using -std=c++98 or -std=c++11 --which causes the old or new ABI to be used.

Thus, to link to binaries compiled using the old ABI when using GCC v5.1 or higher, one will need to specify _GLIBCXX_USE_CXX11_ABI=0 when compiling the C++ code.

References[edit]

  1. Free Software Foundation. The GNU C++ Library, Chapter 3. https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html