ARMアセンブリと機械語を変換する方法

10 min read

はじめに

ARMアセンブラの勉強のメモ書きです。ARMアセンブラと機械語の変換や仕組みについて記述してあります。間違った情報があるかもしれませんがご了承ください。

機械語

ARMは32bit命令を使用している。 命令はデータ処理命令、メモリ命令、分岐命令とその他の命令に分類される。

基本形

※上段:ビット数。下段:内容。

31:28 27:26 25:20 19:0
cond op funct -

各内容

  • cond:命令の発動タイミング cond=1110:Always発動
  • op:オペレーションコード ←ここを見てまず何の命令かを判断する。
    • op=00:データ処理命令
    • op=01:メモリ命令
    • op=10:分岐命令
  • funct:ファンクション データ処理、メモリ、分岐でそれぞれ形が変わる。
  • -:命令によって形が変わる。

データ処理命令

op=00のときに実行される命令

基本形

※上段:ビット数。下段:内容。

31:28 27:26 25 24:21 20 19:16 15:12 11:0
cond op I cmd S Rn Rd Src2

各内容

  • I:直値orレジスタの判断。これによりSrc2の形が変わる。
    • I=1:直値
    • I=0:レジスタorシフト数レジスタ
  • cmd:データ処理命令 ここで、データ処理の内容が決まる。
  • S:条件フラグ更新
    • S=1:更新する
    • S=0:更新しない
  • Rd:レジスタ1
  • Rn:レジスタ2
  • Src2:ソース2

cmdの内容

cmd フォーマット 説明 動作
0000 AND Rd,Rn,Src2 ビット毎のAND Rd ← Rn & Src2
0001 EOR Rd,Rn,Src2 ビット毎のXOR Rd ← Rn ^ Src2
0010 SUB Rd,Rn,Src2 減算 Rd ← Rn - Src2
0011 RSB Rd,Rn,Src2 逆方向減算 Rd ← Src2 - Rn
0100 ADD Rd,Rn,Src2 加算 Rd ← Rn + Src2
0101 ADC Rd,Rn,Src2 キャリー付加算 Rd ← Rn + Src2 + C
0110 SBC Rd,Rn,Src2 キャリー付減算 Rd ← Rn - Src2 - C
0111 RSC Rd,Rn,Src2 キャリー付き逆方向減算 Rd ← Src2 - Rn - C
1000 TST Rn,Src2 テスト Rn & Src2に基づきフラグをセット
1001 TEQ Rn,Src2 等しいかテスト Rn ^ Src2に基づきフラグをセット
1010 CMP Rn,Src2 比較 Rn -Src2に基づきフラグをセット
1011 CMN Rn,Rd,Src2 負の比較 Rn + src2に基づきフラグをセット
1100 ORR Rd,Rn,Src2 ビット毎のOR Rd ← Rn ∧ Src2
1101 シフト 下表参照
1110 BIC Rd,Rn,Src2 ビット毎のクリア Rd ← Rn & ~Src2
1111 MVN Rd,Rn,Src2 ビット毎の否定 Rd ← ~Rn
  • フォーマット例 この場合、R1=Rd、R2=Rn、#12=Src2に相当する。
ADD R1,R2,#12

シフトの内容

I=1のときは移動命令、I=0のときはシフトor回転命令となる。 I=0のときは、shを見て命令を判断する。 ※例外1:I=0かつsh=00かつshmat5=00000の場合、移動命令(MOV)となる。 ※例外2:I=0かつsh=11かつshmat5=00000の場合、拡張右回転命令(RRX)となる。

I=1のときのシフト内訳

フォーマット 説明 動作
MOV Rd,Src2 移動 Rd ← Src2

I=0のときのシフト内訳

sh フォーマット 説明 動作
00 LSL Rd,Rm,Rs/shmat5 論理左シフト Rd ← Rm << Src2
01 LSR Rd,Rm,Rs/shmat5 論理右シフト Rd ← Rm >> Src2
10 ASR Rd,Rm,Rs/shmat5 算術右シフト Rd ← Rm >> Src2
11 ROR Rd,Rm,Rs/shmat5 右回転 Rd ← Rn ror Src2

条件フラグが更新される命令

S=1になる命令

  • ADDS
  • SUBS
  • ASRS,LSLS,LSRS,RORS
  • ANDS,ORRS,EORS,BICS
  • MOVS,MVNS
  • MULS,SMULLS,UMULLS
  • CMP,CMN
  • TEQ,TST

Src2の内容

大きく分けてデータ処理命令(ADD、SUB)とシフト命令に分けられる

データ処理命令の場合

2パターン存在する

I=1:直値を扱う場合

Src2の中身が以下のように変形する

※上段:ビット数。下段:内容。

11:8 7:0
rot imm8
  • rot:回転数
  • imm8:8bitの値

直値を扱う場合、基本8bitでしか値を扱えない。しかし、バレルシフタという工夫を行うことで、32bit(制限付き)までの値を扱うことができる。

  • 計算方法 imm8の値をrot x 2の値分右回転させる。 例:imm8=1111 1111 rot=1110 rot=1110は10進数に直すと、14である。よって、14 x 2 = 28bit分、右回転を行えば良い。 32bit回転前 0000 0000 0000 0000 0000 0000 1111 1111 32bit回転後 0000 0000 0000 0000 0000 1111 1111 0000

回転後の値は10進数で4080である。この値は8bitでは扱えないがこのようにバレルシフトを行うことで扱うことができる。当たり前だが、8bit以内で表現できる値の場合はrot=0000を指定する。

例:ADD R0,R1,#42

cond op I cmd S Rn Rd rot imm8
1110 00 1 0100 0 0001 0000 0000 0010 1010

cmd=ADD、Rd=R0、Rn=R1、rot/imm8=#42となっている。 この機械語を4bitずつに区切ると

1110 0010 1000 0001 0000 0000 0010 1010

となり、これを16進数に直すと

0xE281002A

となる。

I=0:レジスタを扱う場合

Src2の中身が以下のように変形する

※上段:ビット数。下段:内容。

11:7 6:5 4 3:0
shmat5 sh 0 Rm
  • shmat5:共有メモリ ※データ処理命令かつレジスタを扱う場合はshmat5=00000になる。
  • sh:シフト命令 ※データ処理命令かつレジスタを扱う場合はsh=00になる。
  • 0:0が格納されている
  • Rm:レジスタ 3つ目のレジスタに該当する

例:SUB R8,R9,R10

cond op I cmd S Rn Rd shmat5 sh 0 Rm
1110 00 0 0010 0 0110 0101 00000 00 0 1010

cmd=SUB、Rd=R9、Rn=R8、Rm=R10となっている。 この機械語を4bitずつに区切ると

1110 0000 0100 0110 0101 0000 0000 1010

となり、これを16進数に直すと

0xE049800A

となる。

シフト命令の場合

3パターン存在する

I=1:移動命令の場合

Src2の中身が以下のように変形する

※上段:ビット数。下段:内容。

11:8 7:0
rot imm8
  • rot:回転数
  • imm8:8bitの値

前述したとおり、シフト命令でI=1の場合は移動命令(MOV)となる。 例:MOV R0,#7

cond op I cmd S Rn Rd rot imm8
1110 00 1 1101 0 0000 0000 0000 0000 0111

cmd=シフト命令、Rd=R0、rot/imm8=#7となっている。 この機械語を4bitずつに区切ると

1110 0011 1010 0000 0000 0000 0000 0111

となり、これを16進数に直すと

0xE3A00007

となる。

I=0:シフト量が直値の場合

Src2の中身は以下のように変形する

※上段:ビット数。下段:内容。

11:7 6:5 4 3:0
shmat5 sh 0 Rm
  • shmat5:共有メモリ ※直値が入る。
  • sh:シフト命令
  • 0:0が格納されている
  • Rm:レジスタ ※シフト命令の場合、Rnは使用せず2つ目のレジスタはRmとなる。

例:LSL R0,R9,#7

cond op I cmd S Rn Rd shmat5 sh 0 Rm
1110 00 0 1101 0 0000 0000 00111 00 0 1001

cmd=シフト命令、Rd=R0、Rm=R9、shmat5=#7、sh=LSLとなっている。 この機械語を4bitずつに区切ると

1110 0001 1010 0000 0000 0011 1000 1001

となり、これを16進数に直すと

0xE1A00309

となる。

I=0:シフト量がレジスタの場合

Src2の中身は以下のように変形する

※上段:ビット数。下段:内容。

11:8 7 6:5 4 3:0
Rs 0 sh 1 Rm
  • Rs:レジスタ 3つ目のレジスタに該当する
  • 0:0が格納されている
  • sh:シフト命令
  • 1:1が格納されている
  • Rm:レジスタ 2つ目のレジスタに該当する

例:ASR R5,R1,R12

cond op I cmd S Rn Rd Rs 0 sh 1 Rm
1110 00 0 1101 0 0000 0101 1100 0 10 1 0001

cmd=シフト命令、Rd=R5、Rm=R、Rs=R12、sh=ASLとなっている。 この機械語を4bitずつに区切ると

1110 0001 1010 0000 0101 1100 0101 0001

となり、これを16進数に直すと

0xE1A05C51

となる。

メモリ命令

op=01のときに実行される命令

基本形

※上段:ビット数。下段:内容。

31:28 27:26 25 24 23 22 21 20 19:16 15:12 11:0
cond op I P U B W L Rn Rd Src2

各内容

  • I:直値orレジスタの判断。これによりSrc2の形が変わる。
    • I=0:直値オフセット
    • I=1:レジスタオフセット
  • U:足し算or引き算
    • U=1:オフセットを足す
    • U=0:オフセットを引く
  • P W:インデックスモード ←PとWの2値を使って判断する。
  • L B:メモリ命令 ←LとBの2値を使って判断する。
  • Rd:レジスタ1
  • Rn:レジスタ2
  • Src2:ソース2

P W:インデックスモードの内容

P W インデックスモード 見分け方
0 0 ポストインデックス LDR R0,[R1],R2 真ん中に括弧
0 1 サポート外 -
1 0 オフセット LDR R0,[R1,R2] 右二個に括弧
1 1 プレインデックス LDR R0,[R1,R2]! ビックリマーク

L B:メモリ命令の内容

L B 命令
0 0 STR
0 1 STRB
1 0 LDR
1 1 LDRB

Src2の内容

I=0:直値を扱う場合

Src2の中身が以下のように変形する

※上段:ビット数。下段:内容。

11:0
imm12
  • imm12:12bitの値を格納 直値を扱う

例:STR R11,[R5],#-26

cond op I P U B W L Rn Rd imm12
1110 01 0 0 0 0 0 0 0101 1011 0000 0001 1010

U=引き算、P W=ポストインデックス、L B=STR、imm12=#26 この機械語を4bitずつに区切ると

1110 0100 0000 0101 1011 0000 0001 1010

となり、これを16進数に直すと

0xE405B01A

となる。

I=1:レジスタを扱う場合

Src2の中身は以下のように変形する

※上段:ビット数。下段:内容。

11:7 6:5 4 3:0
shmat5 sh 0 Rm
  • shmat5:共有メモリ
  • sh:シフト命令
  • 0:0が格納されている
  • Rm:レジスタ

例:省略

分岐命令

OP=10のときに実行される命令

基本形

※上段:ビット数。下段:内容。

31:28 27:26 25 24 23:0
cond op 1 L imm24
  • 1:1が格納されている
  • L:分岐命令
  • imm24:24bit値を格納 ←分岐先アドレスを格納

L:分岐命令

L 命令
0 B
1 BL

分岐先のアドレス計算

  1. 分岐命令を探す
  2. 分岐命令+2の命令を基準として考える
  3. 分岐先のアドレスが基準から何個離れているかを計算する
  4. アドレスの値をimm24に格納する ※値がマイナスの場合は2の歩数表現を使う

例:

1 TEST LDRB R5,[R0,R3]  ←分岐先アドレス
2      STRB R5,[R1,R3]
3      ADD  R3,R3,#1
4      MOV  PC,LR
5      BL   TEST     ←分岐命令
6      LDR  R3,[R1],#4
7      SUB  R4,R3,#9    ←基準

この場合、基準から分岐先アドレスは“-6“となる。 つまり、機械語で表すと

cond op 1 L imm24
1110 10 1 1 1111 1111 1111 1111 1111 1010

この機械語を4bitずつに区切ると

1110 1011 1111 1111 1111 1111 1111 1010

となり、これを16進数に直すと

0xEBFFFFFA

となる。

参考文献