Linux内核中连续stmfd sp!, {lr}与ldmfd sp!, {lr}指令的疑问
ARM Linux内核中
stmfd sp!, {lr}+ldmfd sp!, {lr}指令对的作用分析 我在分析Linux内核的ARM汇编代码时,发现多个函数(比如vfs_write)里都有一组重复出现的指令对:
stmfd sp!, {lr} ldmfd sp!, {lr}
以vfs_write的汇编片段为例:
0xc03741d4 <vfs_write>: push {r4, r5, r6, r7, r8, r9, r11, lr} 0xc03741d8 <vfs_write+4>: add r11, sp, #24 0xc03741dc <vfs_write+8>: sub sp, sp, #96 ; 0x60 0xc03741e0 <vfs_write+12>: mov r4, r0 0xc03741e4 <vfs_write+16>: movw r0, #25800 ; 0x64c8 0xc03741e8 <vfs_write+20>: movt r0, #49472 ; 0xc140 0xc03741ec <vfs_write+24>: mov r8, r3 0xc03741f0 <vfs_write+28>: ldr r0, [r0] 0xc03741f4 <vfs_write+32>: mov r5, r2 0xc03741f8 <vfs_write+36>: mov r7, r1 0xc03741fc <vfs_write+40>: str r0, [r11, #-28] ; 0xffffffe4 0xc0374200 <vfs_write+44>: stmfd sp!, {lr} 0xc0374204 <vfs_write+48>: ldmfd sp!, {lr} 0xc0374208 <vfs_write+52>: ldr r0, [r4, #40] ; 0x28 0xc037420c <vfs_write+56>: mvn r6, #8 0xc0374210 <vfs_write+60>: tst r0, #2
这组指令看似把lr压栈后立即弹出,完全没改变寄存器或栈的状态,看起来像冗余代码,但在多个内核函数中重复出现,想知道这是有特定设计原因还是纯粹的冗余?
这组指令不是冗余,是内核调试/追踪机制的产物
这对指令是Linux内核中函数graph tracer(函数调用图追踪器)的占位代码,具体原因如下:
- 函数graph tracer需要在函数入口附近插入追踪代码,记录函数调用关系和执行时间。对于ARM架构,开启该功能时,这组指令会被替换成
bl指令,跳转到追踪处理函数;关闭追踪功能时,内核会保留这对stmfd+ldmfd指令——它们的行为等价于空操作,但长度和bl指令一致(都是4字节),这样能保证内核代码的地址偏移不变,避免关闭追踪后需要重新调整所有函数的地址引用。 - 虽然这组指令会短暂修改栈指针sp,但随即恢复,对程序逻辑没有任何影响。这种设计的核心是为了在开启/关闭追踪功能时,无需重新编译整个内核,仅替换这组占位指令即可实现功能切换,同时保持代码结构的兼容性。
补充验证
你可以检查内核配置选项,查看是否开启过CONFIG_FUNCTION_GRAPH_TRACER相关调试选项。即便当前关闭了该选项,内核构建时仍会保留这组占位指令,后续开启追踪时可直接patch代码,无需重新编译所有模块。
内容的提问来源于stack exchange,提问作者Ted
相关产品推荐
相关产品推荐

