FreeStyleWiki

アセンブラ(ModR/M)

このエントリーをはてなブックマークに追加

[アセンブラ]

アセンブラ(ModR/M)

最近自作のアセンブラを開発しているのだが、もっとも難しい部分について理解が得られたのでまとめておく

ModR/Mの実際の例

IMUL命令(Signed Multiply)
例えば以下のような命令がある、 /r の意図がみなさんわからないのではないでしょうか?
; 0x69 /r iw
IMUL r32, imm32
CMP命令(Compare Two Operands)
また、次のような命令、 /7 の意図がみなさんわからないのではないでしょうか?
; CMP r/m8, imm8
0x80 /7 ib

ModR/Mの構造

ModR/M ‐ 通信用語の基礎知識
全体の概要についてはこのページをみたほうがよい
Assembly Programming on x86-64 Linux (06)
また、このページも役立つ

/rおよび/7の意味

解説をAssembly Programming on x86-64 Linux (06)から引用する

記法 解説
/0 - /7 ModR/M バイト の reg フィールドの 0 から 7 の数字はオペコードの拡張用に使われる。r/m フィールドだけをオペランドに使用する。
/r 命令には ModR/M バイトが続き、レジスタオペランドと r/m オペランドの両方を使う。
cb、cw、cd、cp, co, ct オペコードの後に 1 バイト(cb)、2 バイト(cw)、4 バイト(cd)、6 バイト(cp)、 8 バイト(co)または 10 バイト(ct)が続く。
ib, iw, id, io オペコード、ModM/R バイト、または SIB の後に続く 1 バイト(ib)、2 バイト(iw)、4 バイト(id)または 8 バイト(io)の定数(即値)。
+rb, +rw, +rd, +ro + の左側のオペコードに加算される 0 から 7 までのレジスタコード。 結果として 1 バイトのオペコードとなる。

  ModR/Mバイト

  • ModR/Mバイトは画像のような構造になっており、それを指定された場合アセンブラはmod/reg/r/mの形でバイナリを吐き出す
mod
modは、次のようにr/mの用途を切り替える。
  • mod=00: [レジスター+レジスター]
  • mod=01: [レジスター+disp8]
  • mod=10: [レジスター+disp16/32]
  • mod=11: レジスター
reg(/0 - /7指定の場合)
regは、/0 - /7を指定された場合はそれをそのまま使う

もちろんこれは10進数表記なので、2進数に直すと以下のようになる。ModR/Mバイトのregフィールドはこれで埋まるわけだ。

/0 - /7 2進数表記
/0 000
/1 001
/2 010
/3 011
/4 100
/5 101
/6 110
/7 111
reg(/0 - /7指定の場合)の実例
0x80 /7 ib をアセンブル
; 0x80 /7 ib
CMP r/m8, imm8

; 例
CMP CL,18

; オペコードは0x80で確定
; ---------------------
; ModR/Mの値は
; [mod] 11
; [reg] 111
; [r/m] 001
; => "11111001" 
; => 0xf9
; ---------------------
; 18は16進数で0x12
; ---------------------

; よって、以下がアセンブルされると
CMP CL,18
; 以下のバイナリが出力される
0x80, 0xf9, 0x12
reg(/r指定の場合)
regは、レジスタ番号を指定する

つまり、mod2bitに続いて、regr/mが3bitずつ続くことになる

REGの値 レジスタ
000 al ax eax
001 cl cx ecx
010 dl dx edx
011 bl bx ebx
100 ah sp esp
101 ch bp ebp
110 dh si esi
111 bh di edi
reg(/r指定の場合)の実例
0x69 /r iw をアセンブル
; 0x69 /r iw
IMUL r32, imm32

; 例
IMUL ECX,4608

; オペコードは0x69で確定
; ---------------------
; ModR/Mの値は
; [mod] 11
; [reg] 001
; [r/m] 001
; => "11001001" 
; => 0xc9
; ---------------------
; 4608は16進数で0x1200
; リトルエンディアンのため、 0x00, 0x12と並ぶ
; ---------------------

; よって、以下がアセンブルされると
IMUL ECX,4608
; 以下のバイナリが出力される
0x69, 0xc9, 0x00, 0x12