Intel CPUのプログラムカウンタは、カーネルモードまたはその他のモードで直接(つまり、「トリック」なしで)読み取ることができますか?
いいえ、EIP/IPに直接アクセスすることはできませんが、位置に依存するコードでは、リンク時定数であるため、近くの(または離れた)シンボルを即時として使用できます。
_ mov eax, nearby_label ; in position-dependent code
nearby_label:
_
位置に依存しない32ビットコードでEIPまたはIPを取得するには:
_ call _here
_here: pop eax
; eax now holds the PC.
_
Pentium Pro(またはおそらくPIII)よりも新しいCPUでは、 _call rel32
_ with rel32 = 0は、リターンアドレス予測スタックに影響を与えないように特別な場合です 。したがって、これは最新のx86で効率的かつコンパクトであり、clangが32ビットの位置に依存しないコードに使用するものです。
古い32ビットのPentiumPro CPUでは、これにより呼び出し/戻り予測スタックのバランスが崩れるため、実際に戻る関数を呼び出すことをお勧めします。これにより、親の最大15個程度の将来のret
命令での分岐の予測ミスを回避できます。関数。 (戻ってこない場合、またはめったに問題にならない場合を除きます。)ただし、return-addresspredictorsスタックは回復します。
_get_retaddr_ppro:
mov eax, [esp]
ret ; keeps the return-address predictor stack balanced
; even on CPUs where call +0 isn't a no-op.
_
x86-64モードでは、RIPはRIP相対lea
を使用して直接読み取ることができます。
_default rel ; NASM directive: use RIP-relative by default
lea rax, [_here] ; RIP + 0
_here:
_
MASMまたはGNU _.intel_syntax
_:_lea rax, [rip]
_
AT&T構文:lea 0(%rip), %rax
特定の命令のアドレスが必要な場合は、通常、次のような方法でうまくいきます。
thisone:
mov (e)ax,thisone
(注:一部のアセンブラーでは、これが間違ったことを行い、[thisone]からWordを読み取る場合がありますが、通常、アセンブラーに正しいことを実行させるための構文がいくつかあります。)
コードが特定のアドレスに静的にロードされている場合、アセンブラはすべての命令の絶対アドレスをすでに知っています(正しい開始アドレスを指定した場合)。動的にロードされたコードは、たとえば最新のOS上のアプリケーションの一部として、動的リンカーによって行われるアドレス再配置のおかげで正しいアドレスを取得します(アセンブラーが再配置テーブルを生成するのに十分スマートである場合)。
X86-64では、次のように実行できます。
lea rax,[rip] (48 8d 05 00 00 00 00)
X86には命令ポインタ(EIP)を直接読み取る命令はありません。少しインラインアセンブリを使用して、アセンブルされている現在の命令のアドレスを取得できます。
// GCC inline assembler; for MSVC, syntax is different
uint32_t eip;
__asm__ __volatile__("movl $., %0", : "=r"(eip));
.
アセンブラディレクティブは、アセンブラによって現在の命令のアドレスに置き換えられます。上記のスニペットを関数呼び出しでラップすると、毎回同じアドレス(その関数内)を取得することに注意してください。より使いやすいC関数が必要な場合は、代わりに非インラインアセンブリを使用できます。
// In a C header file:
uint32_t get_eip(void);
// In a separate Assembly (.S) file:
.globl _get_eip
_get_eip:
mov 0(%esp), %eax
ret
つまり、命令ポインタを取得するたびに、追加の関数呼び出しが必要になるため、効率が少し低下します。この方法で実行しても、リターンアドレススタック(RAS)が破壊されないことに注意してください。リターンアドレススタックは、RET命令の 分岐ターゲット予測 を容易にするためにプロセッサによって内部的に使用されるリターンアドレスの個別のスタックです。
CALL命令があるたびに、現在のEIPがRASにプッシュされ、RET命令があるたびに、RASがポップされ、最上位の値が分岐ターゲットとして使用されます。その命令の予測。RAS( Codyのソリューション のように各CALLをRETと一致させないなど)を台無しにすると、次のようになります。不要な分岐予測が大量に発生し、プログラムの速度が低下します。このメソッドは、CALL命令とRET命令のペアが一致しているため、RASを破壊しません。
ラベルを値として使用して実行されているアドレスにアクセスするには、アーキテクチャに依存しない(ただし、gccに依存する)方法があります。
http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
void foo()
{
void *current_address = $$current_address_label;
current_address_label:
....
}
これは/ proc/statからも読み取ることができます。 procのマンページを確認してください。