コンテンツにスキップ

最終更新:2026-10-12

C++ string ABI 互換性

プロジェクト実戦の問題:C++ String ABI 互換性問題を避ける

多くの人がC++ stringを好まず、自分で車輪を再発明するのはなぜでしょうか?その問題のひとつがC++ stringのabi問題です。あなたは遭遇したことがありますか?

以前、conanとクロスプラットフォームライブラリを使っていたときにC++ stringのabi互換問題が発生しました。本節ではABIの背景とコンパイラに関連する内容を取り上げ、この穴の避け方について話します。経験があれば、ぜひコメントを~

1. ABI 変更の背景

**GCC 5.1**以降はC++11サポートが強化され、いくつかの新機能が導入されました。このバージョンで、libstdc++は新しいライブラリABIを導入し、std::stringとstd::listの実装を更新しました。これらの変更はC++11標準に準拠するものです。C++11標準はCopy-On-Write文字列を禁止し、リンクリストが自身のサイズを追跡することを要求しています。

実際のプロジェクトでConanを使う場合、開発者は compiler.libcxx を調整することで使用するABIを選択できます:

  • libstdc++:旧ABIを使用。
  • libstdc++11:新ABIを使用。

適切なABIを選択することで、特に異なるバージョンのコンパイラやライブラリを使う場合に、他の依存ライブラリとの互換性を確保できます。

2. コンパイラのデフォルト設定

GCCを使ってコンパイルする際、--with-default-libstdcxx-abi=gcc4-compatible を明示的に指定しなければ、GCCはデフォルトで新しいC++11 ABIを使用します。これは次を意味します:

  • あなたのコードや依存ライブラリが旧ABIでコンパイルされている場合、リンクしようとしたときに未定義シンボルのエラーに遭遇する可能性があります。

例えば、以下のコードをコンパイルするとき:

// lib.cc
#include <string>
void foo(const std::string& s) {}

次のコマンドを使うと:

g++-14 -D_GLIBCXX_USE_CXX11_ABI=0 -c lib.cc

シンボル __Z3fooRKSs が生成されます。これは旧ABIの形式です。

しかし、新ABIを使う別のファイルをコンパイルしてリンクすると、次のようなエラーが出る可能性があります:

g++-14 main.cc lib.o
Undefined symbols for architecture x86_64:
  "foo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)", referenced from:
      _main in ccbWGoYh.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status

3. 解決策

コンパイルを確実に通すために、2つの解決策があります:

方法 1: ABIを手動で指定

すべてのソースファイルをコンパイルするとき、手動で -D_GLIBCXX_USE_CXX11_ABI=0 を追加して古いライブラリと互換性を保ちます。例えば:

g++-14 -D_GLIBCXX_USE_CXX11_ABI=0 main.cc lib.o

方法 2: すべてのファイルを新しいABIでコンパイル

新しいC++11 ABIを使ってよいなら、ライブラリファイルをコンパイルするときに -D_GLIBCXX_USE_CXX11_ABI=1 を使えば、両者のシンボルが一致します:

g++-14 -D_GLIBCXX_USE_CXX11_ABI=1 -c lib.cc
g++-14 main.cc lib.o

このとき、リンク時にシンボルが一致し、未定義シンボルのエラーはもう発生しません。

4. シンボルの比較

nm コマンドで異なるABI下のシンボルを確認すると、両者の違いが直観的にわかります:

g++-14 -D_GLIBCXX_USE_CXX11_ABI=0 -c lib.cc
nm lib.o | grep foo  
# 输出: 0000000000000000 T __Z3fooRKSs

g++-14 -D_GLIBCXX_USE_CXX11_ABI=1 -c lib.cc
nm lib.o | grep foo  
# 输出: 0000000000000000 T __Z3fooRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

5. まとめ

ABIはC++のアップグレードおよびコンパイルの過程で遭遇する非常に大きな穴です。この記事でこれらの内容を明確に説明できていれば幸いです。

関連トピック

コメント