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

时间:2008-12-08 22:18:18

标签: c function-pointers

如何从C中的function's pointer获取函数的名称?

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

12 个答案:

答案 0 :(得分:63)

我很惊讶为什么每个人都说这是不可能的。在Linux上可以使用非静态函数。

我至少知道两种方法来实现这一目标。

有用于回溯打印的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]

它为您提供二进制名称,函数名称,函数start和指针值的指针偏移量,以便您可以解析它。

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

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

嗯,还有一种方法 - 使用libdwarf使用调试信息,但它需要未经剥离的二进制文件,而且不容易这样做,我不推荐它。

答案 1 :(得分:29)

如果没有其他帮助,这是不可能的。

你可以:

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

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

  3. 然而,后者很难,而且不便携。该方法将取决于操作系统的二进制格式(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)
    

    出于简单的调试目的,后者可能完全符合您的需要 - 它需要地址,格式化并将其发送到printk,或者您可以将printk%pF一起使用格式说明符。

答案 2 :(得分:15)

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

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

答案 3 :(得分:5)

以下内容适用于Linux:

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

只有当相关函数在同一个程序中时(不在动态链接的库中),它才有效。

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

答案 4 :(得分:2)

你不能完全,但如果你愿意,你可以针对这个问题实施不同的方法。你可以创建一个结构指针,而不是指向一个函数,也可以设置一个描述性的字符串,你可以设置为你想要的任何东西。 我还添加了一个调试posebilety,因为你可能不希望这些变量永远是打印的。

// 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

答案 5 :(得分:2)

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

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

这种方法也适用于静态函数。

答案 6 :(得分:1)

一般来说,无法做到这一点。

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

其他人已提到要使用调试符号进行编译,然后您可以尝试从运行的应用程序中找到一种分析这些的方法,类似于调试器将要执行的操作。 但这绝对是专有的,不可移植。

答案 7 :(得分:0)

查看Visual Leak Detector,看看他们如何让他们的callstack打印工作。这假设您使用的是Windows。

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

不是确切的问题要求,但在阅读了这里的答案之后 我虽然是针对我的类似问题的解决方案:

/**
* search methods */
static int starts(const char *str, const char *c);
static int fuzzy(const char *str, const char *c);

int (*search_method)(const char *, const char *);

/* asign the search_method and do other stuff */
[...]

printf("The search method is %s\n", search_method == starts ? "starts" : "fuzzy")

如果您的程序非常需要此功能,则可以在XMacro中定义方法名称以及字符串,然后在代码中使用#define X(name, str) ... #undef X从函数名称中获取相应的字符串。

答案 10 :(得分:-3)

你做不到。函数名称在编译和链接时未附加到函数。这一切都是通过内存地址,而不是名称。

答案 11 :(得分:-4)

如果没有反射镜,你会不知道自己的样子。你必须使用像C#这样具有反射能力的语言。