- 简述
- 编译时处理
- 链接时处理
- 系统初始化
默认本篇文章的读者已经了解arm、编译和简单汇编。
arm64采用fpatchable-function-entry而不是pg,这里暂不讨论,扩展了解https://zhuanlan.zhihu.com/p/104683907。
简单叙述ftrace的原理和流程,ftrace的实现依赖3个过程,分别为编译、链接重定位、系统初始化、开启追踪。
- 编译时:内核开启CONFIG_FUNCTION_TRACER后,编译选项会增加-pg,在每个函数中打上标记;
- 链接重定位:将编译时标记链接到处理函数;
- 系统初始化:将函数标记替换为nop指令;
- 开启追踪:将函数标记替换为ftrace_caller,记录信息。
- 当内核开启CONFIG_FUNCTION_TRACER时,kernel Makefile会在编译参数增加-pg。
# The arch Makefiles can override CC_FLAGS_FTRACE. We may also append it later. ifdef CONFIG_FUNCTION_TRACER CC_FLAGS_FTRACE := -pg endif
- 示例驱动代码
int func2(int p1, int p2)
{
return p1+p2;
}
int func1(int p1, int p2)
{
return func2(p1, p2);
}
static int __init hello_init(void)
{
pr_info("hello driver init!n");
func1(1, 2);
return 0;
}
static void __exit hello_exit(void)
{
pr_info("hello driver exitn");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
- 未开启CONFIG_FUNCTION_TRACER 时,objdump -d如下:
00000000: 0: e1a0c00d mov ip, sp 4: e92dd800 push {fp, ip, lr, pc} 8: e24cb004 sub fp, ip, #4 c: e0800001 add r0, r0, r1 10: e89da800 ldm sp, {fp, sp, pc} 00000014 : 14: e1a0c00d mov ip, sp 18: e92dd800 push {fp, ip, lr, pc} 1c: e24cb004 sub fp, ip, #4 20: e0800001 add r0, r0, r1 24: e89da800 ldm sp, {fp, sp, pc} Disassembly of section .init.text: 00000000 : 0: e1a0c00d mov ip, sp 4: e92dd800 push {fp, ip, lr, pc} 8: e24cb004 sub fp, ip, #4 c: e59f0008 ldr r0, [pc, #8] ; 1c 10: ebfffffe bl 0 <_printk> 14: e3a00000 mov r0, #0 18: e89da800 ldm sp, {fp, sp, pc} 1c: 00000000 .word 0x00000000
- 开启CONFIG_FUNCTION_TRACER 时,objdump -d如下:
00000000: 0: e1a0c00d mov ip, sp 4: e92dd800 push {fp, ip, lr, pc} 8: e24cb004 sub fp, ip, #4 c: e52de004 push {lr} ; (str lr, [sp, #-4]!) 10: ebfffffe bl 0 <__gnu_mcount_nc> 14: e0800001 add r0, r0, r1 18: e89da800 ldm sp, {fp, sp, pc} 0000001c : 1c: e1a0c00d mov ip, sp 20: e92dd800 push {fp, ip, lr, pc} 24: e24cb004 sub fp, ip, #4 28: e52de004 push {lr} ; (str lr, [sp, #-4]!) 2c: ebfffffe bl 0 <__gnu_mcount_nc> 30: e0800001 add r0, r0, r1 34: e89da800 ldm sp, {fp, sp, pc} Disassembly of section .init.text: 00000000 : 0: e1a0c00d mov ip, sp 4: e92dd800 push {fp, ip, lr, pc} 8: e24cb004 sub fp, ip, #4 c: e52de004 push {lr} ; (str lr, [sp, #-4]!) 10: ebfffffe bl 0 <__gnu_mcount_nc> 14: e59f0008 ldr r0, [pc, #8] ; 24 18: ebfffffe bl 0 <_printk> 1c: e3a00000 mov r0, #0 20: e89da800 ldm sp, {fp, sp, pc} 24: 00000000 .word 0x00000000
- 对比可以发现,开启CONFIG_FUNCTION_TRACER 后,编译代码会增加bl 0 <__gnu_mcount_nc>。
如果编译为ko,则链接时是在insmod时进行;如果是编译进内核,vmlinux就是链接后的文件,可以通过反汇编查看。
为方便展示,将上述驱动编译进内核,objdum -d vmlinux如下。
8069a694: 8069a694: e1a0c00d mov ip, sp 8069a698: e92dd830 push {r4, r5, fp, ip, lr, pc} 8069a69c: e24cb004 sub fp, ip, #4 8069a6a0: e52de004 push {lr} ; (str lr, [sp, #-4]!) 8069a6a4: ebe9d726 bl 80110344 <__gnu_mcount_nc> 8069a6a8: e0050091 mul r5, r1, r0 8069a6ac: e1a00005 mov r0, r5 8069a6b0: ebffffe8 bl 8069a658 8069a6b4: e59f1014 ldr r1, [pc, #20] ; 8069a6d0 8069a6b8: e1a04000 mov r4, r0 8069a6bc: e1a02000 mov r2, r0 8069a6c0: e59f000c ldr r0, [pc, #12] ; 8069a6d4 8069a6c4: ebebe82d bl 80194780 8069a6c8: e0850004 add r0, r5, r4 8069a6cc: e89da830 ldm sp, {r4, r5, fp, sp, pc}
可以看到bl 0 <__gnu_mcount_nc>已经被替换为bl 80110344 <__gnu_mcount_nc>。查看__gnu_mcount_nc实现如下。
80110344 <__gnu_mcount_nc>:
80110344: e1a0c00e mov ip, lr
80110348: e8bd4000 ldmfd sp!, {lr}
8011034c: e1a0f00c mov pc, ip
相当于执行了一个return;。
系统初始化todo



