我想写一个C程序来打印程序计数器PC
的内容。 这可以从用户空间或程序集完成,还是使用一些特定的内核例程?
您应该可以通过使用ARM编译器工具链中的__current_pc()
ARM编译器支持许多与GCC相同的扩展)来确定PC。 *这对于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 Linux系统上,可以调用__builtin_return_address(0)
来获取当前函数调用上下文的返回地址。 以这种方式获得程序计数器会导致添加一个函数调用的惩罚,但是它避免了内联汇编,所以这种技术对于GCC支持的任何系统是可移植的。
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.
在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内在 。 海湾合作委员会也应该有这个。
那么我认为你可以通过在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