Reading program counter directly

Can the program counter on Intel CPUs can be read directly (that is without 'tricks') in kernel mode or some other mode?


No, EIP / IP cannot be accessed directly, but in position-dependent code it's a link-time constant so you can use a nearby (or distant) symbol as an immediate.

   mov eax, nearby_label    ; in position-dependent code
nearby_label:

To get EIP or IP in position-independent 32-bit code:

        call _here
_here:  pop eax
; eax now holds the PC.

On CPUs newer than Pentium Pro (or PIII probably), call rel32 with rel32=0 is special-cased to not affect the return-address predictor stack. So this is efficient as well as compact on modern x86, and is what clang uses for 32-bit position-independent code.

On old 32-bit Pentium Pro CPUs, this would unbalance the call/return predictor stack, so prefer calling a function that does actually return, to avoid branch mispredicts on up to 15 or so future ret instructions in your parent functions. (Unless you're not going to return, or so rarely that it doesn't matter.) The return-address predictors stack will recover, though.

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.

In x86-64 mode, RIP can be read directly using a RIP-relative lea.

default rel           ; NASM directive: use RIP-relative by default

lea  rax, [_here]     ; RIP + 0
_here:

MASM or GNU .intel_syntax: lea rax, [rip]

AT&T syntax: lea 0(%rip), %rax


If you need the address of a specific instruction, usually something like this does the trick:

thisone: 
   mov (e)ax,thisone

(Note: On some assemblers this might do the wrong thing and read a word from [thisone], but there's usually some syntax for getting the assembler to do the right thing.)

If your code is statically loaded to a specific address, the assembler already knows (if you told it the right starting address) the absolute addresses of all instructions. Dynamically loaded code, say as a part of an application on any modern OS, will get the right address thanks to address relocation done by the dynamic linker (provided the assembler is smart enough to generate the relocation tables, which they usually are).