如何从C函数的指针获取函数的名字?
如何从C 函数的指针获取函数的名字?
编辑:真正的情况是:我在写一个Linux内核模块,我正在调用内核函数。 其中一些函数是指针,我想检查内核源代码中的那个函数的代码。 但是我不知道它指向哪个函数。 我认为可以这样做,因为当系统发生故障(内核恐慌)时,它会在屏幕上打印出当前具有函数名称的callstack。 但是,我想我错了…是吗?
没有额外的协助,这是不可能的。
你可以:
-
在程序映射函数指针名称中维护一个表
-
检查可执行文件的符号表,如果有的话。
后者,但是,很难,而且不便携。 该方法将取决于操作系统的二进制格式(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! 两种方法都要求函数是非静态的!
那么,还有一种方法 – 使用libdwarf
debugging信息,但它会需要无痕的二进制文件,不是很容易做到这一点,我不推荐它。
在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。
-
使用
kallsyms_lookup_name()
来查找kallsyms_lookup
的地址。 -
使用指向
kallsyms_lookup
的函数指针来调用它。
你不能。 函数名称在编译和链接时不会附加到函数中。 这一切都是由内存地址,而不是名称。
没有反光镜,你不会知道你的样子。 你将不得不使用像C#这样的reflectionfunction的语言。