ARM926EJ-S平台运行时函数.text段内存替换实现方案咨询
ARM9架构下动态函数替换实现方案
前置准备
你需要先在自定义链接脚本中完成两项配置:
- 保证
function_1的.text段长度 ≥function_3的.text段长度,且两段地址都按4字节对齐(ARM指令集)或2字节对齐(Thumb指令集) - 配置函数段所在内存页的属性为可读可写可执行,如果开启了MMU需要修改对应页表项的权限位,关闭MMU可跳过该步骤
直接替换.text段的实现(匹配你原本的方案)
该方案和x86下逻辑基本一致,仅需要额外处理ARM架构专属的缓存、指令对齐问题:
- 关闭全局中断,避免拷贝过程中触发中断调用半修改状态的
function_1导致跑飞 - 执行内存拷贝,把
function_3的.text段内容完整复制到function_1的起始地址,拷贝长度按function_3的段长度取值 - 刷新指令缓存(I-cache),ARM926EJ-S自带指令缓存,修改内存中的指令后缓存中仍留存旧指令,必须手动无效化对应地址的缓存行,参考实现代码:
void flush_icache(uint32_t start_addr, uint32_t end_addr) { uint32_t addr; // ARM926EJ-S默认I-cache行大小为32字节,逐行无效化 for (addr = start_addr; addr < end_addr; addr += 32) { asm volatile ("mcr p15, 0, %0, c7, c5, 1" : : "r" (addr)); } // 全量无效化I-cache也可,适合不想计算段长度的场景 asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); // 加三条空指令做屏障,保证缓存操作完成再执行后续逻辑 asm volatile ("nop");asm volatile ("nop");asm volatile ("nop"); }
- 开中断,后续调用
function_1就会执行function_3的逻辑
注意:如果你的函数内部使用了绝对地址寻址,需要编译时加
-fPIC参数生成位置无关代码,否则拷贝到新地址后寻址会出错,你当前的打印逻辑用相对寻址即可满足,不需要额外处理。
更优的实现路径(不需要拷贝全量函数代码)
直接替换指令的方式受函数长度、位置无关性限制,更稳定的方案是用跳转桩实现:
你可以将function_1的入口修改为仅4字节的跳转指令,初始跳转到原function_1的实现地址,需要替换时只要修改跳转目标地址即可,参考汇编实现:
.global function_1 function_1: ldr pc, [pc, #-4] @ 直接把下方存储的地址加载到PC寄存器实现跳转 .word real_func1 @ 初始指向原function_1的实现地址
替换时只要把function_1入口后4字节的地址值修改为function_3的地址,再刷新一次I-cache即可完成替换,不需要处理函数长度对齐、位置无关等问题,稳定性更高,性能损耗可以忽略。
内容的提问来源于stack exchange,提问作者Amr Mahmoud
相关产品推荐
相关产品推荐

