如何在elf32-i386格式ELF可执行文件中定位指定C函数位置
ELF可执行文件内定位指定函数存储位置的实操指南
核心可行性说明
仅靠你现有的GNU工具链,完全可以在不加载整个ELF文件的前提下,精准定位指定C语言函数的文件存储范围,支持同时获取函数的起始偏移、结束偏移,不需要从零编写ELF解析器。
无需手动解析ELF结构的查询方法
- 通过ELF符号表查询函数的基础参数:执行
readelf -sW 你的目标ELF文件 | grep 目标函数名,输出结果包含两个核心值:Value是函数链接后的虚拟加载地址,Size是函数对应的机器码总长度。由此可直接算出函数的运行时结束地址(起始地址+Size)。注意:如果ELF文件被strip处理移除了符号表,上述查询会失效,需要重新编译ELF时保留符号表(不要加
strip链接参数,或编译时加-g保留调试符号)。 - 将虚拟地址转换为ELF文件内的磁盘偏移:执行
objdump -h 你的目标ELF文件找到代码段(通常为.text段)的参数,记录两个值:段的虚拟起始地址VMA、段在ELF文件中的存储偏移。计算公式为:函数的文件存储偏移 = (函数虚拟地址 - .text段VMA) + .text段文件存储偏移
结合之前拿到的函数长度,就能算出函数在磁盘上的完整存储范围,按磁盘扇区大小对齐后即可直接读取对应扇区加载。 - 更省事的校验方式:执行
objdump -d 你的目标ELF文件 | grep -A 30 "<目标函数名>:",反汇编块的第一行地址就是函数起始虚拟地址,反汇编块的最后一条指令地址+指令长度就是函数结束地址,可直接和前面计算的结果交叉校验。
简化定位流程的编译/链接技巧
- 单独隔离目标函数的存储位置:编译目标函数时加上
__attribute__((section(".isolated_func"))),让编译器把这个函数单独放到自定义的独立段中。链接完成后通过objdump -h查询这个自定义段的文件起始偏移、段大小,就能直接拿到需要加载的连续磁盘范围,不需要做地址转换计算。 - 自定义链接脚本排布函数:编写简单的ld链接脚本,将目标函数强制排布在连续的独立段中,配合
KEEP()标记避免链接器在垃圾回收时丢弃该段内容,从根源上保证函数代码的存储连续,无其他无关代码混杂。
常见踩坑点
- 不要直接把符号表查到的虚拟地址当成文件偏移:ELF的段加载地址和文件存储位置存在固定映射关系,直接用虚拟地址读磁盘一定会定位错误。
- 高等级编译优化可能导致函数丢失符号:开
-O2以上优化时,目标函数可能被内联、重命名或者折叠到其他代码块中,导致符号表找不到对应独立函数。这种情况需要给目标函数加__attribute__((noinline)),或调整编译优化等级重新生成ELF。 - 手动加载函数到内存时,需要将对应内存页的权限设置为可执行,否则跳转执行时会触发内存权限异常。
自学参考方向:可从ELF官方规范的符号表、程序头、节头三个章节入手,结合ld 2.34的链接脚本文档理解段排布逻辑,不需要完整掌握所有ELF结构就能满足定位函数的需求。
内容的提问来源于stack exchange,提问作者zzzhhh
相关产品推荐
相关产品推荐

