このブログでは、C++BuilderのリンカでClassicオブジェクトとClangオブジェクトを混在してリンクさせたときに、リンカまたはランタイムエラーを回避するために役立つ情報を解説します。
現在 エンバカデロでは、最新のClangコンパイラの使用を推奨しています。特にWin64向けに行ってきた作業(全く新しいデバッガなど)と、従来のClassicコンパイラ(bcc32)からのアップグレードを容易にするためのRTLの改善を考慮しています。
ただし、多くのお客様は段階を踏んでコンパイラを移行しています。例えば、最初に従来のClassicWin32(bcc32)からClang Win32(bcc32c)へ移行し、その後にWin64(bcc64)へ移行します。エンバカデロでも実際に、このマイグレーションプランをよく勧めております。
しかしながら、ClassicコンパイラからClangコンパイラへ移行する場合は一般的な問題があります。
2 つのWin32 コンパイラ(Classic (bcc32)および Clang 拡張(bcc32c/x))でビルドされたオブジェクトファイル (.objおよび.libライブラリ)は、お互いに互換性がありません。 これらはバイナリレベルで非互換です。 異なるコンパイラで一緒にビルドされた実行可能ファイルまたは静的ライブラリといった、バイナリまたはオブジェクトをリンクしようとすると、リンク時または実行時に、数多くのエラーが発生する可能性があります。(ClassicおよびClangは両方とも、Win32 に対してDelphiで生成されたオブジェクトファイルに対応しています。しかしながら、ClassicとClang はお互いに互換性がありません。 )
ただし、このようなオブジェクトの混在は、旧バージョンのプロジェクトを移行するときや、プロジェクトに新しいライブラリやEXEを追加するときに、簡単に行う事ができてしまいました。
そのため10.3.3以降では、これらの状況を検出するために役立つ新しいリンカの警告を追加しました。詳しくは、docwikiのオンラインヘルプを参照してください。
では、解決策を示す前に、ビルドでこのエラーが発生した場合に何が起こる可能性があるかを簡単に見てみましょう。
Table of Contents
コンパイラが一致しない場合の問題
C++Builderの以前のバージョンで、リンカ/コンパイラ検知が C++Builder 10.3.3で追加される前は、以下のエラーが発生することが、典型的なコンパイラ不一致の問題を示唆するものでした。
リンク時エラー
Clangコンパイラ(bcc32c/x)のツールチェーンでビルドされたアプリケーションが、従来のコンパイラでビルドされたライブラリに対してリンクされている場合、次のようなエラーが表示される場合があります。
[crayon-67408774c41eb372389987/]また従来のコンパイラ(bcc32)のツールチェーンでビルドされたアプリケーションがClangコンパイラでビルドされたライブラリに対してリンクされている場合、次のようなエラーが表示される場合があります。
[crayon-67408774c4217496307017/]実行時エラー
C++Builderの10.3.3より前のバージョンでは、コンパイラを識別するマーカーが無いため、状況によっては、リンカエラーが全く表示されず、アプリケーションが実行されることもあります。コード自体は問題ないように見えますが、実際には、メモリレイアウト、配置、ランタイムライブラリの期待値、例外処理など、2つの異なるコンパイラは、内部的に異なる方法で処理を実行しているため、それが原因でアプリケーションは実行時に、ある時点でクラッシュします。
10.3.3以降のメッセージには「Warning: Compiler mismatch」と表示され、期待された内容と検出された内容が示されます。以下はその例です。
[crayon-67408774c422a191594179/]では、リンカはどのように混合オブジェクトをチェックし、何を期待するかをどのように知るのでしょうか?
上記のエラーは、静的ライブラリにリンクしているVCLアプリケーションで発生しているメッセージの例です。
このプロジェクトでは、メインとなるVCLアプリケーションのプロジェクトはClangコンパイラでビルドされ、静的ライブラリのプロジェクトはClassicコンパイラでビルドされています。 コード内でのリンクは、次のとおりです。
[crayon-67408774c422d367865651/]詳しくは、#pragma commentと#linkのリンクに関するドキュメントを参照してください。
エンバカデロのコンパイラは、各オブジェクトにどのコンパイラでビルドされたかを示すレコード(小さなデータ)を出力します。以下の図は、それぞれのコンパイラでビルドしたオブジェクトファイル(.obj)に埋め込まれているコンパイラを判別するコードをバイナリエディタで比較した例です。
Clangコンパイラの場合は、バイナリコードの中に”Clang32″という文字列(コンパイラマーカー)が埋めこまれています。
リンカは、リンク付けする際に見た最初のオブジェクトで見つけた上記のコンパイラマーカーを確認し、異なるオブジェクトを見つけた場合には、フラグを立てます。そしてリンクされた各オブジェクトに対して、期待するコンパイラでビルドされたかどうかをチェックしています。そして、もし期待しないコンパイラによってビルドされている場合は、上記のリンカ警告が表示されます。
まとめ
- Win32のClangコンパイラとClassicコンパイラでビルドしたオブジェクトファイルは、バイナリレベルで非互換
- もしコンパイラが異なるオブジェクトファイル同士を混在させてリンクされている場合は、リンカ警告が表示される
- もしリンカ警告が表示された場合は、どちらか片方のコンパイラに統一してビルドし直すことによって、リンカエラーやアプリケーション実行時の安定性の問題が改善できる
参考情報
こちらのdocwikiページでは、このブログで取り上げていない情報も説明されています。もし興味があれば、是非ご一読してください。