2013年4月29日月曜日

アドレスサイズプレフィックス、オペランドサイズプレフィックス

GNU assemblerの話。
.code16, .code32, .code64でそれぞれ 16bit, 32bit, 64bit モードでアセンブルしてくれるようです。

まず、レジスタの名前ですが、ビット長ごとに以下の通り。
16bit  32bit  64bit
%ax    %eax   %rax
%bx    %ebx   %rbx
%cx    %ecx   %rcx
%dx    %edx   %rdx
%si    %esi   %rsi
%di    %edi   %rdi



例えば、以下のような test.s というファイルを作る。


.file "test.s"
.text

.code16

main16:
mov (%si), %ax
mov (%si), %eax
mov (%esi), %ax
mov (%esi), %eax

.code32

main32:
mov (%si), %ax
mov (%si), %eax
mov (%esi), %ax
mov (%esi), %eax

.code64

main64:
// mov (%si), %ax
// mov (%si), %eax
// mov (%si), %rax
mov (%esi), %ax
mov (%esi), %eax
mov (%esi), %rax
mov (%rsi), %ax
mov (%rsi), %eax
mov (%rsi), %rax


リストファイルを出してみる。
$ gcc -Wa,-a=test.lst -c -o test.o test.s
$ cat test.lst
GAS LISTING test.s page 1


   1               .file "test.s"
   2               .text
   3              
   4               .code16
   5               main16:
   6 0000 8B04     mov (%si), %ax
   7 0002 668B04   mov (%si), %eax
   8 0005 678B06   mov (%esi), %ax
   9 0008 67668B06 mov (%esi), %eax
  10              
  11               .code32
  12               main32:
  13 000c 67668B04 mov (%si), %ax
  14 0010 678B04   mov (%si), %eax
  15 0013 668B06   mov (%esi), %ax
  16 0016 8B06     mov (%esi), %eax
  17              
  18               .code64
  19               main64:
  20               // mov (%si), %ax
  21               // mov (%si), %eax
  22               // mov (%si), %rax
  23 0018 67668B06 mov (%esi), %ax
  24 001c 678B06   mov (%esi), %eax
  25 001f 67488B06 mov (%esi), %rax
  26 0023 668B06   mov (%rsi), %ax
  27 0026 8B06     mov (%rsi), %eax
  28 0028 488B06   mov (%rsi), %rax
  29              
GAS LISTING test.s page 2


DEFINED SYMBOLS
                            *ABS*:0000000000000000 test.s
              test.s:5      .text:0000000000000000 main16
              test.s:12     .text:000000000000000c main32
              test.s:19     .text:0000000000000018 main64

NO UNDEFINED SYMBOLS


ここで気が付くのは、同じ命令でも、違うコードが出ている。
例えば、
mov (%esi), %eax
16bitでは、67668B06
32bitでは、8B06
64bitでは、678B06
となっている。

67というのは、アドレスサイズプレフィックス
66というのは、オペランドサイズプレフィックス

これを見ると、各モードで、最も命令コードが短くなるアドレスサイズ、オペランドサイズが決まっているように見える。

16bitモードでは、アドレスサイズ、オペランドサイズが16bitが基本。
67を付けるとアドレスサイズが32bitになる。
66を付けるとオペランドサイズが32bitになる。

32bitモードでは、アドレスサイズ、オペランドサイズが32bitが基本。
67を付けるとアドレスサイズが16bitになる。
66を付けるとオペランドサイズが16bitになる。

64bitモードでは、アドレスサイズ64bit、オペランドサイズ32bitが基本。
67を付けるとアドレスサイズが16bitになる。
66を付けるとオペランドサイズが16bitになる。
48を付けるとオペランドサイズが64bitになる。

以下の表はアドレスサイズとオペランドサイズのbit幅に対し、どのようなプレフィックスを付ければいいのかを表している。


                  Mode
Addr Data   16bit 32bit 64bit
16   16      なし  6766   不可
16   32      66    67    不可
16   64      不可   不可   不可
32   16      67    66   6766
32   32     6766   なし   67
32   64      不可   不可  6748
64   16      不可   不可   66
64   32      不可   不可   なし
64   64      不可   不可   48