Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/39.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何获得函数';函数';Linux内核中的指针?_C_Linux Kernel_Function Pointers - Fatal编程技术网

C 如何获得函数';函数';Linux内核中的指针?

C 如何获得函数';函数';Linux内核中的指针?,c,linux-kernel,function-pointers,C,Linux Kernel,Function Pointers,如何从C中获取函数名 编辑:实际情况是:我正在编写linux内核模块并调用内核函数。其中一些函数是指针,我想在内核源代码中检查该函数的代码。但我不知道它指向哪个函数。我认为这是可以做到的,因为当系统出现故障(内核死机)时,它会在屏幕上打印出带有函数名的当前调用堆栈。但是,我想我错了。。。是吗?你不能。在编译和链接函数时,函数名未附加到函数。这一切都取决于当时的内存地址,而不是名称。如果没有额外的帮助,这是不可能直接实现的 你可以: 在程序中维护一个表,将函数指针映射到名称 检查可执行文件的符号表

如何从C中获取函数名


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

你不能。在编译和链接函数时,函数名未附加到函数。这一切都取决于当时的内存地址,而不是名称。

如果没有额外的帮助,这是不可能直接实现的

你可以:

  • 在程序中维护一个表,将函数指针映射到名称

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

  • 然而,后者很难实现,而且不便于携带。该方法将取决于操作系统的二进制格式(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)
    
    #包括
    const char*kallsyms_查找(无符号长地址,无符号长*符号化,
    无符号长*of set,char**modname,char*namebuf)
    无效打印符号(常量字符*fmt,无符号长地址)
    

    出于简单的调试目的,后者可能会完全满足您的需要—它获取地址、格式化并将其发送到
    printk
    ,或者您可以使用
    %pF
    格式说明符使用
    printk

    查看他们是如何使调用堆栈打印工作的。不过,这假设您使用的是Windows。

    您不能直接使用Windows,但如果需要,您可以实施不同的方法来解决此问题。您可以创建一个结构指针,而不是指向一个函数以及一个描述性字符串,您可以将其设置为任意值。 我还添加了一个调试posebilety,因为您可能不希望这些变量永远是prinet

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

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

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

    其他人已经提到使用调试符号进行编译,然后您可以尝试从运行的应用程序中找到分析这些符号的方法,类似于调试器所做的操作。
    但这是绝对专有的,不便于携带。

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

    我很奇怪为什么每个人都说这是不可能的。在Linux上,非静态函数是可能的

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

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

    示例(工作代码):

    #包括
    #包括
    无效foo(无效){
    printf(“foo\n”);
    }
    int main(int argc,char*argv[]){
    void*funptr=&foo;
    回溯符号fd(&funptr,1,1);
    返回0;
    }
    
    使用gcc test.c-rdynamic编译

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

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

    另一种方法是使用
    dladdr()
    (另一个扩展),我想
    print\u backtrace()
    使用
    dladdr()
    dladdr()
    返回在
    dli\u sname
    字段中具有函数名的结构。这里我不提供代码示例,但很明显-有关详细信息,请参见
    mandladdr

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


    那么,还有一种方法-使用
    libdwarf
    使用调试信息,但这需要非压缩二进制文件,而且不太容易,所以我不推荐使用。

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

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

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

    以下内容适用于Linux:

    • 使用
      %p
      printf函数的地址
    • 然后执行
      nm | grep
      (不带
      0x
      前缀)
    • 它应该显示函数名
    只有当所讨论的函数在同一个程序中(不是在动态链接库或其他程序中)时,它才起作用


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

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

    void *func = &foo;
    printk("func: %pF at address: %p\n", func, func);
    
  • 使用
    kallsyms\u lookup\u name()
    查找
    kallsyms\u lookup
    的地址

  • 使用指向
    kallsyms\u lookup
    的函数指针调用它


  • 不完全是问题的要求,但是在阅读了这里的答案之后 我想到了我的一个类似问题的解决方案:

    /**
    * 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)#在代码中取消定义X,以从函数名中获取相应的字符串。

    如果您解释为什么
    void *func = &foo;
    printk("func: %pF at address: %p\n", func, func);
    
    /**
    * 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")