Android 13下libart.so的uprobe符号偏移适配问题求助
在Android x86_64模拟器中,使用Rust的aya框架为libart.so库设置uprobe时,该操作在Android 14(内核6.1)中正常,但在Android 13(内核5.15)中无响应。两款系统均通过perf_event_open系统调用挂载uprobe,且libart.so是由zygote进程预加载、所有应用共享的核心库。
环境配置
Android 13
- 符号
art::JNIEnvExt::DeleteLocalRef(_jobject*)在libart.so中的偏移:0x601300 - .text段虚拟地址:
0x35ff00,ELF文件内偏移:0x15ff00 - 运行时maps显示可执行段基址:
7388c1d5f000
Android 14
- 同符号在libart.so中的偏移:
0x684d00 - .text段虚拟地址:
0x200000,ELF文件内偏移:0x200000 - 运行时maps显示可执行段基址:
7720d2000000
运行时行为
- Android 13:
perf_event_open传入的config2参数为0x601300,调用返回ENOTSUPP错误 - Android 14:
perf_event_open传入的config2参数为0x684d00,调用成功且能正常捕获符号调用
bpftrace验证
- Android 14:bpftrace使用
0x684d00作为config2设置uprobe,功能正常 - Android 13:bpftrace传入的
config2为0x401300(比符号偏移少0x200000,该值正好是.text段虚拟地址与ELF内偏移的差值);在Rust程序中手动减去该偏移后,uprobe恢复正常工作
核心疑问
原本认为ELF文件中偏移0x15ff00的.text段会被直接映射到虚拟地址0x35ff00,为什么还需要额外应用这个映射差值?
这本质是不同内核版本对uprobe的config2参数要求存在差异:
内核5.15(Android 13)的规则
该版本内核要求config2传入的是符号在ELF文件中的绝对偏移,而非库加载到内存后,符号相对于库基址的虚拟地址偏移。
你拿到的0x601300是符号加载后的虚拟偏移,要转换成ELF文件内的偏移,必须减去.text段的「虚拟地址与文件内偏移的差值」——也就是0x35ff00 - 0x15ff00 = 0x200000,最终得到0x601300 - 0x200000 = 0x401300,这才是内核5.15能识别的config2值。内核6.1(Android 14)的规则
该版本内核简化了参数逻辑,直接接受加载后的虚拟地址偏移作为config2。刚好Android 14中.text段的虚拟地址与文件内偏移完全一致(均为0x200000),所以符号的虚拟偏移和ELF内偏移完全相同,直接使用0x684d00就能正常工作。
简单来说:内核5.15认的是「符号在ELF文件里的位置」,内核6.1认的是「符号在内存里的位置」,这就是你需要额外调整偏移的核心原因。
内容的提问来源于stack exchange,提问作者Sergej Isbrecht

