AMD64 -- nopw アセンブリ命令?



このコンパイラ出力では、 nopw のマシンコード エンコーディングがどのように行われるかを理解しようとしています。 指導作品:


00000000004004d0 <main>:
4004d0: eb fe jmp 4004d0 <main>
4004d2: 66 66 66 66 66 2e 0f nopw %cs:0x0(%rax,%rax,1)
4004d9: 1f 84 00 00 00 00 00

http://john.freml.in/amd64-nopl で「nopw」についての議論があります。 4004d2-4004e0 の意味を説明できる人はいますか?オペコード リストを見ると、66 .. のようです。 コードはマルチバイト展開です。数時間オペコードリストを調べようとしない限り、おそらくここでより良い答えを得ることができると思います.



その asm の出力は、単純な無限ループに至るまで最適化する C の次の (非常識な) コードからのものです:


long i = 0;
main() {
recurse();
}
recurse() {
i++;
recurse();
}

gcc -O2 でコンパイルした場合 、コンパイラは無限再帰を認識し、それを無限ループに変えます。 main() で実際にループするほど、これは非常にうまく機能します。 recurse() を呼び出さずに 関数。



編集者注:NOP を使用した関数のパディングは、無限ループに固有のものではありません。これは、Godbolt コンパイラー エクスプローラーで、さまざまな長さの NOP を持つ一連の関数です。


答え:


0x66 バイトは「オペランド サイズ オーバーライド」プレフィックスです。これらを複数持つことは、1 つ持つことと同じです。


0x2e は 64 ビット モードでは「null プレフィックス」です (それ以外の場合は CS:セグメント オーバーライドです。これがアセンブリ ニーモニックに表示される理由です)。


0x0f 0x1f ModRM バイトを取る NOP の 2 バイト オペコードです


0x84 この場合、さらに 5 バイトを使用するアドレッシング モードをコード化する ModRM バイトです。


一部の CPU は多くのプレフィックス (たとえば 3 つ以上) を持つ命令のデコードが遅いため、SIB + disp32 を指定する ModRM バイトは、プレフィックス バイトを 5 バイト増やすよりも、余分な 5 バイトを使い切る方がはるかに優れています。




基本的に、これらのバイトは、実行されることのない 1 つの長い NOP 命令です。コンパイラが .p2align 4 を出力したため、次の関数が 16 バイト境界に整列されるようにするためにそこにあります。 ディレクティブなので、アセンブラは NOP でパディングします。 x86 の gcc のデフォルトは

-falign-functions=16 .実行される NOP の場合、long-NOP の最適な選択はマイクロアーキテクチャによって異なります。 Intel Silvermont や AMD K8 など、多くのプレフィックスで停止するマイクロアーキテクチャの場合、それぞれ 3 つのプレフィックスを持つ 2 つの NOP の方が高速にデコードできた可能性があります。


質問がリンクされているブログ記事 ( http://john.freml.in/amd64-nopl ) では、コンパイラが単一バイトの 0x90 NOP 命令の束ではなく、複雑な単一の NOP 命令を使用する理由を説明しています。


命令エンコーディングの詳細については、AMD のテクニカル リファレンス ドキュメントを参照してください:



  • http://developer.amd.com/documentation/guides/pages/default.aspx#manuals


主に「AMD64 Architecture Programmer's Manual Volume 3:General Purpose and System Instructions」にあります。 Intel の x64 アーキテクチャに関するテクニカル リファレンスにも同じ情報が含まれていると確信しています (さらに理解しやすいかもしれません)。