x86 Linux ELF中指令地址到符号的解析问题求助
我从context->[RIP]拿到指令绝对地址0x563174d510d5,要解析成对应的静态符号。试过dlsym但它对静态符号无效,所以必须手动实现,且不能用CLI工具。
当前操作步骤:
- 从
/proc/[engined的pid]/maps找到进程映射范围0x563174000000-0x5631763e0000 - 计算偏移量:
0x563174d510d5 - 0x563174000000 = 0xD510D5 - 在符号表中查找该偏移所属的最近函数起始地址,但结果和
addr2line的解析有差异,怀疑偏移量计算遗漏了步骤,附上ELF程序头信息,询问是否存在额外计算环节。
ELF程序头信息
ELF文件类型:DYN(共享目标文件/位置无关可执行文件) 入口点:0xd4aa20 共有10个程序头,起始偏移为64 程序头: 类型 文件偏移 虚拟地址 物理地址 文件大小 内存大小 标志 对齐 PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x0000000000000230 0x0000000000000230 R E 8 INTERP 0x0000000000000270 0x0000000000000270 0x0000000000000270 0x000000000000001c 0x000000000000001c R 1 [请求的程序解释器:/lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00000000023df318 0x00000000023df318 R E 200000 LOAD 0x00000000023df400 0x00000000025df400 0x00000000025df400 0x0000000004afd048 0x000000000c6ec200 RW 200000 DYNAMIC 0x0000000002415080 0x0000000002615080 0x0000000002615080 0x0000000000000270 0x0000000000000270 RW 8 NOTE 0x000000000000028c 0x000000000000028c 0x000000000000028c 0x0000000000000044 0x0000000000000044 R 4 TLS 0x00000000023df400 0x00000000025df400 0x00000000025df400 0x0000000000000038 0x000000000006ea20 R 400 GNU_EH_FRAME 0x000000000218e464 0x000000000218e464 0x000000000218e464 0x000000000004959c 0x000000000004959c R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 10 GNU_RELRO 0x00000000023df400 0x00000000025df400 0x00000000025df400 0x0000000000036c00 0x0000000000036c00 R 1
你的偏移量计算本身是正确的:0x563174d510d5 - 0x563174000000 = 0xD510D5,这个值是目标地址相对于进程加载基址的偏移(即RVA,相对虚拟地址),和ELF符号表中st_value字段的定义匹配。结果和addr2line有差异,大概率是以下几个环节出了问题:
符号表选择错误
要解析静态符号,必须读取ELF中的.symtab段(静态符号表),而非.dynsym段(动态符号表)。动态符号表只包含导出到动态链接器的符号,不会包含静态符号,若误读会导致找不到对应符号,结果自然和addr2line(会使用调试信息中的符号)不符。符号匹配逻辑不严谨
不要仅找“最近的函数起始地址”,正确的匹配逻辑应该是筛选出类型为STT_FUNC的符号,然后找到满足st_value ≤ 0xD510D5 < st_value + st_size的符号——这个符号才是目标地址所属的函数。仅找最近起始地址可能会匹配到相邻的其他函数,导致结果偏差。符号过滤规则缺失
解析符号表时要过滤掉非函数符号(比如变量、弱符号、节符号等),只保留类型为STT_FUNC的函数符号,否则会混入无关符号干扰匹配结果。加载基址验证
从maps拿到的0x563174000000确实是正确的加载基址:第一个LOAD段(R E权限)的内存大小是0x23df318,0x563174000000 + 0x23df318 = 0x5631763df318,和maps中的结束地址0x5631763e0000(对齐后的结果)完全吻合,基址计算没有问题。
如果以上几点都排查过仍有差异,建议检查符号表的解析代码是否正确处理了ELF的字节序、符号表项的结构(比如Elf64_Sym的字段偏移是否正确),以及是否正确读取了符号名对应的.strtab段内容。
内容的提问来源于stack exchange,提问作者Pratyush NaRaiN

