1. ホーム
  2. c++

[解決済み] GCCの-Wpsabiオプションはいったい何をするのでしょうか? それを抑制することの意味は何ですか?

2023-04-28 19:42:19

質問

背景

昨年は nlohmann json library を使っていました。 [1] を使用しており、GCC 5.x を使用して x86_64 でクロスコンパイルしていました。 arm-linux-gnueabi-* を使用して、警告なしでクロスコンパイルしていました。私がGCCを新しいバージョンにアップデートすると、GCCは不可解な診断ノートのページを生成しました。たとえば、以下はそのノートの 1 つです。

In file included from /usr/arm-linux-gnueabi/include/c++/7/vector:69:0,
             from include/json.hpp:58,
             from src/write_hsi.cpp:23:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long int, long long unsigned int, double, std::allocator, nlohmann::adl_serializer>}; _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]’:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc:394:7: note: parameter passing for argument of type ‘std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >::iterator {aka __gnu_cxx::__normal_iterator<nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >}’ changed in GCC 7.1
   vector<_Tp, _Alloc>::
   ^~~~~~~~~~~~~~~~~~~
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc: In member function ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer> nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser::parse_internal(bool) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]’:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc:105:21: note: parameter passing for argument of type ‘__gnu_cxx::__normal_iterator<nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >’ changed in GCC 7.1
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

解決策を見つけるのは簡単でした。 -Wno-psabi をコンパイラのオプションに追加することです。実際、これがライブラリに実装された修正でした。 [2]

Application Binary Interfaces (ABI) と processor-specific ABI (psABI) の基本は理解できたのですが、どのようにすればよいのでしょうか。参考までに、この回答

[11] が ABI の簡単な概要を説明しています。

ABI ( アプリケーションバイナリインターフェース ) は、高級言語における低レベルの概念と、特定のハードウェア/OS プラットフォームのマシンコードの能力との間のマッピングを定義した規格です。以下のようなものが含まれます。

  • どのように C/C++/Fortran/... データ型 がメモリ上にどのように配置されるか(データサイズ/アラインメント)。
  • どのようにネストされた 関数呼び出し の動作(関数の呼び出し元に戻るための情報がどこに、どのように格納されているか、CPUのレジスタやメモリ内の関数引数がどこに渡されているか)。
  • どのように プログラムの起動/初期化 がどのように動作するか(実行ファイルがどのようなデータ形式を持っているか、そこからどのようにコードやデータがロードされるか、DLLがどのように動作するか......)。

これらに対する回答は

  • 言語固有 (従って、C ABI、C++ ABI、Fortran ABI、Pascal ABI、...さらに Java バイトコード仕様も、実際のハードウェアではなく "virtual" プロセッサをターゲットにしているものの、ABI です) 。
  • オペレーティング システム固有 (同じハードウェア上の MS Windows と Linux は異なる ABI を使用します)。
  • ハードウェア/CPU 固有の (ARM と x86 の ABI は異なります)。
  • 長い)時間をかけて進化している (既存の ABI は、新しい CPU の機能を利用できるようにしばしば更新/改訂されてきました。たとえば、x86 SSE レジスタをアプリケーションで使用する方法を指定することは、当然ながら CPU が が持っていた このため、既存の ABI は明確にされる必要がありました)。

つまり、ABI は包括的なコンポーネントであり、そのコンポーネントの 1 つ ("hardware/CPU-specific" 詳細) が psABI です。

私の問題点

私が抱えている問題は

  1. 意味を理解せずに普遍的に警告を無効にするのは好きではありません。
  2. アドバイス "を使用する -Wno-psabi を使用してメモを消してください" というアドバイスは、コンパイラのアップグレード後に突然現れるこの種の診断メモに対するかなり一般的なアドバイスのように思われます。 [2] [3] [4] GCC開発者の一人ですら、こうすることを推奨しています。 [5]
  3. どちらも -Wpsabi-Wno-psabi は文書化されている [6] で、GCC マニュアルにあります。 [7]

その結果、私は具体的に何がどうなっているのかよくわかりません。 -Wno-psabi が影響を与えるもの、与えないものについてはよくわかりません。関連するオプション -Wabi を文書化したものです。 [8]

-Wabi (C, Objective-C, C++ and Objective-C++ only)

G++がベンダーニュートラルなC++ ABIとおそらく互換性のないコードを生成する場合に警告を出す...

また、psABI 関連の変更についても警告します。現時点での既知の psABI の変更点は以下のとおりです。

  • SysV/x86-64 では、long double のメンバーを持つユニオンは psABI で指定されたとおりにメモリ内で渡されます。たとえば、以下のようになります。

union U { long double ld; int i; };

union U は常にメモリで渡されます。

私の理解では、このすべては

  1. -Wabi は、psABIの変更があった場合に警告を発生させます。
  2. GCC 7 は ABI バグを修正しました。 [9] GCC 5 で導入され、ARM ターゲットに影響を与えるバグを修正しました。
    • リリースノートには、「これは ABI の変更です」と記載されています。 [10]
    • なぜかリリースノートには、関連する診断ノートが、文書化されていない -Wpsabi を使用したときに生成され、ドキュメント化された -Wabi .
    • この ABI の変更については、マニュアルには記載されていません。
  3. "これは ABI 変更です" と "使用しますをまとめると、以下のようになります。 -Wpsabi "を合わせると、どうやらこれは 特に psABIの変更であり、異なる種類のABIの変更ではありません。(実際には、psABI 自体ではなく、psABI の GCC の実装における変更です)。

ドキュメントが常に最新ではないこと、特に、既知のドキュメント化されていないオプションであるものについてはそうであることは分かっています。しかし、私が懸念しているのは、"use -Wno-psabi は、これらの不可解な診断メモのいくつかの異なる種類の標準的な応答のようです。しかし、私の ABI に関する基本的な理解では、ABI の変更は大きな問題ではないのでしょうか?メッセージを消すのではなく、ABIの変更に関心を持つべきではないでしょうか?文書化されていないものと、ABI 対 psABI の細かい部分との間で、私はよく分からないのですが......。

例えば、もし私が -Wno-psabi を追加し、これらのノートを消した場合、将来的に別の ABI 変更があり、その際に を行います。 が私のプロジェクトに影響を与えたらどうなるでしょうか?重要かもしれない将来の警告や注釈を事実上黙殺してしまったのでしょうか?

また、"すべてのコードを再コンパイルすれば、何も心配することはないと言われているにもかかわらず、"

[5] すべてのコードとは正確には何ですか。私のソース コードですか? glibc ですか? 私が使用している他のシステム全体の共有ライブラリですか?

リファレンス

  1. https://github.com/nlohmann/json
  2. https://github.com/nlohmann/json/issues/658
  3. https://stackoverflow.com/a/48149400
  4. https://stackoverflow.com/a/13915796/10270632
  5. https://gcc.gnu.org/ml/gcc/2017-05/msg00073.html
  6. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81831
  7. https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc
  8. https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/C_002b_002b-Dialect-Options.html
  9. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77728
  10. https://gcc.gnu.org/gcc-7/changes.html
  11. https://stackoverflow.com/a/8063350

どのように解決するのですか?

ABI を気にする必要があるのは、ライブラリの境界を越えるときだけです。自分自身のアプリケーション/ライブラリの中では、おそらくすべてのオブジェクト ファイルが同じコンパイラー バージョンとスイッチでコンパイルされているため、ABI は実際には重要ではありません。

ABI1 でコンパイルされたライブラリと ABI2 でコンパイルされたアプリケーションがある場合、アプリケーションはライブラリから関数を呼び出そうとすると、引数を正しく渡せないのでクラッシュします。クラッシュを修正するには、ライブラリ (および依存する他のすべてのライブラリ) を ABI2 で再コンパイルする必要があります。

あなたの特定のケースでは、アプリケーションと同じコンパイラー バージョンを使用して nlohmann をコンパイルする限り (または nlohmann をヘッダーとして使用するだけ)、ABI 変更を心配する必要はありません。

警告をグローバルに抑制することは、将来の ABI 問題が見えなくなるため、危険なオプションであると思われます。より良いオプションは #pragma を使用して、問題のある関数に対してのみ警告を無効にすることです。

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-psabi"
void foo()
{
}
#pragma GCC diagnostic pop