linux 下通过解析 /proc/self/maps 获取已加载 .so 路径,需过滤非空 pathname 且以 .so 结尾或含典型库路径的行,排除伪段并去重;windows 下用 enumprocessmodulesex + getmodulefilenameexa 枚举 dll,注意权限、延迟加载及 uac 限制。

Linux 下用 /proc/self/maps 解析已加载的 .so 路径
Linux 没有标准 C++ API 获取运行时加载的共享库列表,最可靠的方式是读取 /proc/self/maps。该文件按内存映射段列出所有加载区域,其中包含路径的行即为已映射的 .so(或可执行文件本身)。
关键点在于过滤:只取 pathname 字段非空、且以 .so 结尾(或含 /lib、/usr/lib 等典型路径)的行;跳过 [stack]、[heap]、[vdso] 等伪段。
- 需逐行解析,每行格式为:
start-end perm offset dev inode pathname - pathname 位置不固定,可能为空;建议用空格分割后取最后一个非空字段
- 同一 .so 可能因多个 mmap 区域出现多次,建议用
std::set<:string></:string>去重 - 注意权限字段(如
r-xp),通常只关心有执行权限(x)的映射段,排除纯数据段
Windows 下用 EnumProcessModules + GetModuleFileNameExA
Windows 需要调用 PSAPI(psapi.h),先获取模块句柄列表,再逐个查路径。必须链接 psapi.lib,且在 Windows 7+ 上推荐用 EnumProcessModulesEx 配合 LIST_MODULES_ALL 标志,否则可能漏掉某些模块(如延迟加载的 DLL)。
-
EnumProcessModules返回的模块句柄顺序无意义,也不保证包含主模块(exe),需显式调用GetModuleHandle(NULL)补上 -
GetModuleFileNameExA在低完整性进程(如被 UAC 限制)中对系统模块可能失败,返回空字符串或ERROR_ACCESS_DENIED,应跳过并继续 - 路径中常见
\?前缀(长路径格式),可直接保留,也可用PathCchSkipRoot或简单地检测并截掉 - 避免在 DLL 的
DLL_PROCESS_ATTACH期间调用——此时模块列表尚未稳定,可能引发死锁或崩溃
跨平台封装要注意符号可见性与链接时机
如果写成通用函数(比如 std::vector<:string> getLoadedLibraries()</:string>),需注意编译器和链接器行为差异:
- Linux 下若使用
dlopen手动加载的 .so,只要没被dlclose,就一定出现在/proc/self/maps中;但静态链接的.a库不会出现 - Windows 下用
LoadLibrary加载的 DLL 会立即出现在枚举结果里;但通过隐式链接(import lib)引入的 DLL,只有首次调用其导出函数后才真正加载(延迟绑定),此时才可被枚举到 - Clang/GCC 编译时加
-fvisibility=hidden不影响此功能,因为读的是内核暴露的映射信息,不是符号表 - 不要尝试用
dladdr反查函数地址来推断模块——它只能返回“某个符号所属的 DSO”,无法枚举全部,且对 Windows 不可用
常见误判:把 vvar、vDSO、内存映射文件当动态库
解析 /proc/self/maps 时容易把内核提供的虚拟 DSO(如 vvar、vdso、vDSO)或普通 mmap 文件误认为 .so。它们路径字段常为空或为 [vdso],但有些系统会显示类似 /lib64/ld-linux-x86-64.so.2 的真实路径——这时要靠后缀和路径特征双重判断。
- 明确排除所有带方括号的 pathname,如
[heap]、[stack]、[vvar]、[vdso] - 接受以
.so、.so.、.dll、.DLL结尾的路径,也接受不含后缀但路径含/lib、/usr/lib、/System/Library/Frameworks/(macOS)的项 - Windows 上
GetModuleFileNameExA对内核模块(如ntoskrnl.exe)返回空,无需额外过滤;但用户态驱动(如win32k.sys)不会出现在结果中——这本身就是正确行为
实际工程中,最易忽略的是权限和竞态:Linux 下读 /proc/self/maps 是原子快照,没问题;Windows 下 EnumProcessModules 要求 PROCESS_QUERY_INFORMATION 权限,沙盒环境可能拿不到,得提前检查 GetLastError();另外,模块可能在枚举中途被卸载,所以拿到句柄后应尽快调用 GetModuleFileNameExA,别缓存句柄长期使用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











