linux下获取动态库加载基址最可靠方式是解析/proc/self/maps:逐行匹配含.so路径且权限为r-xp的行,提取首字段十六进制起始地址即为运行时基址,需注意aslr导致每次不同,且dladdr仅适用于已dlopen加载或符号导出的模块。

Linux下用/proc/self/maps定位动态库的加载基址
动态库(如libfoo.so)在运行时被映射到进程地址空间,其实际加载地址不固定(ASLR启用时每次不同),但可通过/proc/self/maps实时读取。关键不是“符号表基址”,而是“模块加载起始地址”——符号表(.dynsym等节)位于该基址偏移处,需后续解析ELF结构才能定位。
实操建议:
- 打开
/proc/self/maps,逐行扫描含.so路径的行(如7f8a2b3c0000-7f8a2b3e1000 r-xp 00000000 08:01 123456 /usr/lib/x86_64-linux-gnu/libc.so.6),首字段即为加载基址(十六进制) - 注意区分
r-xp(代码段)和r--p(只读数据段):通常以r-xp行的起始地址为准,因函数符号多在此段 - 若库被多次映射(如
.so+.so.debug),优先选无debug后缀且权限含x的行 - 用
std::ifstream按行读取,配合std::string::find匹配".so"和" r-xp ",避免正则开销
用dlopen(nullptr, RTLD_NOW)获取主程序或已加载库句柄
dlopen(nullptr, ...)返回的是主可执行文件(或当前共享对象)的句柄,不是动态库的基址。但它能配合dladdr反查符号地址,间接验证基址是否正确。
实操建议:
- 调用
void* handle = dlopen(nullptr, RTLD_NOW);后,立即用dlerror()检查是否失败(仅当未链接-ldl时会失败) - 对任意已知符号(如
main或自定义全局函数)调用dladdr(&symbol, &info),info.dli_fbase即该符号所在模块的加载基址 - 注意:
dladdr对静态编译的符号(如内联函数、未导出的static函数)可能返回空,应选extern "C"导出或__attribute__((visibility("default")))标记的符号 - 此法无法直接获取未通过
dlopen显式加载的依赖库(如libc)基址,仍需回退到/proc/self/maps
解析ELF头定位.dynsym节真实地址需两步转换
即使拿到动态库加载基址,.dynsym节在内存中的地址 ≠ 基址 + ELF文件内该节的sh_addr。因为ELF中sh_addr是链接视图地址(link-time VMA),而运行时实际地址需结合程序头(PT_LOAD段)偏移修正。
实操建议:
- 先用
mmap将.so文件只读映射(非执行),读取其ELF头和程序头,找到类型为PT_LOAD且含PF_R|PF_X权限的段,记录其p_vaddr(虚拟地址)和p_offset(文件偏移) - 计算运行时基址与链接视图基址的差值:
runtime_base - link_vaddr,其中link_vaddr即该PT_LOAD段的p_vaddr - 再读节头表,找到
.dynsym节的sh_addr,加上上述差值得到运行时真实地址 - 实践中极少需要手动算这个——调试器和
readelf -S --wide /path/to/lib.so输出的Address列已是链接视图地址,直接加运行基址会错,必须走PT_LOAD校准
Windows下用GetModuleHandle替代方案
Windows无/proc机制,但可用GetModuleHandle获取已加载模块句柄,再用GetModuleInformation读取基址。注意它返回的是模块加载起始地址,不是符号表位置。
实操建议:
- 对DLL名调用
HMODULE h = GetModuleHandleA("kernel32.dll");,若返回NULL说明未显式加载,此时需先LoadLibrary(但会改变引用计数) - 调用
GetModuleInformation(GetCurrentProcess(), h, &modinfo, sizeof(modinfo)),modinfo.lpBaseOfDll即基址 - 符号表(
.edata导出表)地址需解析PE头:IMAGE_NT_HEADERS→OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]→VirtualAddress,再加基址 - 跨平台代码中,务必用
#ifdef _WIN32隔离,Linux侧不可调用GetModuleHandle
真正难的不是读出一个数字,而是确认这个地址对应哪个内存段、是否包含你关心的符号、以及在ASLR或prelink环境下是否稳定可复现。多数场景下,用/proc/self/maps扫出基址 + objdump -T查符号偏移,比硬解析ELF更可靠。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











