1

我想存储从系统启动到系统崩溃的调用函数地址或名称。在程序执行期间调用它们时,有什么方法可以从任何硬件寄存器中检索这些函数地址?

4

4 回答 4

2

gcc 文档

生成用于进入和退出函数的检测调用。在函数进入之后和函数退出之前,使用当前函数的地址及其调用站点调用以下分析函数。(在某些平台上, __builtin_return_address 在当前函数之外不起作用,因此调用站点信息可能无法用于分析函数。)

     void __cyg_profile_func_enter (void *this_fn,
                                     void *call_site);
     void __cyg_profile_func_exit  (void *this_fn,
                                     void *call_site);

例子:

#include <stdio.h>

void __cyg_profile_func_enter(void * this_fn, void * call_site)
{
  fprintf(stderr, "enter: %p %p\n", this_fn, call_site);
}

void __cyg_profile_func_exit(void * this_fn, void * call_site)
{
  fprintf(stderr, " exit: %p %p\n", this_fn, call_site);
}

void foo(void);
void bar(void);

void foo(void)
{
  bar();

  return;
}

void bar(void)
{
  return;
}

int main(void)
{
  bar();
  foo();

  return 0;
}

使用以下命令编译和链接:

gcc -finstrument-functions -finstrument-functions-exclude-function-list=__cyg_profile_func_enter,__cyg_profile_func_exit -Wall -g -o main main.c 

预期的输出将与此类似:

enter: 0x400665 0x7fcfedaf6c8d
enter: 0x400643 0x400681
 exit: 0x400643 0x400681
enter: 0x40061c 0x400686
enter: 0x400643 0x400633
 exit: 0x400643 0x400633
 exit: 0x40061c 0x400686
 exit: 0x400665 0x7fcfedaf6c8d
于 2013-07-02T10:36:46.003 回答
1

如果你在 linux 上:

您可以使用ptrace跟踪您的程序并检查寄存器。

要获取有关您的功能的信息,您可以使用libelf。(或使用 nm & objdump)

于 2013-07-02T10:08:39.173 回答
0

我只在*nix系统下做了一些类似的工作,所以我基于*nix为你提供我的解决方案。

我假设你使用gcc你的默认编译器,然后你最好在你的makefile中启用这两者-finstrument-functions-fdump-rtl-expand例如:

CFLAGS += -Wall -O -ggdb -Wstrict-prototypes -Wno-pointer-sign -finstrument-functions -fdump-rtl-expand

在此之后,您可以实现跟踪功能,例如,your_trace.c:

#include <stdio.h>
#include <stdlib.h>

/* Function prototypes with attributes */
void main_constructor( void )
    __attribute__ ((no_instrument_function, constructor));

void main_destructor( void )
    __attribute__ ((no_instrument_function, destructor));

void __cyg_profile_func_enter( void *, void * ) 
    __attribute__ ((no_instrument_function));

void __cyg_profile_func_exit( void *, void * )
    __attribute__ ((no_instrument_function));


static FILE *fp;


void main_constructor( void )
{
  fp = fopen( "trace.txt", "w" );
  if (fp == NULL) exit(-1);
}


void main_deconstructor( void )
{
  fclose( fp );
}


void __cyg_profile_func_enter( void *this, void *callsite )
{
  fprintf(fp, "E%p\n", (int *)this);
}


void __cyg_profile_func_exit( void *this, void *callsite )
{
  fprintf(fp, "X%p\n", (int *)this);
}

在这些之后,编译你的代码后,你会看到一个 *.map 文件,其中包含函数信息。也是用trace.c编译的,如果只是简单的运行编译后的输出文件,就会生成函数调用信息,即trace.txt文件,里面有函数地址,你可以用add2line查看每一个,也可以用pvtrace工具获取函数调用trace.txt,例如,我编译的名为 的程序DEMO,然后:

-> pvtrace ./DEMO

您将获得graph.dot并记录您的运行时函数调用。

或者

使用仅启用-ggdb和使用调试工具来查看每个函数地址,例如DDDGDB

于 2013-07-02T10:54:53.603 回答
0

您不能从函数本身中保存函数地址,也不能在执行期间访问函数名称,但您可以在调用函数之前保存它:

 savefptr(myfunction);
 savefname("myfunction");
 myfunction(a,b,c);

savefptr()和的适当定义savefname()

如果这样做是为了跟踪/调试(例如,您想生成一个日志以了解代码中发生了什么),那么使用__FILE__and__LINE__宏跟踪文件名和代码行可能会很好:

 fprintf (stderr, "I'm in:'%s' line %d.",
                          __FILE__, __LINE__);

如果可以避免它们,我建议不要使用编译器或操作系统细节。

于 2013-07-02T10:20:09.337 回答