当前位置:首页 > 公众号精选 > Linux阅码场
[导读]目录1.前言2.ARM64栈帧结构3.编译阶段3.1未开启ftrace时的blk_update_request3.2开启ftrace时的blk_update_request4.链接阶段4.1未开启ftrace时的blk_update_request4.2开启ftrace时的blk...

目录

  • 1. 前言

  • 2. ARM64栈帧结构

  • 3. 编译阶段

  • 3.1 未开启ftrace时的blk_update_request

  • 3.2 开启ftrace时的blk_update_request

  • 4. 链接阶段

  • 4.1 未开启ftrace时的blk_update_request

  • 4.2 开启ftrace时的blk_update_request

  • 5. 运行阶段

  • 5.1 ftrace_init执行后的blk_update_request

  • 5.2 设定trace函数blk_update_request

  • 6. 钩子函数的替换过程

  • 7.总结

  • 参考文档

1. 前言

本文主要是根据阅码场 《Linux内核tracers的实现原理与应用》视频课程,我自己在aarch64上的实践。通过观察钩子函数的创建过程以及替换过程,理解trace的原理。本文同样以blk_update_request函数为例进行说明。

kernel版本:5.10平台:arm64

2.ARM64栈帧结构

在开始介绍arm64架构下的ftrace之前,先来简要说明一下arm64栈帧的相关知识。arm64有31个通用寄存器r0-r30,其中r0-r7用于Parameter/result 寄存器; r29为Frame Pointer寄存器,r30为Link寄存器,指向上级函数的返回地址;SP为栈指针。将以如下代码为例,说明它的栈帧结构:

/* * ARCH: armv8 * GCC版本:aarch64-linux-gnu-gcc (Linaro GCC 5.4-2017.01) 5.4.1 20161213 */intfun2(int c,int d){   return0;} intfun1(int a,int b){   int c = 1;   int d = 2;     fun2(c, d);   return0;} intmain(int argc,char **argv){   int a = 0;   int b = 1;   fun1(a,b);}aarch64-linux-gnu-objdump -d a.out 反汇编后的结果为:

0000000000400530 :  /* 更新spfun2的栈底 */  400530:       d10043ff        sub     sp, sp, #0x10  400534:       b9000fe0        str     w0, [sp,#12]  400538:       b9000be1        str     w1, [sp,#8]  40053c:       52800000        mov     w0, #0x0                        // #0  400540:       910043ff        add     sp, sp, #0x10  400544:       d65f03c0        ret 0000000000400548 :  /* 分配48字节栈空间,先更新sp=sp-48, 再入栈x29, x30, 此时sp指向栈顶 */  400548:       a9bd7bfd        stp     x29, x30, [sp,#-48]!  /* x29sp指向栈顶*/  40054c:       910003fd        mov     x29, sp  /* 入栈fun1参数0 */  400550:       b9001fa0        str     w0, [x29,#28]  /* 入栈fun1参数1 */  400554:       b9001ba1        str     w1, [x29,#24]  /* 入栈fun1局部变量c */  400558:       52800020        mov     w0, #0x1                        // #1  40055c:       b9002fa0        str     w0, [x29,#44]  /* 入栈fun1局部变量d */  400560:       52800040        mov     w0, #0x2                        // #2  400564:       b9002ba0        str     w0, [x29,#40]  400568:       b9402ba1        ldr     w1, [x29,#40]  40056c:       b9402fa0        ldr     w0, [x29,#44]  /* 跳转到fun2 */  400570:       97fffff0        bl      400530   400574:       52800000        mov     w0, #0x0                        // #0  400578:       a8c37bfd        ldp     x29, x30, [sp],#48  40057c:       d65f03c0        ret 0000000000400580
:  /* 分配48字节栈空间,先更新sp=sp-48, 再入栈x29, x30, 此时sp指向栈顶*/  400580:       a9bd7bfd        stp     x29, x30, [sp,#-48]!  /* x29sp指向栈顶*/  400584:       910003fd        mov     x29, sp  /* 入栈main参数0 */  400588:       b9001fa0        str     w0, [x29,#28]  /* 入栈main参数1 */  40058c:       f9000ba1        str     x1, [x29,#16]  /* 入栈变量a */  400590:       b9002fbf        str     wzr, [x29,#44]  400594:       52800020        mov     w0, #0x1                        // #1  /* 入栈变量b */  400598:       b9002ba0        str     w0, [x29,#40]  40059c:       b9402ba1        ldr     w1, [x29,#40]  4005a0:       b9402fa0        ldr     w0, [x29,#44]  /* 跳转到fun1 */  4005a4:       97ffffe9        bl      400548   4005a8:       52800000        mov     w0, #0x0                        // #0  4005ac:       a8c37bfd        ldp     x29, x30, [sp],#48  4005b0:       d65f03c0        ret  4005b4:       00000000        .inst   0x00000000 ; undefined对应栈帧结构为:



总结一下:通过对aarch64代码反汇编的分析,可以得出:

1.     每个函数在入口处首先会分配栈空间,且一次分配,确定栈顶,之后sp将不再变化;

2.     每个函数的栈顶部存放的是caller的栈顶指针,即fun1的栈顶存放的是main栈顶指针;

3.     对于最后一级callee函数,由于x29保存了上一级caller的栈顶sp指针,因此不在需要入栈保存,如示例中fun2执行时,此时x29指向fun1的栈顶sp

下面我们将根据是否开启ftrace配置,并区分编译阶段、链接阶段和运行阶段,分别查看钩子函数的替换及构建情况。

3. 编译阶段

3.1 未开启ftrace时的blk_update_request

00000000000012ac :    12ac:       d10183ff        sub     sp, sp, #0x60    12b0:       a9017bfd        stp     x29, x30, [sp,#16]    12b4:       910043fd        add     x29, sp, #0x10    12b8:       a90253f3        stp     x19, x20, [sp,#32]    12bc:       a9035bf5        stp     x21, x22, [sp,#48]    12c0:       a90463f7        stp     x23, x24, [sp,#64]    12c4:       f9002bf9        str     x25, [sp,#80]    12c8:       aa0003f6        mov     x22, x0    12cc:       53001c38        uxtb    w24, w1    12d0:       2a0203f5        mov     w21, w2    12d4:       2a1803e0        mov     w0, w24    12d8:       94000000        bl      12c     ...在未使能内核配置项CONFIG_FTRACE时,反汇编blk_update_request函数可以看出,不包含钩子函数。

3.2 开启ftrace时的blk_update_request

0000000000003f10 :    3f10:       d10183ff        sub     sp, sp, #0x60    3f14:       a9017bfd        stp     x29, x30, [sp,#16]    3f18:       910043fd        add     x29, sp, #0x10    3f1c:       a90253f3        stp     x19, x20, [sp,#32]    3f20:       a9035bf5        stp     x21, x22, [sp,#48]    3f24:       a90463f7        stp     x23, x24, [sp,#64]    3f28:       f9002bf9        str     x25, [sp,#80]    3f2c:       aa0003f6        mov     x22, x0    3f30:       53001c38        uxtb    w24, w1    3f34:       2a0203f5        mov     w21, w2    3f38:       aa1e03e0        mov     x0, x30    3f3c:       94000000        bl      0 <_mcount>    ...在使能内核配置项CONFIG_FTRACE时,可以看到blk_update_request函数增加了如下部分:

    3f3c:       94000000        bl      0 <_mcount>那么 bl 0 <_mcount> 是由谁在何时插入的呢? 答案是编译器在编译时插入,编译选项-pg -mrecord-mcoun会在编译时在每个可trace函数插入bl 0 <_mcount>,并将所有可trace的函数放到一个__mcount_loc的section中。

通过查看blk-core.o的可重定位段,可以看到有大量的地址需要定位到_mcount函数,其中3f3c地址正是位于blk_update_request,它会在链接阶段被重定位到_mcount函数的地址。

ubuntu@VM-0-9-ubuntu:~/qemu/kernel/linux/block$ aarch64-linux-gnu-objdump -r blk-core.o | grep _mcount0000000000000014 R_AARCH64_CALL26  _mcount000000000000005c R_AARCH64_CALL26  _mcount00000000000000ac R_AARCH64_CALL26  _mcount0000000000000108 R_AARCH64_CALL26  _mcount0000000000000164 R_AARCH64_CALL26  _mcount00000000000001bc R_AARCH64_CALL26  _mcount0000000000000214 R_AARCH64_CALL26  _mcount...0000000000003f3c R_AARCH64_CALL26  _mcount...我们还可以看到,blk-core.o有一个.rela__mcount_loc的可重定位段,里面存放了所有需要可trace函数中需要重定位到函数_mcount的地址。

ubuntu@VM-0-9-ubuntu:~/qemu/kernel/linux/block$ aarch64-linux-gnu-objdump -r blk-core.o...RELOCATION RECORDS FOR [__mcount_loc]:OFFSET           TYPE              VALUE0000000000000000 R_AARCH64_ABS64   .text 0x00000000000000140000000000000008 R_AARCH64_ABS64   .text 0x000000000000005c0000000000000010 R_AARCH64_ABS64   .text 0x00000000000000ac0000000000000018 R_AARCH64_ABS64   .text 0x0000000000000108...00000000000001b8 R_AARCH64_ABS64   .text 0x0000000000003f3c...

4. 链接阶段

4.1 未开启ftrace时的blk_update_request

未使能内核配置项CONFIG_FTRACE时,链接阶段与编译阶段一样,反汇编blk_update_request函数可以看出,不包含钩子函数

4.2 开启ftrace时的blk_update_request

ffff8000104e43c8 :ffff8000104e43c8:       d10183ff        sub     sp, sp, #0x60ffff8000104e43cc:       a9017bfd        stp     x29, x30, [sp,#16]ffff8000104e43d0:       910043fd        add     x29, sp, #0x10ffff8000104e43d4:       a90253f3        stp     x19, x20, [sp,#32]ffff8000104e43d8:       a9035bf5        stp     x21, x22, [sp,#48]ffff8000104e43dc:       a90463f7        stp     x23, x24, [sp,#64]ffff8000104e43e0:       f9002bf9        str     x25, [sp,#80]ffff8000104e43e4:       aa0003f6        mov     x22, x0ffff8000104e43e8:       53001c38        uxtb    w24, w1ffff8000104e43ec:       2a0203f5        mov     w21, w2ffff8000104e43f0:       aa1e03e0        mov     x0, x30ffff8000104e43f4:       97ed1fde        bl      ffff80001002c36c <_mcount>ffff8000104e43f8:       2a1803e0        mov     w0, w24ffff8000104e43fc:       97fff432        bl      ffff8000104e14c4 ...在链接阶段,使能内核配置项CONFIG_FTRACE时,可以看到编译阶段的如下代码

    3f3c:       94000000        bl      0 <_mcount>在链接阶段已经被替换为:

ffff8000104e43f4:       97ed1fde        bl      ffff80001002c36c <_mcount>其中_mcount函数反汇编为:

ffff80001002c36c <_mcount>:ffff80001002c36c:       d65f03c0        ret

5. 运行阶段

5.1ftrace_init执行后的blk_update_request

(gdb) x/20i blk_update_request   0xffff8000104e43c8 :     sub     sp, sp, #0x60   0xffff8000104e43cc :   stp     x29, x30, [sp,#16]   0xffff8000104e43d0 :   add     x29, sp, #0x10   0xffff8000104e43d4 :  stp     x19, x20, [sp,#32]   0xffff8000104e43d8 :  stp     x21, x22, [sp,#48]   0xffff8000104e43dc :  stp     x23, x24, [sp,#64]   0xffff8000104e43e0 :  str     x25, [sp,#80]   0xffff8000104e43e4 :  mov     x22, x0   0xffff8000104e43e8 :  uxtb    w24, w1   0xffff8000104e43ec :  mov     w21, w2   0xffff8000104e43f0 :  mov     x0, x30   0xffff8000104e43f4 :  nop   0xffff8000104e43f8 :  mov     w0, w24   0xffff8000104e43fc :  bl      0xffff8000104e14c4 内核在start_kernel执行时,会调用ftrace_init,它会将所有可trace函数中的_mcount进行替换,如上可以看出链接阶段的 bl ffff80001002c36c <_mcount> 已经被替换为nop指令

5.2 设定trace函数blk_update_request

执行如下命令来trace函数blk_update_request

ubuntu@VM-0-9-ubuntu:~$echo blk_update_request > /sys/kernel/debug/tracing/set_ftrace_filterubuntu@VM-0-9-ubuntu:~$echo function > /sys/kernel/debug/tracing/current_tracer我们再来查看blk_update_request反汇编代码

(gdb) x/20i blk_update_request   0xffff8000104e43c8 :     sub     sp, sp, #0x60   0xffff8000104e43cc :   stp     x29, x30, [sp,#16]   0xffff8000104e43d0 :   add     x29, sp, #0x10   0xffff8000104e43d4 :  stp     x19, x20, [sp,#32]   0xffff8000104e43d8 :  stp     x21, x22, [sp,#48]   0xffff8000104e43dc :  stp     x23, x24, [sp,#64]   0xffff8000104e43e0 :  str     x25, [sp,#80]   0xffff8000104e43e4 :  mov     x22, x0   0xffff8000104e43e8 :  uxtb    w24, w1   0xffff8000104e43ec :  mov     w21, w2   0xffff8000104e43f0 :  mov     x0, x30   0xffff8000104e43f4 :  bl      0xffff80001002c370    0xffff8000104e43f8 :  mov     w0, w24   0xffff8000104e43fc :  bl      0xffff8000104e14c4 可以看到之前在blk_update_request的nop指令被替换成
bl 0xffff80001002c370

继续反汇编ftrace_caller得到如下的汇编代码:

(gdb) disassemble ftrace_callerDump of assembler code for function ftrace_caller:   0xffff80001002c374 < 0>:     stp     x29, x30, [sp,#-16]!   0xffff80001002c378 < 4>:     mov     x29, sp   // x30blk_update_requestlr-4是当前执行函数的入口地址,也就是ftrace_callerip   // 它将作为参数0传递给ftrace_ops_no_ops   0xffff80001002c37c < 8>:     sub     x0, x30, #0x4   // 参考前面arm64栈帧结构,x29指向上一级函数blk_update_request栈顶   //[x29]指向blk_mq_end_request函数的栈顶   //[[x29] 8]blk_mq_end_requestip(实际是ip的下条指令)   0xffff80001002c380 < 12>:    ldr     x1, [x29]   0xffff80001002c384 < 16>:    ldr     x1, [x1,#8]   0xffff80001002c388 < 20>:    bl      0xffff800010188ffc    0xffff80001002c38c < 24>:    nop   0xffff80001002c390 < 28>:    ldp     x29, x30, [sp],#16   0xffff80001002c394 < 32>:    retEnd of assembler dump.可以看到ftrace_caller会调用ftrace_ops_no_ops,我们在ftrace_ops_no_ops源码中看到它会遍历ftrace_ops_list链表,并执行这个链表上的回调函数,这里看下ftrace_ops_list上都链接了哪些func

(gdb) p *ftrace_ops_list$4 = {  func = 0xffff8000101a0b1c , //ftrace_ops_list链表唯一func  next = 0xffff800011c5a438 , //说明ftrace_ops_list链表只有一个func  flags = 8273,  private = 0xffff800011cf94e8 ,  saved_func = 0xffff8000101a0b1c ,  local_hash = {    notrace_hash = 0xffff800010cf7118 ,    filter_hash = 0xffff00000720af80,    regex_lock = {      owner = {        counter = 0      },......从ftrace_ops_list链表中可以看到只有一个function_trace_call函数组成,因此可以说ftrace_caller最终会调用到function_trace_call。

通过前面的分析,我们一步步找到了blk_update_request的钩子函数function_trace_call,其函数原型如下,其中参数ip指向ftrace_caller,参数parent_ip指向blk_mq_end_request:

staticvoidfunction_trace_call(unsignedlong ip, unsignedlong parent_ip,                                                             struct ftrace_ops *op, struct pt_regs *pt_regs)下一节我们将追踪钩子函数的构造以及替换过程。

6. 钩子函数的替换过程

前面我们看到blk_update_request的nop指令被替换成bl ftrace_caller,那么此处的ftrace_caller是在哪里定义的呢?我们可以看到arch/arm64/kernel/entry-ftrace.S有如下的定义:

/* * void ftrace_caller(unsigned long return_address) * @return_address: return address to instrumented function * * This function is a counterpart of _mcount() in 'static' ftrace, and * makes calls to: *     - tracer function to probe instrumented function's entry, *     - ftrace_graph_caller to set up an exit hook */SYM_FUNC_START(ftrace_caller)        mcount_enter         mcount_get_pc0  x0              //     function's pc        mcount_get_lr   x1              //     function's lr SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)      // tracer(pc, lr);        nop                             // This will be replaced with "bl xxx"                                        // where xxx can be any kind of tracer. #ifdef CONFIG_FUNCTION_GRAPH_TRACERSYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL) // ftrace_graph_caller();        nop                             // If enabled, this will be replaced                                        // "b ftrace_graph_caller"#endif         mcount_exitSYM_FUNC_END(ftrace_caller)通过 gdb可以看到ftrace_caller的反汇编代码如下:

(gdb) disassemble ftrace_callerDump of assembler code for function ftrace_caller:   0xffff80001002c370 < 0>:     stp     x29, x30, [sp,#-16]!   0xffff80001002c374 < 4>:     mov     x29, sp   0xffff80001002c378 < 8>:     sub     x0, x30, #0x4   0xffff80001002c37c < 12>:    ldr     x1, [x29]   0xffff80001002c380 < 16>:    ldr     x1, [x1,#8]   0xffff80001002c384 < 20>:    nop                  /*ftrace_call*/   0xffff80001002c388 < 24>:    nop                  /*ftrace_graph_call,暂不讨论*/   0xffff80001002c38c < 28>:    ldp     x29, x30, [sp],#16   0xffff80001002c390 < 32>:    retEnd of assembler dump.当执行echo blk_update_request >set_ftrace_filter时相当于使能了blk_update_request的钩子替换标志,当执行echo function >current_tracer时会检查这个标志,并执行替换,它会产生如下的调用链:

/sys/kernel/debug/tracing # echo function > current_tracer[   45.632002] CPU: 0 PID: 111 Comm: sh Not tainted 5.10.0-dirty #35[   45.632457] Hardware name: linux,dummy-virt (DT)[   45.632697] Call trace:[   45.632981]  dump_backtrace 0x0/0x1f8[   45.633169]  show_stack 0x2c/0x7c[   45.634039]  ftrace_modify_all_code 0x38/0x118[   45.634269]  arch_ftrace_update_code 0x10/0x18[   45.634495]  ftrace_run_update_code 0x2c/0x48[   45.634727]  ftrace_startup_enable 0x40/0x4c[   45.634943]  ftrace_startup 0xec/0x11c[   45.635137]  register_ftrace_function 0x68/0x84[   45.635369]  function_trace_init 0xa0/0xc4[   45.635574]  tracer_init 0x28/0x34[   45.635768]  tracing_set_tracer 0x11c/0x17c[   45.635982]  tracing_set_trace_write 0x124/0x170[   45.636224]  vfs_write 0x16c/0x368[   45.636409]  ksys_write 0x74/0x10c[   45.636594]  __arm64_sys_write 0x28/0x34[   45.636923]  el0_svc_common 0xf0/0x174[   45.637138]  do_el0_svc 0x84/0x90[   45.637330]  el0_svc 0x1c/0x28[   45.637510]  el0_sync_handler 0x3c/0xac[   45.637721]  el0_sync 0x140/0x180进一步查看ftrace_modify_all_code的代码,我们可以看到如下的调用流程:

ftrace_modify_all_code(command)  \--ftrace_update_ftrace_func(ftrace_ops_list_func)       |--pc = (unsignedlong)
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

印度海得拉巴和马萨诸塞州波士顿2022年4月20日 /美通社/ -- 全球领先的生命科学组织数据和分析提供商Excelra宣布,对价值和证据领域快速发展的年轻技术公...

关键字: ce

北京2022年4月11日 /美通社/ -- 亚马逊云科技助力数据服务和管理平台提供商Kyligence构建企业级云原生大数据OLAP解决方案,使其云上交付速度提升30%。同时,通过加入“ISV(独立软件供应商)加速赢计划...

关键字: ce ge 进程

(全球TMT2022年4月11日讯)亚马逊云科技助力数据服务和管理平台提供商Kyligence构建企业级云原生大数据OLAP解决方案,使其云上交付速度提升30%。同时,通过加入“ISV(独立软件供应商)加速赢计划”,K...

关键字: 亚马逊 ge ce

(全球TMT2022年4月7日讯)为Microsoft Dynamics 365和Salesforce提供云端配置、价格、报价(CPQ)和文档自动化软件的供应商Experlogix宣布与Microsoft Dynami...

关键字: ce Dynamics logix

价值9.02亿美元的OpenSpace在建筑和房地产领域继续得到对其技术的广泛采用,现已获取了1万多个工地现场超过6.5亿平米面积的影像...

关键字: pen ce

(全球TMT2022年2月21日讯)爱立信IoT Accelerator推出一款可靠、安全的蜂窝物联网平台,使全球电信运营商(CSP)和企业能够在数千万台设备上拓展其物联网业务。爱立信发布IoT Accelerator...

关键字: ce 蜂窝物联网 物联网平台

(全球TMT2022年2月22日讯)穆巴达拉投资公司(Mubadala Investment Company,简称“穆巴达拉”)以领投方身份完成了对总部位于新加坡的数据中心提供商Princeton Digital Gr...

关键字: Digital Group ce

英国伦敦和印度海得拉巴2022年2月10日 /美通社/ -- 数据科学和分析领导者Excelra 与人工智能先驱X-Chem之间的全新合作将加速临床前药物发现,帮助...

关键字: ce

灵活工作安排和“大辞职”潮无意间提高网络风险,值此之际,人工智能将加强安全团队 英国剑桥2022年1月28日 /美通社/ -- 网络安全人工智...

关键字: ce

(全球TMT2022年1月29日讯)网络安全人工智能领域的全球领导者Darktrace宣布,其自主响应技术现将在端点上采取行动 -- 完善Darktrace Antigena产品系列,该产品系列已覆盖SaaS应用程序、...

关键字: 人工智能 网络安全 ce
关闭
关闭