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