今日、IA-32命令のデコード/逆アセンブルの興味深い複雑さについてお話したいと思います。
この記事を読む前
に、IA-32コマンドの一般的な構造と既存のプレフィックスについて説明している記事
「IA-32コマンドシステムのプレフィックス」を参照することをお勧めします。 この記事では、必須のプレフィックスとそれらに関連するいくつかのニュアンスについて詳しく説明します。
それはすべて、デコーダーを作成するために設計された言語
-GDSLについての記事を読むことから始まり
ました 。 この記事では、私がすでに知っているいくつかの例と、今まで聞いたことのない新機能を紹介します。 それは私が今あなたに話すのは彼らについてです。
MULSS
、
MULSD
、および
MULPD
(ベクトル乗算命令)などの一部の命令には、同じオペコード
0x0f 0x59
がありますが、必須のプレフィックスは異なります(それぞれ
0x66
および
0x66
)。 問題は、命令コードに複数のそのような接頭辞が同時に存在する場合、どうなるのでしょうか? おそらく、最後のプレフィックスに従って、これがどのような命令であるかを判断する方が論理的でしょう。 しかし、これは常にそうではありません! 最後のプレフィックスが
0xf2
または
0xf3
である場合、必須と見なされますが、
0x66
は、プレフィックス
0xf2
および
0xf3
この命令のエンコーディングにない場合にのみ必須です。 これらの命令の正しい逆アセンブラ出力の例は、表にあります。
ある時点で、これらのステートメントの正確性を疑っていましたが、残念ながら、
ドキュメントはこの問題についてあいまいな考えを示しています。 その後、実際のプロセッサでテストを実施することが決定され、そのように動作することが判明しました。 このチェックは、アセンブラーインサートを使用して実行されました。 テストの1つの例を以下に示します。
#include <stdio.h> int main() { double a[2] = {2, 2}, b[2] = {0, 0}; __asm__ __volatile__ ( // Copy data from a to xmm7 register "movupd %1, %%xmm7\n" //"mulsd %%xmm7, %%xmm7\n" ".byte 0xf2, 0x66, 0x0f, 0x59, 0xff\n" // Copy data from xmm7 register to b "movupd %%xmm7, %0\n" :"=m"(*b) :"m"(*a) : ); printf("%lf %lf\n", b[0], b[1]); return 0; }
コンパイルして実行すると、次のように表示されます。
$ gcc -O0 -Wall mulsd.c $ ./a.out 4.000000 2.000000
つまり、ベクトルの最初の要素のみが乗算され、2番目の要素は変更されないままで、これは
MULSD
ではなく
MULPD
命令に対応します。
この例では、既知のパッケージに含まれるいくつかの逆アセンブラーがテストされました。 これらの製品の開発者にすぐに報告された彼らのタスクに対処した人はほとんどいませんでした。 結果の概要を以下に示します。
gdbとobjdumpはbinutilsの一部であり、逆アセンブルには同じライブラリを使用することに注意してください。 ODA開発者の1人であるAnthony DeRosaは、私のエラーメッセージに応じて、binutilsの一部であるlibopcodesライブラリを使用していると言いました。 つまり、1箇所で修正を行うと、少なくとも3つの製品を一度に修正する必要がありますが、残念ながら、binutilsのいずれもまだ私に答えていません。
使用する逆アセンブラは正しく機能しますか?