HN 日本語サマリー

← 一覧へ戻る
プログラミング

x86の未定義命令がud2と呼ばれるのはなぜか? なぜ2なのか?

Why is the x86 undefined instruction called ud2? Why 2? (devblogs.microsoft.com)

239 pointsby ibobev55 コメント

要約

x86アーキテクチャにおける未定義命令「ud2」の由来について解説しています。元々は0x0FFFや0x0FB9といったバイトシーケンスが未定義命令として使われていましたが、これらの挙動が将来のプロセッサで変更される可能性や、Hyrumの法則により依存するプログラムが存在したため、Intelは公式にサポートされる未定義命令として「ud2」を導入しました。これは、よりクリーンで予測可能な動作を提供するためです。

全文翻訳

x86の未定義命令「ud2」の名前の由来は?なぜ「2」なのか?

x86のコンパイラ出力(あるいは私のように、APIを迂回しようとしたソフトウェアによるクラッシュを調べている場合)を見ていると、ud2 という命令を目にすることがあるかもしれません。これは一体どういうことなのでしょうか?

ud2 命令は、アーキテクチャ的に未定義の命令であり、「無効なオペコード」例外を発生させることが保証されています。一部のコンパイラは、これを「到達不能」コードのマーカーとして生成します。これにより、もし実行が何らかの理由でそこへ到達してしまった場合、ランダムな命令を実行するのではなく、クラッシュが発生します。例えば、[[noreturn]] とマークされた関数が何らかの理由で復帰してしまった場合、コンパイラはその呼び出しの後に ud2 を配置し、プログラムが次の関数にフォールスルーするのではなくクラッシュするようにします。

さて、なぜこの命令は単に ud ではなく ud2 と呼ばれるのでしょうか? ud1 は存在したのでしょうか? ud1 の何が悪かったので、ud2 を作らなければならなかったのでしょうか?

経緯を再構築できると思います。

元々、x86にはアーキテクチャ的に未定義の命令はありませんでした。そのため、無効なオペコード例外を強制したい人々は、実行時に確実に無効なオペコード例外を発生させるバイトシーケンスを探し始めました。

誰かが 0F FF というシーケンスが無効なオペコード例外を引き起こすことを発見しました。ただし、何らかの理由で、この命令は内部的に2つのパラメータ(宛先レジスタとソースオペランド、レジスタまたはメモリ)を取るようにデコードされました。無効なオペコード例外が他の何かが起こる前に発生するため、これらのパラメータは実際には使用されません。

一方、誰かが 0F B9 というシーケンスも同様の特性を持つことを発見しました。こうして、0F FF 信者と 0F B9 支持者の2つの派閥が生まれました。どちらの手法も機能するように見え、一方が他方を犠牲にするわけではなかったため、実際には大きな争いはありませんでした。

その後、Intelは次のプロセッサの開発に着手し、おそらく 0F FF がもはや無効なオペコード例外を発生させないような変更を加えました。新しい命令を導入しようとしたか、あるいは依然として未定義でしたが、無効なオペコード例外を発生させる代わりにランダムな操作を実行したのかもしれません。そして、新しいプロセッサでソフトウェアを実行し始めたところ、一部のプログラムが動作しなくなり、骨の折れる調査の結果、それらのプログラムが 0F FF が無効なオペコードであることに依存していたことが判明しました。

言い換えれば、彼らはHyrumの法則に遭遇したのです。十分な数のユーザーがいれば、あらゆる観測可能な動作は誰かによって依存されるようになります。義務的なXKCD。

0F B9 でも同様の発見がありました。

人々が無効なオペコード例外をトリガーするための信頼できる方法を求めていることを認識したIntelの担当者は、これを公式のものにすることに決め、実際にサポートされ、永久に無効な命令を作成し、それを ud2 と名付けました。

ud2 と呼ばれるのは、0F FF のバリアントが遡って ud0 と名付けられ、0F B9 のバリアントが遡って ud1 と名付けられたため、ud2 が推奨される未定義オペコードとして残されたからです。

ud2 の利点の1つは、パラメータを持たない2バイト命令であるため、ランダムにデコードされるが未使用のソースと宛先の処理を気にする必要がないことです。

ボーナストーク: しかし、なぜ ud0 と ud1 の未使用パラメータを気にするのでしょうか? ud0 と ud1 も2バイトの無効オペコードであると言えないでしょうか? 確かに3バイト目、あるいはメモリオペランドにオフセットやスケールインデックスがある場合はそれ以上になるかもしれませんが、プロセッサはそれを使用しません。

プロセッサはそれを使用しませんが、デコードはします。そして、命令のデコードが現在存在しないページにまたがる場合、無効なオペコード例外はまったく発生しません。アクセス違反が発生します。そのため、これは重要です。

ボーナスボーナストーク: ただし、一部の古いプロセッサは、命令の残りの部分が正しくデコードされるかを確認せずに、0F FF をデコードするとすぐに無効なオペコード例外を発生させていました。そのため、0F FF がページの末尾にあり、次のページが存在しない場合、無効なオペコード例外が発生することもあれば、アクセス違反が発生することもありました。

ud2 を使うのが賢明です。その動作は一貫しており、アーキテクチャ的に保証されています。

Author

[画像: Raymond Chen]

Raymondは30年以上にわたりWindowsの進化に関わってきました。2003年に彼は「The Old New Thing」として知られるウェブサイトを開始し、それは彼の想像をはるかに超える人気を博しました。このウェブサイトは、偶然にも同じタイトルの書籍「The Old New Thing」(Addison Wesley 2007)を生み出しました。彼は時折、Windows Dev DocsのTwitterアカウントに登場し、有用な情報を含まない話を語っています。