如何从C函数的指针获取函数的名字?

如何从C 函数的指针获取函数的名字?

编辑:真正的情况是:我在写一个Linux内核模块,我正在调用内核函数。 其中一些函数是指针,我想检查内核源代码中的那个函数的代码。 但是我不知道它指向哪个函数。 我认为可以这样做,因为当系统发生故障(内核恐慌)时,它会在屏幕上打印出当前具有函数名称的callstack。 但是,我想我错了…是吗?

没有额外的协助,这是不可能的。

你可以:

  1. 在程序映射函数指针名称中维护一个表

  2. 检查可执行文件的符号表,如果有的话。

后者,但是,很难,而且不便携。 该方法将取决于操作系统的二进制格式(ELF,a.out,.exe等),也取决于链接器完成的任何重定位。

编辑:既然你现在已经解释了你的真实用例,答案其实并不难。 内核符号表在/proc/kallsyms ,并且有一个用于访问它的API:

 #include <linux/kallsyms.h> const char *kallsyms_lookup(unsigned long addr, unsigned long *symbolsize, unsigned long *ofset, char **modname, char *namebuf) void print_symbol(const char *fmt, unsigned long addr) 

为了简单的debugging目的,后者可能会完全按照你的需要来做 – 取得地址,格式化后发送给printk

我很惊讶为什么大家都说这是不可能的。 Linux在非静态function上是可能的。

我知道至less有两种方法来实现这一点。

有用于回溯打印的GNU函数: backtrace()backtrace_symbols() (参见man )。 在你的情况下,你不需要backtrace()因为你已经有了函数指针,你只需要将它传递给backtrace_symbols()

示例(工作代码):

 #include <stdio.h> #include <execinfo.h> void foo(void) { printf("foo\n"); } int main(int argc, char *argv[]) { void *funptr = &foo; backtrace_symbols_fd(&funptr, 1, 1); return 0; } 

gcc test.c -rdynamic编译

输出: ./a.out(foo+0x0)[0x8048634]

它为您提供二进制名称,函数名称,从函数开始的指针偏移量和指针值,以便您可以parsing它。

另一种方法是使用dladdr() (另一个扩展),我猜print_backtrace()使用dladdr()dladdr()返回在dli_sname字段中具有函数名称的Dl_info结构。 我在这里没有提供代码示例,但很明显 – 详细信息请参阅man dladdr

NB! 两种方法都要求函数是非静态的!

那么,还有一种方法 – 使用libdwarfdebugging信息,但它会需要无痕的二进制文件,不是很容易做到这一点,我不推荐它。

在Linux内核中,可以直接使用printk的“%pF”格式!

 void *func = &foo; printk("func: %pF at address: %p\n", func, func); 

以下是我在Linux上的作品:

  • 使用%p打印函数的地址
  • 然后做一个nm <program_path> | grep <address> nm <program_path> | grep <address> (不带0x前缀)
  • 它应该显示你的function名称。

它只有在有问题的函数是在同一个程序(不在dynamic链接库或什么的)中才有效。

如果您可以find加载的共享库的加载地址,则可以从打印的数字中减去地址,然后在库上使用nm来查找函数名称。

你不能小心翼翼,但如果你愿意,你可以采取不同的方法来解决这个问题。 你可以创build一个结构指针,而不是指向一个函数以及一个描述性的string,你可以设置任何你想要的。 我也添加了一个debuggingposebilety,因为你可能不希望这些variables永远printet。

 // Define it like this typedef struct { char *dec_text; #ifdef _DEBUG_FUNC void (*action)(char); #endif } func_Struct; // Initialize it like this func_Struct func[3]= { #ifdef _DEBUG_FUNC {"my_Set(char input)",&my_Set}}; {"my_Get(char input)",&my_Get}}; {"my_Clr(char input)",&my_Clr}}; #else {&my_Set}}; {&my_Get}}; {&my_Clr}}; #endif // And finally you can use it like this func[0].action( 0x45 ); #ifdef _DEBUG_FUNC printf("%s",func.dec_text); #endif 

如果可以指向的函数列表不是太大,或者如果您已经怀疑有一小组函数,则可以打印这些地址并将其与执行期间使用的地址进行比较。 例如:

 typedef void (*simpleFP)(); typedef struct functionMETA { simpleFP funcPtr; char * funcName; } functionMETA; void f1() {/*do something*/} void f2() {/*do something*/} void f3() {/*do something*/} int main() { void (*funPointer)() = f2; // you ignore this funPointer(); // this is all you see printf("f1 %p\n", f1); printf("f2 %p\n", f2); printf("f3 %p\n", f3); printf("%p\n", funPointer); // if you want to print the name struct functionMETA arrFuncPtrs[3] = {{f1, "f1"}, {f2, "f2"} , {f3, "f3"}}; int i; for(i=0; i<3; i++) { if( funPointer == arrFuncPtrs[i].funcPtr ) printf("function name: %s\n", arrFuncPtrs[i].funcName); } } 

输出:

 f1 0x40051b f2 0x400521 f3 0x400527 0x400521 function name: f2 

这种方法也适用于静态function。

一般来说,如何做到这一点是没有办法的。

如果您将相应的代码编译到DLL /共享库中,您应该能够使用所有入口点并与您获得的指针进行比较。 还没有尝试过,但我有一些DLLs /共享库的经验,并会期望它的工作。 这甚至可以实施交叉计划。

其他人提到已经使用debugging符号进行编译,那么您可以尝试从正在运行的应用程序中find一种分析方法,类似于debugging器将执行的操作。 但这是绝对专有的,不是便携式的。

查看Visual Leak Detector ,了解他们如何获取其堆栈打印工作。 不过,这假定你正在使用Windows。

  1. 使用kallsyms_lookup_name()来查找kallsyms_lookup的地址。

  2. 使用指向kallsyms_lookup的函数指针来调用它。

你不能。 函数名称在编译和链接时不会附加到函数中。 这一切都是由内存地址,而不是名称。

没有反光镜,你不会知道你的样子。 你将不得不使用像C#这样的reflectionfunction的语言。