Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16833] ファイルの概要

このコミットは、Go言語のツールチェインの一部である libmach ライブラリ内の src/libmach/8db.c ファイルに対する変更です。libmach は、Goのアセンブラやデバッガが使用する、機械語命令のディスアセンブル(逆アセンブル)やシンボル解決を行うためのライブラリです。特に 8db.c は、x86およびamd64アーキテクチャの命令セットのデコードと表示を担当しています。

このファイルは、IntelおよびAMDプロセッサの命令セットを解析し、人間が読めるアセンブリ言語のニーモニックに変換するためのデータ構造(Optable)とロジックを含んでいます。Optable は、命令のオペコード(命令コード)に基づいて、対応するニーモニック、オペランドの形式、およびその他の特性を定義するテーブルです。

コミット

commit 87976e72a8e821666288a88e2946f2fcf42e1760
Author: Anthony Martin <ality@pbrane.org>
Date:   Sat Jul 20 00:38:26 2013 -0700

    libmach: support more 386/amd64 instructions

    R=golang-dev, dave, bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/10030043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/87976e72a8e821666288a88e2946f2fcf42e1760

元コミット内容

libmach: support more 386/amd64 instructions

変更の背景

このコミットの背景には、Go言語のコンパイラやツールチェインが、より新しいx86/amd64プロセッサの拡張命令セットを活用するようになったことがあります。Goは、パフォーマンス最適化のために、特定の処理(例えば、暗号化、データ処理、ベクトル演算など)において、CPUが提供する特殊な命令(SIMD命令や暗号化命令など)を利用することがあります。

libmach は、Goのデバッガ(gdbdelve など)やプロファイラが、生成された機械語コードを人間が理解できるアセンブリ言語に逆アセンブルするために不可欠なコンポーネントです。もし libmach が新しい命令を認識できない場合、それらの命令は「不明なオペコード」として表示されたり、誤って解釈されたりする可能性があります。これは、開発者がGoプログラムの低レベルの動作をデバッグしたり、パフォーマンスを分析したりする際に大きな障害となります。

したがって、このコミットは、Goツールチェインが生成する可能性のある、あるいはGoプログラムが外部ライブラリを通じて利用する可能性のある、より広範なx86/amd64命令セットを libmach が正確にディスアセンブルできるようにするための対応です。これにより、Go開発者は最新のCPU機能を活用したコードをより効果的に分析できるようになります。

前提知識の解説

x86/amd64命令セットと拡張命令

IntelおよびAMDのx86/amd64アーキテクチャは、基本的な命令セットに加えて、特定のタスクを高速化するための多くの拡張命令セットを持っています。これらは通常、SIMD(Single Instruction, Multiple Data)命令や、特定のアルゴリズム(例: 暗号化)をハードウェアで直接実行するための命令として提供されます。

  • SSE (Streaming SIMD Extensions): 浮動小数点演算やSIMD処理を高速化するための命令セット。SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2など、多くのバージョンがあります。
    • PREFETCH: メモリからデータを事前にキャッシュに読み込むことで、メモリレイテンシを隠蔽し、パフォーマンスを向上させるための命令。PREFETCHNTA (Non-Temporal Hint) は、データが一度しか使用されない可能性が高い場合に、キャッシュを汚染しないようにするためのヒントを提供します。
    • PSHUFB (Packed Shuffle Bytes): SSSE3で導入された命令で、バイト単位でレジスタ内のデータをシャッフル(並べ替え)します。
  • AES-NI (Advanced Encryption Standard New Instructions): AES暗号化アルゴリズムをハードウェアレベルで高速化するための命令セット。AESENC (AES Encrypt), AESDEC (AES Decrypt), AESKEYGENASSIST (AES Key Generation Assist) などが含まれます。これにより、ソフトウェア実装に比べてはるかに高速かつ安全にAES処理を実行できます。
  • CRC32: SSE4.2で導入された命令で、CRC32(巡回冗長検査)チェックサムを計算します。データ転送の整合性チェックなどに使用されます。

libmachOptable

libmach は、Go言語のデバッガやアセンブラが、機械語コードを解釈するために使用するライブラリです。このライブラリは、CPUの命令セットに関する知識を内部に持っており、バイナリデータストリームを読み込んで、対応するアセンブリ命令に変換します。

Optablelibmach 内で使用される重要なデータ構造で、命令のオペコードとそれに対応するアセンブリニーモニック、オペランドのタイプ、およびその他の命令特性をマッピングするために使用されます。x86/amd64命令セットは非常に複雑で、多くの命令が複数のバイト(オペコード、ModR/Mバイト、SIBバイト、ディスプレースメント、イミディエイトなど)で構成され、さらにプレフィックス(例: 0x660xF20xF3)によって動作が変化することがあります。

Optable は、これらの複雑な命令デコードロジックを効率的に表現するために、ネストされたテーブル構造を持つことがあります。例えば、あるオペコードが別の Optable へのポインタを持つことで、複数のバイトにわたるオペコードや、ModR/Mバイトのレジスタフィールドによってさらに分岐する命令を処理できます。

技術的詳細

このコミットは、src/libmach/8db.c ファイル内の Optable データ構造を拡張し、より多くのx86/amd64命令を認識できるようにしています。具体的には、以下の命令群が追加または修正されています。

  1. optab0F18 の追加と 0F18 オペコードのサポート:

    • PREFETCHNTA, PREFECTCH0, PREFECTCH1, PREFECTCH2 命令が追加されました。これらはSSE命令セットの一部であり、プロセッサのキャッシュにデータをプリフェッチするためのヒントを提供します。
    • optab0F18 という新しい Optable が定義され、メインの optab0F テーブルの 0x18 エントリから参照されるようになりました。
  2. optab660F38 および optab660F3A の追加と 0x66 0F 38/3A オペコードのサポート:

    • 0x66 プレフィックスと 0x0F オペコードに続く 0x38 および 0x3A オペコードは、SSSE3、SSE4.1、AES-NIなどの比較的新しい命令セットの命令に使用されます。
    • optab660F38 には PSHUFB, AESENC, AESIMC, AESENCLAST, AESDEC, AESDECLAST が追加されました。
    • optab660F3A には PINSR%S, AESKEYGENASSIST が追加されました。
    • これらの新しい Optable は、既存の optab660F テーブルの 0x38 および 0x3A エントリから参照されるようになりました。
  3. optabF20F38 の追加と 0xF2 0F 38 オペコードのサポート:

    • 0xF2 プレフィックスと 0x0F オペコードに続く 0x38 オペコードは、SSE4.2のCRC32命令に使用されます。
    • optabF20F38 には CRC32B, CRC32%S が追加されました。
    • この新しい Optable は、既存の optabF20F テーブルの 0x38 エントリから参照されるようになりました。
  4. 既存命令のフォーマット修正:

    • 多くの既存の命令ニーモニックのオペランド間にタブ (\t) が追加され、ディスアセンブル出力の可読性が向上しました。これは機能的な変更ではなく、出力の整形に関する変更です。
  5. プレフィックス処理の変更:

    • badop ラベル内の命令デコードロジックにおいて、0xF2 (REPNE) および 0xF3 (REP) プレフィックスが 0x0F オペコードに続く場合に、ip->prefix = 0; と設定されるようになりました。これは、これらのプレフィックスが特定の命令(例えば、CRC32)では命令の一部として解釈されるべきであり、独立したプレフィックスとして扱われるべきではないことを示唆しています。これにより、ディスアセンブラがこれらの命令を正しく認識し、誤ったプレフィックス表示を避けることができます。

これらの変更により、libmach は、Goコンパイラが生成する可能性のある、またはGoプログラムが利用する可能性のある、より広範なx86/amd64命令セット(特にSSE4.1, SSE4.2, AES-NIなど)を正確にディスアセンブルできるようになります。

コアとなるコードの変更箇所

変更はすべて src/libmach/8db.c ファイル内で行われています。

主な変更箇所は以下の通りです。

  1. 新しい Optable 配列の定義:

    • optab0F18 (行 482-487)
    • optab660F38 (行 523-530)
    • optab660F3A (行 532-536)
    • optabF20F38 (行 599-602)
  2. 既存の Optable への参照追加:

    • optab660F (行 550-551): 0x380x3A エントリがそれぞれ optab660F38optab660F3A を参照するように変更。
    • optabF20F (行 612): 0x38 エントリが optabF20F38 を参照するように変更。
    • optab0F (行 630): 0x18 エントリが optab0F18 を参照するように変更。
  3. 既存命令のフォーマット修正:

    • optab660F (行 554-557)
    • optab0F (行 679-706)
    • optabD9 (行 953, 957)
    • optabDD (行 1102, 1104, 1111)
    • optabDE (行 1125, 1127, 1129) など、多くの行でオペランド間のスペースがタブ (\t) に変更されています。
  4. プレフィックス処理の変更:

    • badop ラベル内の switch (ip->opre) ブロック (行 1896-1904)
      • case 0xF2:case 0xF3: のブロックに ip->prefix = 0; が追加されました。

コアとなるコードの解説

このコミットの核となる変更は、libmach がx86/amd64命令をデコードする際に使用する Optable の拡張と、プレフィックス処理の微調整です。

Optable は、命令のオペコードをキーとして、その命令に関する情報(ニーモニック、オペランドのタイプ、次のデコードステップなど)を格納するテーブルです。x86/amd64命令セットには、1バイトのオペコードだけでなく、0x0F で始まる2バイトオペコードや、0x660xF20xF3 などのプレフィックスと組み合わされることで意味が変わる命令が多数存在します。

このコミットでは、特に 0x0F に続くオペコードや、0x660xF2 プレフィックスと 0x0F 38 / 0x0F 3A オペコードの組み合わせによって定義される新しい命令群(SSE4.1, SSE4.2, AES-NIなど)をサポートするために、新しい Optable 配列が導入されました。

例えば、optab660F38 は、0x66 プレフィックス、0x0F オペコード、そして 0x38 オペコードに続く命令を定義します。optab660F[0x38] エントリが AUX タイプ(補助テーブル)として optab660F38 を参照するように設定されることで、デコーダはこれらの多バイトオペコード命令を正しく解析できるようになります。

また、badop ラベル内のプレフィックス処理の変更は、0xF2 (REPNE) や 0xF3 (REP) といったプレフィックスが、特定の命令(例: CRC32)では命令自体の一部として機能し、独立したリピートプレフィックスではないことをデコーダに伝えるためのものです。これにより、ディスアセンブル出力がより正確になります。

これらの変更により、libmach は、Goコンパイラが生成する可能性のある、より現代的なx86/amd64命令を正確に認識し、デバッグやプロファイリングの際に開発者にとって有用な情報を提供できるようになります。

関連リンク

参考にした情報源リンク

  • Intel® 64 and IA-32 Architectures Software Developer’s Manuals: 命令セットの詳細な説明のために参照。
  • Wikipedia (x86 instruction listings, SSE, AES-NI, CRC32): 各命令セットや命令の概要を理解するために参照。
  • Go言語のソースコード (libmach): Optable の構造やデコードロジックの理解のために参照。