在运行时遍历Linux ELF“section”头部

3
简短版: 可以在运行时遍历所有ELF“section”头,并获取每个已加载共享库的每个“section”头的重定位地址。
详细版: 我正在尝试在用户空间实现与内核中存在的动态调试相同的机制(dyn_debug)。它的工作方式是,每个LOG宏实例都会在程序的特定“section”中创建静态变量 __attribute__((section("__verbose"))) 这会强制编译器将变量放在“。data”部分而不是“__verbose”部分。稍后,可以通过变量__start___verbose、__stop___verbose访问此部分的起始和停止地址。这样,某些中央例程就可以遍历所有注册的“日志”条目并根据需要更改属性。 这对于静态链接的可执行文件很好用,但是当使用共享库时,有几个“__verbose”部分(每个共享库一个),以及一个在可执行文件本身中(当然,我使用了-fPIC标志,以便包含在库中)。 还使用“-export-dynamic”链接了所有符号以确保导出所有符号。
每个共享库和主可执行文件都有 attribute(constructor)方法进行初始化。
我在两种情况下观察到了两种不同的行为。
情况1:对于第一种情况,库不是通过ldopen加载的,而是通过“libc loader”加载的
  1. 引用__start___verbose始终返回相同的地址(即主可执行文件的地址),其中仅存在“main”可执行文件的日志条目。
  2. dlsym(RTLD_NEXT, __start___verbose)返回可解析库的“下一个”符号的地址,因此实际上我获得了所有地址。
情况2:使用ldopen加载库
  1. 引用__start___verbose始终返回相同的地址(即主可执行文件的地址)
  2. dlsym(RTLD_NEXT, __start___verbose)返回NULL。
  3. dlsym(RTLD_DEFAULT, __start___verbose)返回“main”进程表。
  4. dlsym(handle, __start___verbose) - 返回正确的部分地址
问题:除了4需要从“loader”显式调用外,是否有任何方法可以获取使用ldopen打开的库的该符号?
代码:
/* 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)
}

你是否有特殊处理__start___verbose__stop___verbose变量,因为我没有它们。 - ysdx
你不想在ELF文件的(高优先级)构造函数中初始化变量吗?否则,在执行构造函数代码时,它们将不会被初始化。 - ysdx
1个回答

2

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

我可能会使用自定义链接脚本:

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

这定义了部分隐藏符号,以便每个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);  
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接