2016-03-29 21 views
2

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

长版本: 我试图在内核(dyn_debug)中实现用于用户空间中的动态调试的相同机制。它的工作方式是每个LOG宏实例在程序中的特定“部分”中创建静态变量 __attribute__((section("__verbose"))) 这会强制编译器将该变量放在“.data”部分而不是“__verbose”部分。本节后面的开始和结束地址可以通过变量 __start___verbose,__stop___verbose来访问。通过这种方式,一些中央例程可以遍历所有已注册的“日志”条目并根据需要更改属性。 这适用于静态链接的可执行文件,但是在使用共享库时,有几个“__verbose”节(每个共享库一个节)和一个可执行文件本身。 (我使用-fPIC标志当然是为了包含在库中) 为了确保所有的符号都被导出,所有与“-export-dynamic”链接的所有内容。

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

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

情形1:对于第一壳体库不装载ldopen而是由“libc的加载器”

  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)返回“主”进程表。
  4. 的dlsym(手柄,__start___verbose) - 返回正确的区段地址

问:有没有办法为库ldopen打开,以获得该符号,除了4,如4需要显式调用从“装载机”

代码:

/* 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) 
} 
+0

你做什么特别的有'__start ___ verbose'和'__stop ___ verbose'变量,因为我没有他们。 – ysdx

+0

难道你不想在ELF文件的(高优先级)构造函数中初始化你的变量吗?否则,在执行构造函数的代码时,它们不会被初始化。 – ysdx

回答

1

你不应该在运行时访问部分信息。部分不支持在运行时使用,并且可以从可执行文件中移除(剥离)。

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

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