web-dev-qa-db-ja.com

x86アセンブリポインター

これが馬鹿げた質問ではないことを願っていますが、アセンブリのポインタに心を包み込もうとしています。

正確な違いは何ですか:

mov eax, ebx

そして

mov [eax], ebx

そしていつdword ptr [eax] 使用すべきです?

また、mov eax, [ebx]コンパイルエラーが発生します。これはなぜですか?

14
Duxa

既に述べたように、オペランドを括弧で囲むと、そのオペランドはCのポインターであるかのようにdereferencedになります。つまり、 、括弧は、値を読み取ることを意味しますfrom(または値を保存するinto)その値を直接読み取るのではなく、そのメモリの場所。

したがって、この:

mov  eax, ebx

単にebxの値をeaxにコピーします。擬似C表記では、これはeax = ebxになります。

これに対して:

mov  eax, [ebx]

ebxのコンテンツを逆参照し、eaxにポイント先の値を保存します。擬似C表記では、これはeax = *ebxになります。

最後に、これ:

mov  [eax], ebx

ebxの値をeaxが指すメモリ位置に保存します。繰り返しますが、疑似C表記では*eax = ebxです。


ここのレジスタは、シンボリック変数名などのメモリオペランドで置き換えることもできます。したがって、この:

mov  eax, [myVar]

変数myVarのアドレスを逆参照し、その変数のcontentseax = myVarのようなeaxに格納します。

対照的に、これ:

mov  eax, myVar

eax = &myVarのように、変数myVaraddresseaxに保存します。

少なくとも、それがmostアセンブラーの仕組みです。 Microsoftのアセンブラー(MASMと呼ばれる)、およびMicrosoft C/C++コンパイラーのインラインアセンブリは、少し異なります。上記の2つの命令を同等のものとして扱い、基本的にメモリオペランドを囲む括弧を無視します

MASMで変数のアドレスを取得するには、OFFSETキーワードを使用します。

mov  eax, OFFSET myVar

ただし、MASMにはこの寛容な構文があり、だらしなくすることができますが、そうすべきではありません。変数を間接参照してその実際の値を取得する場合は、常に括弧を含めます。適切な構文を使用して明示的にコードを記述した場合、間違った結果が得られることはありません。他の人が理解しやすくなります。さらに、MASMの「私が書いたものではなく、私が意味することをする」松葉杖に頼るのではなく、他のアセンブラーが書くと予想される方法でコードを書く習慣を身に付けるように強制します。

「私が書いたものではなく、私が意味することをする」松葉杖について言えば、MASMは一般に、変数のサイズを知っているので、オペランドサイズ指定子を省略することもできます。しかし、ここでも、明確さと一貫性のためにそれを書くことをお勧めします。したがって、myVarintである場合、次のようにします。

mov  eax, DWORD PTR [myVar]    ; eax = myVar

または

mov  DWORD PTR [myVar], eax    ; myVar = eax

この表記は necessaryNASMのような他のアセンブラーでは は強く型付けされておらず、myVarDWORDサイズのメモリ位置です。

レジスタの名前がそのサイズを示しているため、レジスタオペランドを間接参照する場合、これはまったく必要ありません。 alahは常にBYTEサイズ、axは常にWordサイズ、eaxは常にDWORD- sized、およびraxは常にQWORD- sizedです。ただし、メモリオペランドの表記方法との一貫性を保つために、必要に応じて含めることは問題ありません。


また、mov eax, [ebx]を実行しようとすると、コンパイルエラーが発生します。これはなぜですか?

ええと…あなたはすべきではありません。これは、MSVCのインラインアセンブリでうまく組み立てられます。すでに見たように、それは次と同等です:

mov  eax, DWORD PTR [ebx]

ebxが指すメモリ位置が逆参照され、DWORDサイズの値がeaxにロードされることを意味します。


mov a, [eax]ができない理由eaxが指している場所へのポインタを "a"にすべきではないのですか?

いいえ。このオペランドの組み合わせは許可されていません。 MOV命令のドキュメント からわかるように、本質的に5つの可能性があります(代替のエンコーディングとセグメントを無視):

mov  register, register     ; copy one register to another
mov  register, memory       ; load value from memory into register
mov  memory,   register     ; store value from register into memory
mov  register, immediate    ; move immediate value (constant) into register
mov  memory,   immediate    ; store immediate value (constant) in memory

mov memory, memoryがないことに注意してください。これはあなたが試みていたものです。

ただし、単にコーディングすることで、aeaxが指しているものを指すようにすることができます。

mov  DWORD PTR [a], eax

aeaxは同じ値になりました。 eaxがポインタだった場合、aは同じメモリ位置へのポインタになります。

aが指しているvalueeaxを設定する場合は、次のようにする必要があります。

mov  eax, DWORD PTR [eax]    ; eax = *eax
mov  DWORD PTR [a], eax      ; a   = eax

もちろん、これはポインターを破壊し、逆参照された値に置き換えます。ポインターを失いたくない場合は、2番目の「スクラッチ」レジスターを使用する必要があります。何かのようなもの:

mov  edx, DWORD PTR [eax]    ; edx = *eax
mov  DWORD PTR [a], edx      ; a   = edx

これはすべてややこしいことです。 mov命令は、x86 ISAで多数の潜在的な意味でオーバーロードされています。これは、CISCアーキテクチャとしてのx86のルーツによるものです。対照的に、最新のRISCアーキテクチャは、レジスタ間移動、メモリロード、およびメモリストアをより適切に分離します。 x86はそれらすべてを単一のmov命令に詰め込みます。戻って今すぐ修正するには遅すぎます。構文に慣れる必要があり、場合によっては一目見ます。

22
Cody Gray