Search code examples
clinuxassemblylinux-kernelarm

How to print exact value of the program counter in C


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?


Solution

  • 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 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.