私のオープンソースソフトウェアプロジェクトでは、gccアトミックビルトインを呼び出します:__ sync_add_and_fetchと__sync_sub_and_fetchは、特定の変数にアトミックインクリメントとデクリメントを実装します。コードをコンパイルしようとしている人から定期的にメールが届きますが、次のリンカーエラーが発生します。
refcountobject.cpp:(.text+0xb5): undefined reference to `__sync_sub_and_fetch_4'
refcountobject.cpp:(.text+0x115): undefined reference to `__sync_add_and_fetch_4'
少し掘り下げた後、根本的な原因を絞り込んで、古いバージョンのgcc(4.1)がデフォルトでi386のターゲットアーキテクチャになっているという事実に絞り込みました。そして明らかに、gccは実際には80386でのアトミック加算に固有のものを持っていないので、その場所に未定義の__sync_add_and_fetch_4呼び出しを暗黙的に挿入します。これがどのように機能するかについての素晴らしい説明はここにあります。
ここで説明するように、簡単な回避策は、コンパイラフラグの1つとして-march=pentiumを追加するようにMakefileを変更するように指示することです。そして、すべてが良いです。
では、ユーザーがMakefileを手動で修正する必要がないように、長期的な修正とは何でしょうか。
私はいくつかのアイデアを検討しています:
-march=pentiumをコンパイラフラグとしてMakefileにハードコーディングしたくありません。Intelベースではないものなら何でも壊れると思います。しかし、Makefileにデフォルトのターゲットがi386であることを検出するルールがあれば、確かにそれを追加することができます。私は、gcc -dumpmachineを呼び出し、最初のトリプレットを解析するスクリプトであるルールをMakefileに含めることを考えています。文字列がi386の場合、コンパイラフラグが追加されます。私は、80386マシン用に実際に構築している人はいないと思います。
もう1つの方法は、リンカーがフォールバックするための__sync_add_and_fetch_4の実装を実際に提供することです。定義されているGCC_HAVE_SYNC_COMPARE_AND_SWAPマクロの存在に基づいて条件付きでコンパイルすることもできます。グローバルpthread_mutexを使用して実装のプロトタイプを作成しました。最高のパフォーマンスではない可能性がありますが、問題はうまく機能して解決します。x86用にコンパイルする場合は、実装用に「lockxadd」を呼び出すようにインラインアセンブリを自分で作成することをお勧めします。