dlopen调试笔记
编译glibc
下载glibc源码:http://mirrors.aliyun.com/gnu/libc/
执行编译:
mkdir build
cd build
export CFLAGS="-g -ggdb -O1 -fno-omit-frame-pointer -Wno-maybe-uninitialized -Wno-format-overflow"
../configure --prefix=`pwd`/install
make -j
make install
编译测试程序
注意测试程序代码不能使用c写,会让程序链接libstdc.so,但没有基于自己编译的ld.so重新编译libstdc++.so,导致程序无法启动。
#include <stdio.h>
#include <sched.h>
#include <dlfcn.h>
#include <unistd.h>
void* OpenSo(const char *name, const char* info, int mode) {
void* handle = dlopen(name, mode);
if (handle == NULL) {
printf("dlopen failed: %s", dlerror());
}
return handle;
}
int main(int, char**) {
const char * soName = ("/home/tiny/Source/TestSo/build/libTestSo.so");
OpenSo(soName, "in parent ", RTLD_LAZY | RTLD_LOCAL);
pid_t pid = fork();
if (pid == 0) {
OpenSo(soName, "in child ", RTLD_NOW | RTLD_GLOBAL);
} else {
OpenSo(soName, "in parent ", RTLD_NOW | RTLD_GLOBAL);
}
}
修改程序链接选项,使用自己编译的glibc。注意ld.so需要修改linker,而不是直接链接它。
add_executable(TestDlopen main.c)
target_link_libraries(TestDlopen PUBLIC /home/tiny/Source/glibc-2.35/build/install/lib/libc.so.6 )
target_link_options(TestDlopen PUBLIC -Wl,-dynamic-linker=/home/tiny/Source/glibc-2.35/build/install/lib/ld-linux-x86-64.so.2)
gdb可视化调试glibc
在vscode中打开glibc的代码,调试时启动程序配置为测试程序TestDlopen
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "/home/tiny/Source/TestDlopen/build/TestDlopen",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [{ "name": "LD_PRELOAD", "value": "/home/tiny/Source/glibc-2.35/build/install/lib/libc.so.6" }],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
]
}
dlopen关键步骤
入口:
dlopen
___dlopen
dlopen_implementation
dlopen_doit
_dl_open
dl_open_worker
dl_open_worker中关键步骤
args->worker_continue = false;
__rtld_lock_lock_recursive (GL(dl_load_tls_lock));
dl_open_worker_begin
__rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
if (!args->worker_continue) // 如果so已经加载过,就不会执行后面的流程
return;
call_dl_init
dl_open_worker_begin中的关键步骤
_dl_map_object
/* It was already open. */
if (__glibc_unlikely (new->l_searchlist.r_list != NULL))
{
...
return;
}
_dl_map_object_deps
/* Only do lazy relocation if `LD_BIND_NOW' is not set. */
int reloc_mode = mode & __RTLD_AUDIT;
if (GLRO(dl_lazy))
reloc_mode |= mode & RTLD_LAZY;
_dl_relocate_object
args->worker_continue = true;
_dl_map_object中的关键步骤
{
if (!_dl_name_match_p (name, l))
{
...
}
return l; // 已加载过就不再重复加载
}
_dl_map_object_from_fd
评论区