GCC C++ Dual ABI
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)
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
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
The following C++ program will be compiled to generate the ABI symbol names contained within it:
#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:
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
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
:
$ 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 to0
(old ABI) or1
(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
- ↑ Free Software Foundation. The GNU C++ Library, Chapter 3. https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html