可否在运行elf前加载并从/proc/pid/maps获取虚拟地址?perf原理相关问询
问题解答
1. 能否在ELF实际运行前完成加载并读取/proc/pid/maps的虚拟地址?
/proc/[pid]/maps 是内核暴露给用户态的进程虚拟地址空间映射信息,只有当对应进程已经被内核创建、地址空间初始化完成后才可以访问。
如果是常规的ELF执行流程,ELF的加载由execve系统调用完成:内核会解析ELF结构,将代码段、数据段等按照程序头表的配置映射到进程虚拟地址空间,完成动态链接器加载等操作后,才会开始执行ELF的入口代码。你需要等execve执行完成、映射建立之后,才可以从maps中读到对应段的虚拟地址。
如果是用户态手动加载ELF(比如自己实现ELF加载逻辑、用dlopen加载动态库),则在你完成手动映射之后,就可以从当前进程的maps中读到对应的地址。
2. perf 是否为先加载二进制、完成配置再运行程序?
是的,perf record ./binary 类命令的核心执行流程如下:
- perf主进程首先
fork出一个子进程,子进程不会立刻执行目标二进制 - 主进程完成性能事件注册、缓冲区初始化、事件绑定到子进程等所有前置配置
- 配置完成后,子进程才会执行
execve系统调用加载目标ELF二进制,完成内核态的段映射、动态链接等操作,之后才会运行目标程序的业务代码
需要注意的是:perf完成自身配置的时候,目标二进制还没有被加载到子进程的地址空间,此时读子进程的maps是拿不到目标ELF的段地址的,必须等execve执行完成之后才可以。
3. 能否通过读取/proc/pid/maps获取指定代码段的虚拟地址?
完全可以,只要控制好读取时机即可,实操注意事项如下:
- 读取时机必须在子进程完成
execve调用、ELF加载完成之后,此时maps中才会出现目标二进制的映射条目 - 位置无关可执行文件(PIE,当前主流发行版默认编译选项)每次加载的代码段地址是随机的,每次运行读取到的地址都会不同,属于正常现象
- 如果需要固定代码段地址,可以在编译二进制时添加
-no-pie编译参数关闭PIE,此时代码段虚拟地址固定,你甚至可以直接读取ELF文件的程序头表中的p_vaddr字段直接拿到代码段虚拟地址,不需要读取/proc/pid/maps - 读取maps时,权限标记为
r-xp、路径匹配目标二进制的条目对应的起始地址,就是该ELF代码段的虚拟基地址
如果你要自己实现该逻辑,不需要依赖perf的流程,可以自行fork子进程后用ptrace跟踪子进程,捕获execve系统调用完成的事件后再读取maps即可。
内容的提问来源于stack exchange,提问作者qunqunqun
相关产品推荐
相关产品推荐

