当前位置:首页 > 公众号精选 > 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)
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭