オプション1は実際にはうまくいくようです。次のプログラムを検討してください。
#include <stdio.h>
void f(int *p) {
__asm mov eax, p
__asm mov ebx, [eax]
// break here
}
void main()
{
int i = 0x12345678;
f(&i);
}
Visual Studio 2008 SP1、単一ファイルの C++ プログラム、およびデバッグ ビルドでは、f() の最後にステップインすると、レジスタ ウィンドウに次のように表示されます。
EAX = 004DF960
EBX = 12345678
ECX = 00000000
EDX = 00000001
ESI = 00000000
EDI = 004DF884
EIP = 003013C3
ESP = 004DF7B8
EBP = 004DF884
EFL = 00000202
EAX、EBX、および ESP の値を見ると、EAX で必要なポインターが実際にあるというかなり良い証拠のように見えます。EAX のアドレスは ESP のアドレスよりも少しだけ高く、スタックの 1 フレーム上にあることを示唆しています。EBX にロードされた参照解除された値は、正しいアドレスを取得したことを示しています。
グローバルのアドレスの読み込みは微妙に異なります。次の例では、LEA 命令を使用してタスクを実行します。
#include <stdio.h>
int a[] = { 0x1234, 0x4567 };
void main()
{
// __asm mov eax, a ; interpreted as eax <- a[0]
__asm lea eax, a ; interpreted as eax <- &a[0]
__asm mov ebx, [eax]
__asm mov ecx, [eax+4]
// break here
}
main() の最後までステップ実行すると、次のレジスタ値が得られます。EAX は配列の最初の要素のアドレスを取得しますが、EBX と ECX はそのメンバーの値を取得します。
EAX = 00157038
EBX = 00001234
ECX = 00004567
EDX = 00000001
ESI = 00000000
EDI = 0047F800
EIP = 001513C9
ESP = 0047F734
EBP = 0047F800
EFL = 00000202
魔法は LEA 命令自体にはありません。むしろ、__asm ディレクティブは、MOV 命令と LEA 命令のどちらが使用されているかによって、C/C++ 識別子の扱いが異なるようです。これは、MOV 命令のコメントを外したときの同じプログラムの ASM ダンプです。LEA 命令がそのオフセットを取得する一方で、MOV 命令がその引数 (DWORD PTR) として a[] の内容を取得する方法に注意してください。
; ...
PUBLIC ?a@@3PAHA ; a
_DATA SEGMENT
?a@@3PAHA DD 01234H ; a
DD 04567H
_DATA ENDS
; ...
mov eax, DWORD PTR ?a@@3PAHA
lea eax, OFFSET ?a@@3PAHA
mov ebx, DWORD PTR [eax]
mov ecx, DWORD PTR [eax+4]
; ...