<p>/proc/self/maps 每行地址范围格式为“起始-结束”(左闭右开),需用 end - start 计算长度;解析时必须检查 sscanf 返回值是否为2,或改用 std::hex + std::istringstream 提高安全性;路径匹配应基于 realpath() 后的完整路径前缀,避免误匹配;变量地址需落入某行区间 [start, end) 才属该映射段。</p>

怎么解析 /proc/self/maps 每一行的地址范围
每一行开头是形如 7f8b2c000000-7f8b2c001000 的十六进制地址对,代表该映射区间的起始和结束(左闭右开)。注意:结束地址不包含在内,计算长度得用 end - start,不是 end - start + 1。
常见错误是直接用 sscanf(line.c_str(), "%lx-%lx", &start, &end) 却没检查返回值——如果某行格式异常(比如 kernel 映射带 [stack:1234] 后缀),sscanf 可能只成功读一个数,end 留垃圾值,后续地址运算就崩了。
- 务必检查
sscanf返回值是否为2 - 用
std::hex配合std::istringstream更安全,能自动跳过空格并拒绝非法字符 - 地址是虚拟内存地址,单位是字节;
/proc/self/maps中所有地址都是当前进程视角的 VA,无需转换
如何识别哪一行对应你的目标文件(比如 libfoo.so)
关键看每行末尾的路径字段。但要注意:它不一定是绝对路径,可能是相对路径、[heap]、[stack]、[vdso],甚至为空(匿名映射)。你得逐行扫描,匹配 std::string_view(line).substr(last_space_pos + 1) 这部分。
容易踩的坑是用 std::string::find("libfoo.so") —— 如果路径是 /usr/lib/x86_64-linux-gnu/libfoo.so.1,而你搜 "libfoo.so" 会命中,但若同时存在 libfoo.so.debug,也可能误匹配。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 建议用完整文件名 + 路径分隔符边界判断,比如搜索
"/libfoo.so"或" /libfoo.so"(前面带空格) - 更稳妥的是先
realpath()你的目标文件,再比对 maps 行末路径是否以该 realpath 开头 - 注意符号链接:maps 显示的是被映射的真实 inode 路径,不是你
dlopen()时传的 symlink 路径
mmap() 映射的文件 vs dlopen() 加载的 so,maps 里表现一样吗
从 /proc/self/maps 角度看,只要底层调用了 mmap()(无论手动还是动态链接器触发),都会有一行记录,权限位(r-xp)、偏移、设备号、inode 都照实反映。所以 dlopen() 加载的 .so 和你自己 mmap(fd, ..., PROT_READ, MAP_PRIVATE, 0, 0) 映射的文件,在 maps 里结构一致。
但区别在于:dlopen 的模块通常有多个映射段(.text 是 r-xp,.data/.bss 是 rw-p),且路径字段显示的是 so 文件路径;而手动 mmap 若映射普通文件,路径字段也是文件路径;若映射 /dev/zero 或匿名,则路径为空。
- 不要依赖“有没有路径字段”来判断是否是 so —— 静态链接的可执行文件也可能有路径(即 exe 自身)
- 想确认是不是动态库,可以结合
readelf -h检查文件类型,或看 maps 中是否出现多个相邻段共享同一路径 - 权限位
p(private)和s(shared)影响写时复制行为,但不影响地址读取逻辑
为什么读到的地址和 &some_global_var 对不上
因为 /proc/self/maps 给的是整个映射区间的基址(比如 .text 段起始),而变量地址是相对于该段内偏移的。例如 maps 显示 7f8b2c000000-7f8b2c005000 r-xp ... libfoo.so,而 &some_func 是 7f8b2c001234,那它在 so 内部的偏移就是 0x1234。
有人试图用 dlsym(RTLD_DEFAULT, "some_func") 得到地址后反推 maps 行,结果失败——因为 RTLD_DEFAULT 查的是全局符号表,可能来自主程序或其他已加载 so,不一定落在你关心的那个映射区间里。
- 要定位变量所属映射段,得遍历 maps 所有行,找到满足
start <= addr && addr < end的那一行 - 注意:PIE 可执行文件的代码段基址每次运行都变,但 maps 总是反映真实运行时布局
- 别忘了栈、堆、vvar/vdso 这些特殊区域也占地址空间,它们没有文件路径,但必须纳入遍历范围才能准确定位
真正麻烦的是多线程下 /proc/self/maps 读取本身是原子的,但如果你边读边触发 mmap/munmap,maps 内容可能中途变化;实际工程中建议一次读完再解析,别边读边处理。
















