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

时间:2022-12-07 16:00:13

How to get function's name from function's pointer in C?

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

Edit: The real case is: I'm writing a linux kernel module and I'm calling kernel functions. Some of these functions are pointers and I want to inspect the code of that function in the kernel source. But I don't know which function it is pointing to. I thought it could be done because, when the system fails (kernel panic) it prints out in the screen the current callstack with function's names. But, I guess I was wrong... am I?

编辑:实际情况是:我正在编写一个linux内核模块,并调用内核函数。其中一些函数是指针,我想在内核源代码中检查该函数的代码。但是我不知道它指向哪个函数。我认为可以这样做,因为当系统失败(内核恐慌)时,它会在屏幕上打印出带有函数名的当前调用堆栈。但是,我想我错了……我是吗?

11 个解决方案

#1


27  

That's not directly possible without additional assistance.

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

You could:

你可以:

  1. maintain a table in your program mapping function pointers to names

    在程序映射函数中维护一个指向名称的表

  2. examine the executable's symbol table, if it has one.

    检查可执行文件的符号表,如果它有一个。

The latter, however, is hard, and is not portable. The method will depend on the operating system's binary format (ELF, a.out, .exe, etc), and also on any relocation done by the linker.

然而,后者是硬的,并且是不可移植的。该方法将取决于操作系统的二进制格式(ELF, a)。输出、.exe等),以及链接器所做的任何重新定位。

EDIT: Since you've now explained what your real use case is, the answer is actually not that hard. The kernel symbol table is available in /proc/kallsyms, and there's an API for accessing it:

编辑:既然您已经解释了真正的用例是什么,那么答案实际上并不是那么难。内核符号表可以在/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)

For simple debug purposes the latter will probably do exactly what you need - it takes the address, formats it, and sends it to printk.

为了简单的调试目的,后者可能会做您需要做的事情——它接受地址,格式化它,并将它发送到printk。

#2


58  

I'm surprised why everybody says it is not possible. It is possible on Linux for non-static functions.

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

I know at least two ways to achieve this.

我知道至少有两种方法可以做到这一点。

There are GNU functions for backtrace printing: backtrace() and backtrace_symbols() (See man). In your case you don't need backtrace() as you already have function pointer, you just pass it to backtrace_symbols().

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

Example (working code):

例子(工作代码):

#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;
}

Compile with gcc test.c -rdynamic

与gcc编译测试。c -rdynamic

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

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

It gives you binary name, function name, pointer offset from function start and pointer value so you can parse it.

它提供了二进制名、函数名、函数起始的指针偏移量和指针值,以便您可以对其进行解析。

Another way is to use dladdr() (another extension), I guess print_backtrace() uses dladdr(). dladdr() returns Dl_info structure that has function name in dli_sname field. I don't provide code example here but it is obvious - see man dladdr for details.

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

NB! Both approaches require function to be non-static!

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

Well, there is one more way - use debug information using libdwarf but it would require unstripped binary and not very easy to do so I don't recommend it.

还有一种方法——使用libdwarf使用调试信息,但是它需要不带二进制文件,而且不太容易实现,所以我不建议这样做。

#3


12  

In the Linux kernel, you can use directly "%pF" format of printk !

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

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

#4


5  

The following works me on Linux:

以下是我在Linux上的工作:

  • printf the address of the function using %p
  • 使用%p打印函数的地址
  • Then do an nm <program_path> | grep <address> (without the 0x prefix)
  • 然后执行nm | grep
    (没有0x前缀)
  • It should show you the function name.
  • 它应该显示函数名。

It works only if the function in question is in the same program (not in a dynamically linked library or something).

只有当所讨论的函数在同一个程序中(而不是在动态链接库或其他程序中),它才能工作。

If you can find out the load addresses of the loaded shared libraries, you can subtract the address from the printed number, and use nm on the library to find out the function name.

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

#5


2  

You can't diectly but you can implement a different approach to this problem if you want. You can make a struct pointer instead pointing to a function as well as a descriptive string you can set to whatever you want. I also added a debugging posebilety since you problably do not want these vars to be printet forever.

你不能确定,但你可以用不同的方法来解决这个问题。您可以创建一个struct指针,而不是指向一个函数以及一个描述性字符串,您可以将其设置为任何您想要的。我还添加了一个调试posebilety,因为您可能不希望这些vars永远是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

#6


2  

If the list of functions that can be pointed to is not too big or if you already suspect of a small group of functions you can print the addresses and compare them to the one used during execution. Ex:

如果可以指向的函数列表不太大,或者您已经怀疑有一组函数,那么可以打印地址并与执行过程中使用的地址进行比较。例:

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);
    }
}

Output:

输出:

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

This approach will work for static functions too.

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

#7


1  

There is no way how to do it in general.

一般来说,没有办法做到这一点。

If you compile the corresponding code into a DLL/Shared Library, you should be able to enlist all entry points and compare with the pointer you've got. Haven't tried it yet, but I've got some experience with DLLs/Shared Libs and would expect it to work. This could even be implemented to work cross-plarform.

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

Someone else mentioned already to compile with debug symbols, then you could try to find a way to analyse these from the running application, similiar to what a debugger would do. But this is absolutely proprietary and not portable.

前面提到的其他人已经用调试符号来编译,然后您可以尝试找到一种方法来从正在运行的应用程序中分析这些代码,类似于调试器所做的工作。但这绝对是专有的,不能移植。

#8


0  

Check out Visual Leak Detector to see how they get their callstack printing working. This assumes you are using Windows, though.

检查视觉检漏器,看看他们是如何得到他们的callstack打印工作的。不过,这假定您正在使用Windows。

#9


0  

  1. Use kallsyms_lookup_name() to find the address of kallsyms_lookup.

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

  2. Use a function pointer that points to kallsyms_lookup, to call it.

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

#10


-2  

You can't. The function name isn't attached to the function by the time it's compiled and linked. It's all by memory address at that point, not name.

你不能。函数名在编译和链接时不会附加到函数上。这都是由内存地址决定的,而不是名字。

#11


-4  

You wouldn't know how you look like without a reflecting mirror. You'll have to use a reflection-capable language like C#.

如果没有镜子,你不会知道自己是什么样子。您必须使用具有反射能力的语言,如c#。

#1


27  

That's not directly possible without additional assistance.

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

You could:

你可以:

  1. maintain a table in your program mapping function pointers to names

    在程序映射函数中维护一个指向名称的表

  2. examine the executable's symbol table, if it has one.

    检查可执行文件的符号表,如果它有一个。

The latter, however, is hard, and is not portable. The method will depend on the operating system's binary format (ELF, a.out, .exe, etc), and also on any relocation done by the linker.

然而,后者是硬的,并且是不可移植的。该方法将取决于操作系统的二进制格式(ELF, a)。输出、.exe等),以及链接器所做的任何重新定位。

EDIT: Since you've now explained what your real use case is, the answer is actually not that hard. The kernel symbol table is available in /proc/kallsyms, and there's an API for accessing it:

编辑:既然您已经解释了真正的用例是什么,那么答案实际上并不是那么难。内核符号表可以在/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)

For simple debug purposes the latter will probably do exactly what you need - it takes the address, formats it, and sends it to printk.

为了简单的调试目的,后者可能会做您需要做的事情——它接受地址,格式化它,并将它发送到printk。

#2


58  

I'm surprised why everybody says it is not possible. It is possible on Linux for non-static functions.

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

I know at least two ways to achieve this.

我知道至少有两种方法可以做到这一点。

There are GNU functions for backtrace printing: backtrace() and backtrace_symbols() (See man). In your case you don't need backtrace() as you already have function pointer, you just pass it to backtrace_symbols().

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

Example (working code):

例子(工作代码):

#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;
}

Compile with gcc test.c -rdynamic

与gcc编译测试。c -rdynamic

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

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

It gives you binary name, function name, pointer offset from function start and pointer value so you can parse it.

它提供了二进制名、函数名、函数起始的指针偏移量和指针值,以便您可以对其进行解析。

Another way is to use dladdr() (another extension), I guess print_backtrace() uses dladdr(). dladdr() returns Dl_info structure that has function name in dli_sname field. I don't provide code example here but it is obvious - see man dladdr for details.

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

NB! Both approaches require function to be non-static!

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

Well, there is one more way - use debug information using libdwarf but it would require unstripped binary and not very easy to do so I don't recommend it.

还有一种方法——使用libdwarf使用调试信息,但是它需要不带二进制文件,而且不太容易实现,所以我不建议这样做。

#3


12  

In the Linux kernel, you can use directly "%pF" format of printk !

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

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

#4


5  

The following works me on Linux:

以下是我在Linux上的工作:

  • printf the address of the function using %p
  • 使用%p打印函数的地址
  • Then do an nm <program_path> | grep <address> (without the 0x prefix)
  • 然后执行nm | grep
    (没有0x前缀)
  • It should show you the function name.
  • 它应该显示函数名。

It works only if the function in question is in the same program (not in a dynamically linked library or something).

只有当所讨论的函数在同一个程序中(而不是在动态链接库或其他程序中),它才能工作。

If you can find out the load addresses of the loaded shared libraries, you can subtract the address from the printed number, and use nm on the library to find out the function name.

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

#5


2  

You can't diectly but you can implement a different approach to this problem if you want. You can make a struct pointer instead pointing to a function as well as a descriptive string you can set to whatever you want. I also added a debugging posebilety since you problably do not want these vars to be printet forever.

你不能确定,但你可以用不同的方法来解决这个问题。您可以创建一个struct指针,而不是指向一个函数以及一个描述性字符串,您可以将其设置为任何您想要的。我还添加了一个调试posebilety,因为您可能不希望这些vars永远是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

#6


2  

If the list of functions that can be pointed to is not too big or if you already suspect of a small group of functions you can print the addresses and compare them to the one used during execution. Ex:

如果可以指向的函数列表不太大,或者您已经怀疑有一组函数,那么可以打印地址并与执行过程中使用的地址进行比较。例:

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);
    }
}

Output:

输出:

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

This approach will work for static functions too.

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

#7


1  

There is no way how to do it in general.

一般来说,没有办法做到这一点。

If you compile the corresponding code into a DLL/Shared Library, you should be able to enlist all entry points and compare with the pointer you've got. Haven't tried it yet, but I've got some experience with DLLs/Shared Libs and would expect it to work. This could even be implemented to work cross-plarform.

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

Someone else mentioned already to compile with debug symbols, then you could try to find a way to analyse these from the running application, similiar to what a debugger would do. But this is absolutely proprietary and not portable.

前面提到的其他人已经用调试符号来编译,然后您可以尝试找到一种方法来从正在运行的应用程序中分析这些代码,类似于调试器所做的工作。但这绝对是专有的,不能移植。

#8


0  

Check out Visual Leak Detector to see how they get their callstack printing working. This assumes you are using Windows, though.

检查视觉检漏器,看看他们是如何得到他们的callstack打印工作的。不过,这假定您正在使用Windows。

#9


0  

  1. Use kallsyms_lookup_name() to find the address of kallsyms_lookup.

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

  2. Use a function pointer that points to kallsyms_lookup, to call it.

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

#10


-2  

You can't. The function name isn't attached to the function by the time it's compiled and linked. It's all by memory address at that point, not name.

你不能。函数名在编译和链接时不会附加到函数上。这都是由内存地址决定的,而不是名字。

#11


-4  

You wouldn't know how you look like without a reflecting mirror. You'll have to use a reflection-capable language like C#.

如果没有镜子,你不会知道自己是什么样子。您必须使用具有反射能力的语言,如c#。