I want to write a C program which would print the contents of the program counter PC
. Can this be done from user space, or assembly, or some specific kernel routines are used?

- 11,136
- 23
- 74
- 97
-
Debugger's code controls/access PC so there should be a way I think using inline assembly. – Grijesh Chauhan Aug 21 '13 at 06:13
-
@GrijeshChauhan: Do you think there might be a GCC extension for program counter? – manav m-n Aug 21 '13 at 06:17
-
1Yes I am not very much sure but I feel there should be some way to work around this note: if in a code you have a `lable:` then you can print its address using `&lable` (this makes me to say yes). Very rear code (legitimate) use this kind of instructions but frequently uses in malware code so its a good Feature. – Grijesh Chauhan Aug 21 '13 at 06:22
-
This link might be of help? http://stackoverflow.com/questions/599968/reading-program-counter-directly – plaknas Aug 21 '13 at 06:32
-
1You haven't specified which PC value you want to print. The one right before the call to the print function..? – Michael Aug 21 '13 at 06:48
-
Why are you trying to do this? Is it just for experimentation or are you actually trying to do something with this. Because knowing the PC value is almost never something you care to know. If there's something you're trying to do, there's probably a better way to do it. Especially since adding an instruction will change the offset for the PC anyway. – ali-hussain Aug 21 '13 at 19:11
-
In answer to your question though: C allows directly accessing the address of a function or label. Debuggers usually take an exception so they have the PC from the exception. In ARM PC is a register and a mov instruction will just work. Beware of Thumb mode. There are a couple of cool tricks on this page: http://stackoverflow.com/questions/4062403/how-to-check-the-eip-value-with-assembly-language . Basically jump to a subroutine that copies the return address which should work in almost every architecture. LEA instruction can be used in x86. – ali-hussain Aug 21 '13 at 19:19
3 Answers
You should be able to determine the PC by using the __current_pc()
intrinsic in the ARM compiler toolchain (the ARM compiler supports many of the same extensions as GCC).* This is particular to ARM:
int main () {
printf("%#x\n", __current_pc());
printf("%#x\n", __current_pc());
printf("%#x\n", __current_pc());
return 0;
}
* Thanks to FrankH. for pointing out the presence of
__current_pc()
In general, the PC gets saved as the return address in a function call. On non-ARM linux systems with GCC, you can call __builtin_return_address(0)
to obtain the return address of the current function call context. Obtaining the program counter in this way incurs the penalty of adding a function call, but it avoids inline assembly, so this technique is portable to any system supported by GCC.
__attribute__ ((__noinline__))
void * get_pc () { return __builtin_return_address(0); }
int main () {
printf("%p\n", get_pc());
printf("%p\n", get_pc());
printf("%p\n", get_pc());
return 0;
}
When I run the above program on my x86
system, it produces the output:
0x8048432
0x8048447
0x804845c
When disassembled in gdb
:
Dump of assembler code for function main:
0x08048424 <+0>: push %ebp
0x08048425 <+1>: mov %esp,%ebp
0x08048427 <+3>: and $0xfffffff0,%esp
0x0804842a <+6>: sub $0x10,%esp
0x0804842d <+9>: call 0x804841c <get_pc>
0x08048432 <+14>: mov %eax,0x4(%esp)
0x08048436 <+18>: movl $0x8048510,(%esp)
0x0804843d <+25>: call 0x80482f0 <printf@plt>
0x08048442 <+30>: call 0x804841c <get_pc>
0x08048447 <+35>: mov %eax,0x4(%esp)
0x0804844b <+39>: movl $0x8048510,(%esp)
0x08048452 <+46>: call 0x80482f0 <printf@plt>
0x08048457 <+51>: call 0x804841c <get_pc>
0x0804845c <+56>: mov %eax,0x4(%esp)
0x08048460 <+60>: movl $0x8048510,(%esp)
0x08048467 <+67>: call 0x80482f0 <printf@plt>
0x0804846c <+72>: mov $0x0,%eax
0x08048471 <+77>: leave
0x08048472 <+78>: ret
End of assembler dump.

- 69,070
- 8
- 110
- 193
-
1
-
apologies, in ARM I should say, return address (`LR`) and `PC` are _not_ the same thing. – FrankH. Aug 21 '13 at 14:23
-
And more ... the reason why yours works is because you _force a function call_ (which makes `LR` within your func the `PC` of the call site). This is _unnecessarily inefficient_. – FrankH. Aug 21 '13 at 14:24
-
-
ARM's compilers <= 5 are not variants of GCC. ARM's compiler 6 is a variant of LLVM. – rsaxvc Jun 22 '14 at 18:49
-
On ARM, you can use:
static __inline__ void * get_pc(void) {
void *pc;
asm("mov %0, pc" : "=r"(pc));
return pc;
}
Or this one should work as well:
static __inline__ void * get_pc(void) {
register void * pc __asm__("pc");
__asm__("" : "=r"(pc));
return pc;
}
The forced inlining is important here, because that ensures you retrieve PC
as per the call site.
Edit: just remembered, __current_pc()
ARM intrinsic. GCC should have this as well.

- 17,675
- 3
- 44
- 63
-
1on aarch64 the following seems to work (in your first function): `asm("adr %0, ." : "=r"(pc));`. – Andre Holzner Sep 11 '19 at 11:16
Well I think you can get the information by inserting assembly blocks inside your C code. This will totally depend on your compiler and the register set of your platform. I did it like this:
int get_counter1()
{
__asm__ ("lea (%rip), %eax ") ;
}
int get_counter2()
{
int x = 0;
__asm__ ("lea (%rip), %eax") ;
}
int main()
{
printf("%x\n",get_counter1());
printf("%x\n",get_counter2());
return 0;
}
4004ce
4004e1