简洁版本:
可以在运行时遍历所有ELF“节” header ,并为每个已加载的共享库获取每个“节” header 的重定位地址。

长版:
我正在尝试实现与内核(dyn_debug)中存在的动态调试相同的机制。每个LOG宏实例在程序中特定“节”中创建静态变量的工作方式__attribute__((section("__verbose")))这将强制编译器将变量不放置在“.data”部分中,而应放置在“__verbose”部分中。可以通过变量访问本节的稍后开始和结束地址
__start ___ verbose,__ stop___verbose。这样,某些中央例程可以遍历所有已注册的“日志”条目并按需更改属性。
可以找到静态链接的可执行文件,但是使用共享库时,有几个“__verbose”部分(每个共享库一个),而可执行文件本身就有一个。 (我当然使用-fPIC标志以便包含在库中)
另外,所有与“-export-dynamic”链接的内容都可以确保导出所有符号。

每个共享库和主要可执行文件都有
属性init的(构造函数)方法。

我在2个案例中观察到两种不同的行为。

情况1:对于第一种情况,库不是使用ldopen而是通过“libc loader”加载的

引用__start___verbose的

  • 始终返回相同的地址(主可执行文件的地址),其中仅存在“主”可执行日志记录条目。
  • dlsym(RTLD_NEXT,__start___verbose),返回“下一个”可解析库的符号地址,因此实际上我得到了所有地址。

  • 情况2:使用ldopen加载库

    引用__start___verbose的
  • 始终返回相同的地址(主可执行文件的地址)
  • dlsym(RTLD_NEXT,__start___verbose)返回NULL。
  • dlsym(RTLD_DEFAULT,__start___verbose)返回“主”过程表。
  • dlsym(handle,__start___verbose)-返回正确的段地址

  • 问题:用ldopen打开的库是否有任何方法可以获取该符号,除了4以外,因为4需要从“loader”显式调用

    码:
    /* Main" */
    void func1()
    {
        static int attribute__((section("__verbose"))) var  = 1;
    }
    
    /* Shared library */
    void func2()
    {
        static int attribute__((section("__verbose"))) var  = 2;
    }
    
    /* Both in main and shared library
     * Prints same address !!! BAD !! */
    void __attribute__((constructor)) initializer()
    {
        struct int *iter;
    
        for (iter = __start___verbose; iter != __stop___verbose; ++iter) {
            printf("Value is %d", *iter)
        }
    }
    
    
    
    /* Works for libraries opened by libc runtime.
     * Does not work for libraries opened with LDOPEN*/
     void __attribute__((constructor)) initializer()
     {
        struct int *iter = ;
    
        for (iter = dlsym(RTLD_NEXT, "__start___verbose"); iter != __stop___verbose; ++iter) {
            printf("Value is %d", *iter)
        }
    }
    
    
    
    /* Snippet for main doing dynamic loading */
    
    handle = ldopen('path', RTLD_NOW)
    iter = dlsym(handle, "__start___verbose")
    for (; iter != __stop___verbose; ++iter) {
        printf("Value is %d", *iter)
    }
    

    最佳答案

    您不应在运行时访问节信息。不应在运行时使用节,并且应将其从可执行文件中删除(剥离)。

    我可能将自定义链接描述文件用于:

    .__verbose:
    {
      PROVIDE_HIDDEN (__verbose_start = .);
      *(.__verbose)
      PROVIDE_HIDDEN (__verbose_end = .);
    }
    

    这为该部分定义了HIDDEN符号,因此每个ELF文件将具有自己的那些符号版本。

    然后,ELF文件中的构造函数(或其他一些代码)可以使用它们:
    struct foo*;
    extern struct foo* __verbose_start __attribute__((visibility("hidden")));
    extern struct foo* __verbose_stop __attribute__((visibility("hidden")));
    
    void __attribute__((constructor)) initializer()
    {
       initialize_logging(__verbose_start,__verbose_stop);
    }
    

    关于c - Linux在运行时通过ELF “section” header 进行迭代,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36295540/

    10-12 23:55