9

我想编写一个 C 程序来打印程序计数器的内容PC。这可以从用户空间、程序集或使用某些特定的内核例程来完成吗?

4

3 回答 3

14

您应该能够使用 ARM 编译器工具链中的__current_pc()内在函数来确定 PC (ARM 编译器支持许多与 GCC 相同的扩展)。*这是 ARM 特有的:

int main () {
    printf("%#x\n", __current_pc());
    printf("%#x\n", __current_pc());
    printf("%#x\n", __current_pc());
    return 0;
}

* 感谢 FrankH。用于指出存在__current_pc()

通常,PC 被保存为函数调用中的返回地址。在带有 GCC 的非 ARM 系统上,可以调用__builtin_return_address(0)获取当前函数调用上下文的返回地址。以这种方式获取程序计数器会导致添加函数调用的代价,但它避免了内联汇编,因此这种技术可以移植到 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;
}

当我在我的系统上运行上述程序时x86,它会产生输出:

0x8048432
0x8048447
0x804845c

拆卸时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.
于 2013-08-21T07:19:58.417 回答
9

在 ARM 上,您可以使用:

static __inline__ void * get_pc(void)  {
    void *pc;
    asm("mov %0, pc" : "=r"(pc));
    return pc;
}

或者这个也应该工作:

static __inline__ void * get_pc(void) {
    register void * pc __asm__("pc");
    __asm__("" : "=r"(pc));
    return pc;
}

强制内联在这里很重要,因为这可以确保您PC根据调用站点进行检索。

编辑:刚刚记得,__current_pc()ARM 内在. GCC 也应该有这个。

于 2013-08-21T14:25:48.070 回答
3

好吧,我认为您可以通过在 C 代码中插入汇编块来获取信息。这完全取决于您的编译器和平台的寄存器集。我是这样做的:

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

于 2013-08-21T06:59:09.380 回答