プロンプトとスコープ
Rust 1.97では、stableにおいてRustのv0シンボルマングリングがデフォルトで有効化されます。このフォーマットはジェネリクスのインスタンス化などの情報を可逆的に表現できますが、安定したRust ABIではなく、標準化されたデマングル出力もありません。古いレガシー方式はnightlyのフォールバックとしてのみ利用可能です。ワークスペースには古いツール、インクリメンタルキャッシュ、事前ビルドされたライブラリ、C FFIがまだ残っていると仮定します。シンボル検査、クラッシュ解析、リリースのための安全な移行を設計してください。
これはコーディングおよびツールチェーンに関する質問であり、v0の文法を暗記することを求めるものではありません。インフラストラクチャ、コンパイラツール、パフォーマンス解析、ネイティブ依存関係を扱う職種に適しています。重要なのは、どの名前が変更される可能性があり、どの外部契約を変更してはならず、再現可能なビルドで移行をどのように検証するかを特定することです。
面接官が評価するポイント
#[no_mangle]、#[export_name]、またはextern宣言で公開されたFFI名と、Rust内部シンボルを区別できるか?- v0の可読性、ジェネリクス情報、前方互換性の話を、安定したABIではないことを認めつつ説明できるか?
- 単にコンパイラをアップグレードするだけでなく、ツールの互換性、キャッシュの分離、混在成果物の検出、ロールバックを設計できるか?
- サンプルバイナリ、デバッガ、デマングラー、シンボルのdiffを用いて実際の影響を検証できるか?
不十分な回答は「デマングラーを更新する」とだけ答えます。優れた回答は、シンボルのコンシューマーをマッピングし、互換性ウィンドウと不変条件を定義し、古い成果物、新しい成果物、クロスプラットフォームリリースをテストします。
最初に明確にすべき質問
- どのコンシューマーがシンボルを読み取りますか:デバッガ、プロファイラ、クラッシュコレクター、サイズアナライザー、ビルドキャッシュ、またはスクリプトですか?サポートするフォーマットが異なる場合があります。
- 移行はソースからの再ビルドですか、それとも既存の
.a、.so、または.rlibファイルをリンクし続ける必要がありますか?前者は統一できますが、後者は明示的な互換性ウィンドウと再ビルド境界が必要です。 - FFIはプライベートなRustシンボルに依存していますか?Cが安定したエクスポート名でリンクしている場合は、その明示的な名前を維持してください。プライベートなRustマングルシンボルをABIとして扱ってはなりません。
- 古いクラッシュレポートはデコード可能な状態を維持する必要がありますか?その場合、シンボルサーバーとデマングラーには、新旧両方のフォーマットに対応するbuild-IDルーティングが必要です。
30秒での回答
「まずシンボルのコンシューマーと契約をマッピングします。Rust内部シンボルはコンパイラによって変更される可能性がありますが、FFIエクスポートは明示的な名前で保護される必要があります。次に、コンパイラ、リンカ、デマングラー、デバッガ、キャッシュを再現可能なマトリックスに固定し、レガシーとv0のサンプルを生成してシンボルのdiffをとります。シンボルサーバーはbuild IDによって古いレポートのデコードを維持し、新しいビルドではv0対応ツールを使用します。移行中は、タグなしキャッシュや事前ビルド済みライブラリの混在を禁止し、サポートされていないコンシューマーがある場合はリリースを一時停止するか範囲を絞り込みます。最後にC FFI、クラッシュバックトレース、プロファイリング、再現性、ロールバックを検証します。」
ステップごとの解決策
1. シンボルの境界を引く
rustcは内部アイテムにマングルされた名前を付与し、リンカはそれらを使用してオブジェクトやライブラリを接続します。#[no_mangle]はアイテムのマングリングを無効にし、#[export_name]は正確なエクスポート名を選択します。関連するextern宣言もリンク名を制御できます。最初の不変条件は、C、C++、および安定したプラグインインターフェースが、Rustのジェネリクスやモジュールパスのエンコーディングではなく、明示的な外部名に依存するということです。
2. v0の保証と制限を述べる
v0は_Rで始まり、ジェネリクスやパス情報を曖昧さなくエンコードできるため、デマングラーが有用なインスタンスコンテキストを復元できます。またrustcのドキュメントには、これが安定したABIではなく、デマングルされた形式も標準化されていないと記載されています。これを解析可能な診断用フォーマットとして扱い、バージョンをまたぐプロトコル、設定、または永続データベースに安定した識別子としてv0文字列を書き込まないでください。
3. 互換性マトリックスを構築する
Rustのバージョン、ターゲット、debugまたはreleaseプロファイル、ツールバージョン、事前ビルドされたライブラリのソース、最終コンシューマーを含めます。エクスポートされたシンボル、バックトレース、プロファイラの認識、ビルドハッシュを備えた小さなバイナリをすべての組み合わせに対して保持します。「フォーマットの変更」と「ツールのアップグレード」が1つのテスト不能な変数にならないよう、ロックファイルまたはコンテナを通じてコンパイラ、リンカ、デマングラーを固定します。
build_id -> rustc version -> target -> mangling format -> debug toolchain4. キャッシュと混在成果物を分離する
キャッシュキーにRustのバージョン、ターゲット、プロファイル、関連するコード生成オプションを含めます。古い.rlib、インクリメンタルディレクトリ、または生成されたシンボルインデックスが新しいビルドで暗黙的に再利用されないようにします。完全な再ビルドを比較する前に、これらをクリーンアップするか明示的にバケット分けしてください。事前ビルドされたライブラリには、ソースバージョンとビルドメタデータを付与します。リンクが失敗した場合は、古いコンパイラを強制する前に成果物の混在を調査します。
5. FFIおよびプラグインの契約を保護する
公開関数、静的変数、コールバックには固定のエクスポート名を使用し、Cヘッダー、バージョンチェック、最小限のABIテストを用意します。外部名、レイアウト、呼び出し規約、エラーセマンティクスが安定している限り、Rustの内部は名前変更、移動、またはよりジェネリックにすることができます。プラグインがプライベートなRustシンボルを検索している場合は、コンパイラを変更する前に安定したシムを作成してください。
6. 移行、リリース、ロールバックを設計する
古いシンボルサーバーとレポートデコーダーを維持しながら、カナリアターゲットでv0を使用して再ビルドします。build IDごとにデバッグシンボルをアップロードし、リリース前にクラッシュバックトレース、プロファイラ、サイズツール、C FFIを検証します。重要なコンシューマーがv0をパースできない場合は、v0が安定したABIであるかのように扱うのではなく、リリース成果物またはツールチェーンマトリックスをロールバックします。コンシューマーが修正された後にのみ範囲を拡大します。
質の高い模範解答
「これはABIのアップグレードではなく、診断フォーマットの移行として扱います。デバッガ、プロファイラ、クラッシュシステム、サイズツール、キャッシュ、事前ビルドされたライブラリの棚卸しを行い、すべてのbuild IDについてRustc、ターゲット、フォーマットを記録します。Rust 1.97のv0はジェネリクスをより完全に表現しますが、ドキュメントには安定したABIではなく、標準化されたデマングル形式もないと明記されているため、内部シンボル名をプロトコルに含めることはしません。
FFIについては、独立したレイアウトと呼び出し規約のテストを用いて、Cが#[export_name]または安定したシムを介してリンクするという不変条件をテストします。コンパイラと解析ツールを固定し、レガシーとv0のサンプルを生成して、エクスポート、バックトレース、プロファイラの結果を比較します。キャッシュキーにはコンパイラ、ターゲット、プロファイル、コード生成オプションを含め、古い.rlibファイルが新しい成果物と混ざらないようにします。カナリアリリースでは、古いシンボルのデコードとロールバックを維持します。すべての重要なコンシューマーが新しいフォーマットをパースでき、再現可能なビルドが成功した後にのみ展開を拡大します。」
よくある間違い
- 間違い: v0シンボルを安定したABIとして扱う。 → 失敗の理由: Rustはv0がABI安定でないと文書化しており、フォーマットが拡張される可能性があります。 → 対策: FFIには明示的なエクスポート名を使用し、内部シンボルは診断用にとどめます。
- 間違い: 古いインクリメンタルキャッシュを直接再利用する。 → 失敗の理由: 新旧のコンパイラとフォーマットが1つのキャッシュ内で混在し、障害の再現が不可能になります。 → 対策: ツールチェーンとターゲットをキャッシュキーに含め、移行中にクリーンアップまたはバケット分けします。
- 間違い: デバッガやプロファイラをテストせず、デマングラーのみをアップグレードする。 → 失敗の理由: コンシューマーによってサポートするバージョンが異なったり、情報の一部しかサポートしていなかったりする場合があります。 → 対策: 代表的なバイナリでエンドツーエンドのマトリックステストを実行します。
- 間違い: nightlyのレガシーオプションを恒久化する。 → 失敗の理由: stableでは同じフォールバックが保証されないため、別のツールチェーンアップグレードで問題が再発します。 → 対策: コンシューマーを修正するかリリーススコープを狭めます。フォールバックは短期的な封じ込めにのみ使用します。
フォローアップと回答
新しいRustがリリースされる間、古い.soがサービスを提供し続ける必要があります。どうしますか?
.soの公開境界を確認します。C ABIが安定したエクスポート名のみを使用している場合は、新しいRust成果物を個別にビルドしてデプロイします。プライベートなRustシンボルに依存している場合は、シムを追加するか置き換えを延期します。両方の成果物にbuild ID、ツールチェーンメタデータ、個別のシンボルインデックスを持たせます。ファイル名だけで混同してはなりません。
クラッシュプラットフォームがレガシーシンボルしかデコードできません。どのように進めますか?
古い成果物のデコードを維持し、オフラインのv0パースとバックトレースのテストを構築します。プラットフォームがリリース前にv0をサポートできない場合は、v0を外部レポートを生成しないカナリアに限定するか、そのターゲットを一時停止します。デバッグシンボルを削除しても、障害が見えなくなるだけです。
デマングルされた名前をメトリクスのディメンションとして使用しないのはなぜですか?
これらは安定した標準ではないためです。コンパイラ、ジェネリクスのインスタンス化、デマングラーのバージョンによってテキストが変更される可能性があります。build ID、正規化されたアートルール、または解析ツールからの安定したマッピングを使用してください。名前を表示する必要がある場合は、リリース間でテキストを比較するのではなく、元のマングルされた名前とツールバージョンを保持します。