加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • kprobe
    • jprobe
    • kretprobe
    • Kprobe-based Event Tracing
    • 總結(jié)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

【調(diào)試】kprobes(二)使用方法

2023/04/17
2072
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

上一節(jié)介紹了kprobe的基本概念,下面我們將使用幾個(gè)具體的例子,看下kprobe在實(shí)際使用中有那些應(yīng)用場景。

kprobe

內(nèi)核的samples/kprobe目錄下有kprobe相關(guān)的例子,我們以這些例子為基礎(chǔ),簡單修改下。

查看函數(shù)的入?yún)?/h4>

我們所有的例子都是探測do_sys_open() 或者_do_fork(),以下是內(nèi)核中的源碼。

do_sys_open
struct?audit_names;
struct?filename?{
?const?char??*name;?/*?pointer?to?actual?string?*/
?const?__user?char?*uptr;?/*?original?userland?pointer?*/
?struct?audit_names?*aname;
?int???refcnt;
?const?char??iname[];
};

long?do_sys_open(int?dfd,?const?char?__user?*filename,?int?flags,?umode_t?mode)
{
?struct?open_flags?op;
?int?fd?=?build_open_flags(flags,?mode,?&op);
?struct?filename?*tmp;

?if?(fd)
??return?fd;

?tmp?=?getname(filename);
?if?(IS_ERR(tmp))
??return?PTR_ERR(tmp);

?fd?=?get_unused_fd_flags(flags);
?if?(fd?>=?0)?{
??struct?file?*f?=?do_filp_open(dfd,?tmp,?&op);
??if?(IS_ERR(f))?{
???put_unused_fd(fd);
???fd?=?PTR_ERR(f);
??}?else?{
???fsnotify_open(f);
???fd_install(fd,?f);
??}
?}
?putname(tmp);
?return?fd;
}
_do_fork
long?_do_fork(unsigned?long?clone_flags,
???????unsigned?long?stack_start,
???????unsigned?long?stack_size,
???????int?__user?*parent_tidptr,
???????int?__user?*child_tidptr,
???????unsigned?long?tls)
{
?struct?task_struct?*p;
?int?trace?=?0;
?long?nr;

?/*
??*?Determine?whether?and?which?event?to?report?to?ptracer.??When
??*?called?from?kernel_thread?or?CLONE_UNTRACED?is?explicitly
??*?requested,?no?event?is?reported;?otherwise,?report?if?the?event
??*?for?the?type?of?forking?is?enabled.
??*/
?if?(!(clone_flags?&?CLONE_UNTRACED))?{
??if?(clone_flags?&?CLONE_VFORK)
???trace?=?PTRACE_EVENT_VFORK;
??else?if?((clone_flags?&?CSIGNAL)?!=?SIGCHLD)
???trace?=?PTRACE_EVENT_CLONE;
??else
???trace?=?PTRACE_EVENT_FORK;

??if?(likely(!ptrace_event_enabled(current,?trace)))
???trace?=?0;
?}

?p?=?copy_process(clone_flags,?stack_start,?stack_size,
????child_tidptr,?NULL,?trace,?tls,?NUMA_NO_NODE);
?/*
??*?Do?this?prior?waking?up?the?new?thread?-?the?thread?pointer
??*?might?get?invalid?after?that?point,?if?the?thread?exits?quickly.
??*/
?if?(!IS_ERR(p))?{
??struct?completion?vfork;
??struct?pid?*pid;

??cpufreq_task_times_alloc(p);

??trace_sched_process_fork(current,?p);

??pid?=?get_task_pid(p,?PIDTYPE_PID);
??nr?=?pid_vnr(pid);

??if?(clone_flags?&?CLONE_PARENT_SETTID)
???put_user(nr,?parent_tidptr);

??if?(clone_flags?&?CLONE_VFORK)?{
???p->vfork_done?=?&vfork;
???init_completion(&vfork);
???get_task_struct(p);
??}

??wake_up_new_task(p);

??/*?forking?complete?and?child?started?to?run,?tell?ptracer?*/
??if?(unlikely(trace))
???ptrace_event_pid(trace,?pid);

??if?(clone_flags?&?CLONE_VFORK)?{
???if?(!wait_for_vfork_done(p,?&vfork))
????ptrace_event_pid(PTRACE_EVENT_VFORK_DONE,?pid);
??}

??put_pid(pid);
?}?else?{
??nr?=?PTR_ERR(p);
?}
?return?nr;
}

實(shí)際調(diào)試中經(jīng)常需要調(diào)查函數(shù)使用的變量的值。要在kprobes的偵測器內(nèi)顯示某個(gè)函數(shù)的局部變量的值,需要一些技巧,原因是在printk的參數(shù)中無法直接指定變量名,因此必須給偵測器函數(shù)提供一個(gè)pt_regs結(jié)構(gòu),其中保存了指定地址的命令執(zhí)行時(shí)的寄存器信息。

當(dāng)然,不同架構(gòu)下該結(jié)構(gòu)的成員變量不盡相同,但用該結(jié)構(gòu)可以顯示變量等更為詳細(xì)的信息。

ARM64,ARM32,X86的寄存器及其訪問方式可以看文末的目錄

kprobe_example.c
/*
?*?NOTE:?This?example?is?works?on?x86?and?powerpc.
?*?Here's?a?sample?kernel?module?showing?the?use?of?kprobes?to?dump?a
?*?stack?trace?and?selected?registers?when?_do_fork()?is?called.
?*
?*?For?more?information?on?theory?of?operation?of?kprobes,?see
?*?Documentation/kprobes.txt
?*
?*?You?will?see?the?trace?data?in?/var/log/messages?and?on?the?console
?*?whenever?_do_fork()?is?invoked?to?create?a?new?process.
?*/

#include?<linux/kernel.h>
#include?<linux/module.h>
#include?<linux/kprobes.h>
#define?TRACE_SYMBOL?"do_filp_open"

/*?For?each?probe?you?need?to?allocate?a?kprobe?structure?*/
static?struct?kprobe?kp?=?{
?.symbol_name?=?TRACE_SYMBOL,
};
/*?x86_64中寄存器中參數(shù)的順序:?rdi?rsi?rdx?rcx?r8?r9*/
/*?aarch64:?x0-x7?對應(yīng)參數(shù)?*/
/*?kprobe?pre_handler:?called?just?before?the?probed?instruction?is?executed?*/
static?int?handler_pre(struct?kprobe?*p,?struct?pt_regs?*regs)
{
?int?dfd?=?-1;
?struct?filename?*filename?=?NULL;
#ifdef?CONFIG_X86
?dfd?=?regs->di;
????filename?=?(struct?filename?*)?regs->si;
#endif

#ifdef?CONFIG_ARM64
???dfd?=?regs->regs[0];
????filename?=?(struct?filename?*)?regs->regs[1];
#endif

??if?(filename?&&?!(strcmp(filename->name,?"testfile")))
????????printk(KERN_INFO?"handler_pre:%s:?dfd=%d,?name=%sn",?p->symbol_name,?dfd,?filename->name);

?return?0;
}

/*?kprobe?post_handler:?called?after?the?probed?instruction?is?executed?*/
static?void?handler_post(struct?kprobe?*p,?struct?pt_regs?*regs,
????unsigned?long?flags)
{
?//printk(KERN_INFO?"handler_postn");
}

/*
?*?fault_handler:?this?is?called?if?an?exception?is?generated?for?any
?*?instruction?within?the?pre-?or?post-handler,?or?when?Kprobes
?*?single-steps?the?probed?instruction.
?*/
static?int?handler_fault(struct?kprobe?*p,?struct?pt_regs?*regs,?int?trapnr)
{
?/*printk(KERN_INFO?"fault_handler:?p->addr?=?0x%p,?trap?#%dn",
??p->addr,?trapnr);*/
?/*?Return?0?because?we?don't?handle?the?fault.?*/
?return?0;
}

static?int?__init?kprobe_init(void)
{
?int?ret;
?kp.pre_handler?=?handler_pre;
?kp.post_handler?=?handler_post;
?kp.fault_handler?=?handler_fault;

?ret?=?register_kprobe(&kp);
?if?(ret?<?0)?{
??printk(KERN_INFO?"register_kprobe?failed,?returned?%dn",?ret);
??return?ret;
?}
?printk(KERN_INFO?"Planted?kprobe?at?%pn",?kp.addr);
?return?0;
}

static?void?__exit?kprobe_exit(void)
{
?unregister_kprobe(&kp);
?printk(KERN_INFO?"kprobe?at?%p?unregisteredn",?kp.addr);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

我們以內(nèi)核目錄下的例程做一個(gè)簡單修改,探測do_filp_open函數(shù),當(dāng)打開testfile文件時(shí),自動打印出文件的路徑。

為了減少無效信息的打印,我們將handler_post,handler_fault直接注釋掉。

當(dāng)探測點(diǎn)do_filp_open命中時(shí),Kprobes調(diào)用handler_pre。在handler_pre根據(jù)struct filename *pathname來獲得文件的名字。

在x86_64架構(gòu)中,函數(shù)的參數(shù)從左到右分別保存在rdi、rsi、rdx、rcx、r8、r9中,因此查看rdi和rsi就能得到第1個(gè)、第2個(gè)參數(shù)的值。

同理,在ARM64架構(gòu)中, 函數(shù)的參數(shù)1~參數(shù)8分別保存到 X0~X7 寄存器中 ,剩下的參數(shù)從右往左依次入棧。因此,X0和X1分別存放dfd, pathname的值。

makefile
CROSS_COMPILE:=aarch64-linux-gnu-
ARCH:=?arm64
CC:=?$(CROSS_COMPILE)gcc
LD:=?$(CROSS_COMPILE)ld

PWD:=?$(shell?pwd)
obj-m?:=?kprobe_example.o?jprobe_example.o??kretprobe_example.o

KERNELDIR:=/home/zhongyi/code/rk3399_linux_release_v2.5.1_20210301/kernel

all:
????????make?-C??$(KERNELDIR)?M=$(PWD)??modules?ARCH=$(ARCH)
clean:
????????rm?-f?*.o
????????rm?-f?*.symvers
????????rm?-f?*.order
????????rm?-f?*.ko
????????rm?-f?*.mod.c

執(zhí)行make編譯后,在開發(fā)板上將驅(qū)動加載后,手動打開testfile文件。

insmod?kprobe_example.ko
vim?testfile
rmmod?kprobe_example.ko
dmesg

使用dmesg可以看到成功輸出文件名和dfd。

[??307.572314]?Planted?kprobe?at?ffffff80081fdf84
[??311.997767]?handler_pre:do_filp_open:?dfd=-100,?name=testfile
[??312.034774]?handler_pre:do_filp_open:?dfd=-100,?name=testfile
[??347.969572]?kprobe?at?ffffff80081fdf84?unregistered

顯示棧跟蹤

使用kprobes的另一個(gè)有效的調(diào)試方法,就是顯示棧跟蹤。

我們只需要在handler_pre中調(diào)用dump_stack();即可。

/*?x86_64中寄存器中參數(shù)的順序:?rdi?rsi?rdx?rcx?r8?r9*/
/*?aarch64:?x0-x7?對應(yīng)參數(shù)?*/
/*?kprobe?pre_handler:?called?just?before?the?probed?instruction?is?executed?*/
static?int?handler_pre(struct?kprobe?*p,?struct?pt_regs?*regs)
{
?dump_stack();
?return?0;
}

編譯加載

insmod?kprobe_example.ko
rmmod?kprobe_example.ko
dmesg

成功打印出棧的信息。

[??451.620803]?CPU:?4?PID:?1299?Comm:?rmmod?Tainted:?G???????????O????4.4.194+?#18
[??451.620809]?Hardware?name:?Firefly-RK3399?Board?(Linux?Opensource)?(DT)
[??451.620813]?Call?trace:
[??451.620820]?[<ffffff8008088410>]?dump_backtrace+0x0/0x220
[??451.620828]?[<ffffff8008088654>]?show_stack+0x24/0x30
[??451.620834]?[<ffffff80084f842c>]?dump_stack+0x94/0xbc
[??451.620842]?[<ffffff8000f22048>]?handler_pre+0x14/0x24?[kprobe_example]
[??451.620848]?[<ffffff8008efd824>]?kprobe_breakpoint_handler+0x100/0x14c
[??451.620855]?[<ffffff8008084128>]?brk_handler+0x54/0x80
[??451.620860]?[<ffffff8008080b0c>]?do_debug_exception+0x58/0xc0
[??451.620866]?Exception?stack(0xffffffc0f2ef7c40?to?0xffffffc0f2ef7d70)
[??451.620879]?7c40:?ffffffc0ef782000?0000008000000000?ffffffc0f2ef7e20?ffffff80081fdf84
[??451.620886]?7c60:?0000000060000145?ffffff8008efc228?ffffffc0ceff2a50?ffffffc0ee7d2988
[??451.620892]?7c80:?ffffffc0f2ef7ca0?ffffff80081c0dc8?ffffffc0f0582e70?00e80000e95f3f53
[??451.620898]?7ca0:?ffffffc0f2ef7d70?ffffff8008efe3e8?ffffffc0f2ef7ec0?0000005583d31928
[??451.620905]?7cc0:?0000000000000055?0000000092000047?ffffffc0ceec5100?ffffffc0dccbd500
[??451.620911]?7ce0:?0000000000000024?ffffffc0dccbd580?00000000ffffff9c?ffffffc0ef782000
[??451.620917]?7d00:?ffffffc0f2ef7e78?0000000000000000?0000000000000000?0000000000000003
[??451.620923]?7d20:?ffffffc0dcfc9a80?0000007fd94380e8?0000000000000000?fefefefefefefeff
[??451.620929]?7d40:?0000000000000001?0000007fd9437db8?0000000000000000?0000000000000000
[??451.620934]?7d60:?0000000000000000?000000007fffffde
[??451.620940]?[<ffffff8008082668>]?el1_dbg+0x18/0x7c
[??451.620947]?[<ffffff80081ed9a4>]?SyS_openat+0x3c/0x4c
[??451.620953]?[<ffffff8008082f70>]?el0_svc_naked+0x24/0x28
[??451.630032]?kprobe?at?ffffff80081fdf84?unregistered

任意位置通過變量名獲取信息

kprobes擁有更加強(qiáng)大的功能,那就是它能在內(nèi)核的任意地址插入偵測器。此外,偵測器可以在任意地址的指令執(zhí)行之前或之后執(zhí)行,或者前后都執(zhí)行。

因此,應(yīng)當(dāng)觀察匯編代碼,找到源代碼中想要調(diào)查的位置對應(yīng)于編譯后的二進(jìn)制文件中的什么地址,并調(diào)查希望顯示的變量保存在哪個(gè)寄存器、哪個(gè)內(nèi)存地址。

通常,我們希望在函數(shù)執(zhí)行的過程中變量,即打印一些流程中的東西,而不是函數(shù)本身被調(diào)用,此時(shí)我們不能簡單設(shè)置 kprobe->symbol_name 函數(shù)名字 ,假設(shè)我們期望獲取 _do_fork函數(shù)變量 nr 的值:

將vmlinux進(jìn)行反匯編,找出_do_fork的地址。

aarch64-linux-gnu-objdump?-s?-d?vmlinux?>?vmlinux.asm

_do_fork 反匯編如下所示,地址為ffffff80080ba83c。

ffffff80080ba83c?<_do_fork>:
ffffff80080ba83c:???????a9b97bfd????????stp?????x29,?x30,?[sp,?#-112]!
ffffff80080ba840:???????910003fd????????mov?????x29,?sp
ffffff80080ba844:???????a90153f3????????stp?????x19,?x20,?[sp,?#16]
ffffff80080ba848:???????a9025bf5????????stp?????x21,?x22,?[sp,?#32]
ffffff80080ba84c:???????a90363f7????????stp?????x23,?x24,?[sp,?#48]
ffffff80080ba850:???????aa0003f5????????mov?????x21,?x0
ffffff80080ba854:???????aa0103f3????????mov?????x19,?x1
ffffff80080ba858:???????aa0203f6????????mov?????x22,?x2
ffffff80080ba85c:???????aa0303f7????????mov?????x23,?x3
ffffff80080ba860:???????aa0403f8????????mov?????x24,?x4
ffffff80080ba864:???????aa1e03e0????????mov?????x0,?x30
ffffff80080ba868:???????97ff4e8a????????bl??????ffffff800808e290?<_mcount>
ffffff80080ba86c:???????37b814f5????????tbnz????w21,?#23,?ffffff80080bab08?<_do_fork+0x2cc>
ffffff80080ba870:???????37701495????????tbnz????w21,?#14,?ffffff80080bab00?<_do_fork+0x2c4>
ffffff80080ba874:???????92401ea0????????and?????x0,?x21,?#0xff
ffffff80080ba878:???????52800074????????mov?????w20,?#0x3???????????????????????//?#3
ffffff80080ba87c:???????f100441f????????cmp?????x0,?#0x11
ffffff80080ba880:???????1a9f1694????????csinc???w20,?w20,?wzr,?ne??//?ne?=?any
ffffff80080ba884:???????11000e81????????add?????w1,?w20,?#0x3
............................
ffffff80080ba91c:???????b5000fb6????????cbnz????x22,?ffffff80080bab10?<_do_fork+0x2d4>
ffffff80080ba920:???????52800001????????mov?????w1,?#0x0????????????????????????//?#0
ffffff80080ba924:???????aa1303e0????????mov?????x0,?x19
ffffff80080ba928:???????94006a17????????bl??????ffffff80080d5184?<get_task_pid>
ffffff80080ba92c:???????aa0003f6????????mov?????x22,?x0
ffffff80080ba930:???????94006a85????????bl??????ffffff80080d5344?pid_vnr>
ffffff80080ba934:???????93407c18????????sxtw????x24,?w0
ffffff80080ba938:???????36a00195????????tbz?????w21,?#20,?ffffff80080ba968?<_do_fork+0x12c>
ffffff80080ba93c:???????d5384101????????mrs?????x1,?sp_el0
ffffff80080ba940:???????f9400422????????ldr?????x2,?[x1,?#8]
ffffff80080ba944:???????aa1703e1????????mov?????x1,?x23
ffffff80080ba948:???????b1001021????????adds????x1,?x1,?#0x4

nr 變量 是 函數(shù)pid_vnr的返回值(也是子進(jìn)程的pid) ,根據(jù)ARM調(diào)用規(guī)范,調(diào)用完成pid_vnr()后,寄存器x0存放的就是其函數(shù)返回值。

參考:ARM64調(diào)用標(biāo)準(zhǔn) https://blog.51cto.com/u_15333820/3452605

通過反匯編可以知道,pid_vnrffffff80080ba930地址處被調(diào)用,因此,偵測器的插入地址就是在ffffff80080ba930之后,并且x0被改變之前。只要符合這兩個(gè)條件,放在哪里都無所謂。

因此,我們將kprobe的點(diǎn)設(shè)置為ffffff80080ba934,然后獲取 x0,就能獲取變量nr的值。

.offset 是探測點(diǎn)相對于_do_fork的偏移,在注冊時(shí)指定。我們這里的 offset = ffffff80080ba934 - ffffff80080ba83c = F8。

另外,反匯編能力就是多看匯編以及找到幾個(gè)關(guān)鍵點(diǎn)(例如常量,跳轉(zhuǎn)語句)就能定位到匯編對應(yīng)的源碼了,這里不再展開了。

/*
?*?NOTE:?This?example?is?works?on?x86?and?powerpc.
?*?Here's?a?sample?kernel?module?showing?the?use?of?kprobes?to?dump?a
?*?stack?trace?and?selected?registers?when?_do_fork()?is?called.
?*
?*?For?more?information?on?theory?of?operation?of?kprobes,?see
?*?Documentation/kprobes.txt
?*
?*?You?will?see?the?trace?data?in?/var/log/messages?and?on?the?console
?*?whenever?_do_fork()?is?invoked?to?create?a?new?process.
?*/

#include?<linux/kernel.h>
#include?<linux/module.h>
#include?<linux/kprobes.h>

/*?For?each?probe?you?need?to?allocate?a?kprobe?structure?*/
static?struct?kprobe?kp?=?{
?.symbol_name?=?"_do_fork",
????.offset?=?0xF8,
};

/*?kprobe?pre_handler:?called?just?before?the?probed?instruction?is?executed?*/
static?int?handler_pre(struct?kprobe?*p,?struct?pt_regs?*regs)
{
#ifdef?CONFIG_X86
?printk(KERN_INFO?"pre_handler:?p->addr?=?0x%p,?ip?=?%lx,"
???"?flags?=?0x%lx,rax?=?0x%lxn",
??p->addr,?regs->ip,?regs->flags,regs->ax);
#endif

#ifdef?CONFIG_ARM64
?pr_info("<%s>?pre_handler:?p->addr?=?0x%p,?pc?=?0x%lx,"
???"?pstate?=?0x%lx,x0?=?0x%lxn",
??p->symbol_name,?p->addr,?(long)regs->pc,?(long)regs->pstate,(long)regs->regs[0]);
#endif

?/*?A?dump_stack()?here?will?give?a?stack?backtrace?*/
?return?0;
}

/*?kprobe?post_handler:?called?after?the?probed?instruction?is?executed?*/
static?void?handler_post(struct?kprobe?*p,?struct?pt_regs?*regs,
????unsigned?long?flags)
{
#ifdef?CONFIG_X86
?printk(KERN_INFO?"post_handler:?p->addr?=?0x%p,?flags?=?0x%lxn",
??p->addr,?regs->flags);
#endif

#ifdef?CONFIG_ARM64
?pr_info("<%s>?post_handler:?p->addr?=?0x%p,?pstate?=?0x%lxn",
??p->symbol_name,?p->addr,?(long)regs->pstate);
#endif
}

/*
?*?fault_handler:?this?is?called?if?an?exception?is?generated?for?any
?*?instruction?within?the?pre-?or?post-handler,?or?when?Kprobes
?*?single-steps?the?probed?instruction.
?*/
static?int?handler_fault(struct?kprobe?*p,?struct?pt_regs?*regs,?int?trapnr)
{
?printk(KERN_INFO?"fault_handler:?p->addr?=?0x%p,?trap?#%dn",
??p->addr,?trapnr);
?/*?Return?0?because?we?don't?handle?the?fault.?*/
?return?0;
}

static?int?__init?kprobe_init(void)
{
?int?ret;
?kp.pre_handler?=?handler_pre;
?kp.post_handler?=?handler_post;
?kp.fault_handler?=?handler_fault;

?ret?=?register_kprobe(&kp);
?if?(ret?<?0)?{
??printk(KERN_INFO?"register_kprobe?failed,?returned?%dn",?ret);
??return?ret;
?}
?printk(KERN_INFO?"Planted?kprobe?at?%pn",?kp.addr);
?return?0;
}

static?void?__exit?kprobe_exit(void)
{
?unregister_kprobe(&kp);
?printk(KERN_INFO?"kprobe?at?%p?unregisteredn",?kp.addr);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

insmod?kprobe_example.ko
rmmod?kprobe_example.ko
dmesg

編譯加載后,成功打印出rax的值。

[??245.080636]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.080640]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.080936]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.080938]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.457340]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.457345]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.457643]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.457645]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.719208]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.719213]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.719505]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.719507]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.820761]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.820765]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??245.821061]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??245.821063]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??246.092572]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??246.092577]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??246.095863]?pre_handler:?p->addr?=?0x0000000050a6c3dd,?ip?=?ffffffffa5ca0009,?flags?=?0x246,rax?=?0x2
[??246.095867]?post_handler:?p->addr?=?0x0000000050a6c3dd,?flags?=?0x246
[??246.126196]?kprobe?at?0000000050a6c3dd?unregistered

jprobe

與kprobes相比,jprobes能更容易地獲取傳給函數(shù)的參數(shù)。有幾點(diǎn)需要注意:

    1. 處理程序應(yīng)該有與被探測函數(shù)相同的參數(shù)列表和返回類型;返回之前,必須調(diào)用

jprobe_return()

    1. (處理程序?qū)嶋H上從未返回,因?yàn)?/ol>

    jprobe_return()

      將控制權(quán)返回給Kprobes) 。

    查看函數(shù)的參數(shù)

    /*
    ?*?Here's?a?sample?kernel?module?showing?the?use?of?jprobes?to?dump
    ?*?the?arguments?of?_do_fork().
    ?*
    ?*?For?more?information?on?theory?of?operation?of?jprobes,?see
    ?*?Documentation/kprobes.txt
    ?*
    ?*?Build?and?insert?the?kernel?module?as?done?in?the?kprobe?example.
    ?*?You?will?see?the?trace?data?in?/var/log/messages?and?on?the
    ?*?console?whenever?_do_fork()?is?invoked?to?create?a?new?process.
    ?*?(Some?messages?may?be?suppressed?if?syslogd?is?configured?to
    ?*?eliminate?duplicate?messages.)
    ?*/
    
    #include?<linux/kernel.h>
    #include?<linux/module.h>
    #include?<linux/kprobes.h>
    
    /*
    ?*?Jumper?probe?for?_do_fork.
    ?*?Mirror?principle?enables?access?to?arguments?of?the?probed?routine
    ?*?from?the?probe?handler.
    ?*/
    
    /*?Proxy?routine?having?the?same?arguments?as?actual?_do_fork()?routine?*/
    #define?TRACE_SYMBOL?"do_filp_open"
    /*與do_filp_open?的參數(shù)完全相同*/
    static?struct?file?*?jp_do_filp_open(int?dfd,?struct?filename?*pathname,
    ??const?struct?open_flags?*op)
    {
    ?if?(pathname?&&?!(strcmp(pathname->name,?"testfile")))
    ?printk(KERN_INFO?"jprobe:?dfd?=?%d,?pathname?=?%sn",?dfd,?pathname->name);
    
    ?/*?Always?end?with?a?call?to?jprobe_return().?*/
    ?jprobe_return();
    ?return?0;
    }
    
    static?struct?jprobe?my_jprobe?=?{
    ?.entry???=?jp_do_filp_open,
    ?.kp?=?{
    ??.symbol_name?=?TRACE_SYMBOL,
    ?},
    };
    
    static?int?__init?jprobe_init(void)
    {
    ?int?ret;
    
    ?ret?=?register_jprobe(&my_jprobe);
    ?if?(ret?<?0)?{
    ??printk(KERN_INFO?"register_jprobe?failed,?returned?%dn",?ret);
    ??return?-1;
    ?}
    ?printk(KERN_INFO?"Planted?jprobe?at?%p,?handler?addr?%pn",
    ????????my_jprobe.kp.addr,?my_jprobe.entry);
    ?return?0;
    }
    
    static?void?__exit?jprobe_exit(void)
    {
    ?unregister_jprobe(&my_jprobe);
    ?printk(KERN_INFO?"jprobe?at?%p?unregisteredn",?my_jprobe.kp.addr);
    }
    
    module_init(jprobe_init)
    module_exit(jprobe_exit)
    MODULE_LICENSE("GPL");
    

    使用kprobes時(shí),必須通過寄存器或棧才能計(jì)算出參數(shù)的值。此外,計(jì)算方法還依賴于架構(gòu)。

    如果使用jprobes,那么無須了解架構(gòu)的詳細(xì)知識,也能簡單地查看參數(shù)的值。

    編譯加載驅(qū)動程序

    insmod?jprobe_example.ko
    vim?testfile
    rmmod?jprobe_example.ko
    dmesg
    

    成功打印出函數(shù)的參數(shù)

    [??612.670453]?jprobe?at?ffffff80081fdf84?unregistered
    [??867.293765]?Planted?jprobe?at?ffffff80081fdf84,?handler?addr?ffffff8000f1a000
    [??871.107502]?jprobe:?dfd?=?-100,?pathname?=?testfile
    [??871.147747]?jprobe:?dfd?=?-100,?pathname?=?testfile
    [??875.723761]?jprobe?at?ffffff80081fdf84?unregistered
    [??907.706066]?Planted?jprobe?at?ffffff80081fdf84,?handler?addr?ffffff8000f22000
    [??911.661891]?jprobe:?dfd?=?-100,?pathname?=?testfile
    [??911.694903]?jprobe:?dfd?=?-100,?pathname?=?testfile
    [??919.272187]?jprobe?at?ffffff80081fdf84?unregistered
    [?2296.830613]?Planted?jprobe?at?ffffff80081fdf84,?handler?addr?ffffff8000f2a000
    [?2302.164861]?jprobe:?dfd?=?-100,?pathname?=?testfile
    [?2302.200634]?jprobe:?dfd?=?-100,?pathname?=?testfile
    [?2307.407014]?jprobe?at?ffffff80081fdf84?unregistered
    

    kretprobe

    kretprobe 也是基于kprobe的,相比于kprobe和jprobe,實(shí)現(xiàn)相對復(fù)雜。下面我們以內(nèi)核目錄下的例程,簡單分析下。

    kretprobe_example.c

    /*
    ?*?kretprobe_example.c
    ?*
    ?*?Here's?a?sample?kernel?module?showing?the?use?of?return?probes?to
    ?*?report?the?return?value?and?total?time?taken?for?probed?function
    ?*?to?run.
    ?*
    ?*?usage:?insmod?kretprobe_example.ko?func=<func_name>
    ?*
    ?*?If?no?func_name?is?specified,?_do_fork?is?instrumented
    ?*
    ?*?For?more?information?on?theory?of?operation?of?kretprobes,?see
    ?*?Documentation/kprobes.txt
    ?*
    ?*?Build?and?insert?the?kernel?module?as?done?in?the?kprobe?example.
    ?*?You?will?see?the?trace?data?in?/var/log/messages?and?on?the?console
    ?*?whenever?the?probed?function?returns.?(Some?messages?may?be?suppressed
    ?*?if?syslogd?is?configured?to?eliminate?duplicate?messages.)
    ?*/
    
    #include?<linux/kernel.h>
    #include?<linux/module.h>
    #include?<linux/kprobes.h>
    #include?<linux/ktime.h>
    #include?<linux/limits.h>
    #include?<linux/sched.h>
    
    static?char?func_name[NAME_MAX]?=?"do_sys_open";
    module_param_string(func,?func_name,?NAME_MAX,?S_IRUGO);
    MODULE_PARM_DESC(func,?"Function?to?kretprobe;?this?module?will?report?the"
    ???"?function's?execution?time");
    
    /*?per-instance?private?data?*/
    struct?my_data?{
    ?ktime_t?entry_stamp;
    };
    
    /*?Here?we?use?the?entry_hanlder?to?timestamp?function?entry?*/
    static?int?entry_handler(struct?kretprobe_instance?*ri,?struct?pt_regs?*regs)
    {
    ?struct?my_data?*data;
    
    ?if?(!current->mm)
    ??return?1;?/*?Skip?kernel?threads?*/
    
    ?data?=?(struct?my_data?*)ri->data;
    ?data->entry_stamp?=?ktime_get();
    ?return?0;
    }
    
    /*
    ?*?Return-probe?handler:?Log?the?return?value?and?duration.?Duration?may?turn
    ?*?out?to?be?zero?consistently,?depending?upon?the?granularity?of?time
    ?*?accounting?on?the?platform.
    ?*/
    static?int?ret_handler(struct?kretprobe_instance?*ri,?struct?pt_regs?*regs)
    {
    ?int?retval?=?regs_return_value(regs);
    ?struct?my_data?*data?=?(struct?my_data?*)ri->data;
    ?s64?delta;
    ?ktime_t?now;
    
    ?now?=?ktime_get();
    ?delta?=?ktime_to_ns(ktime_sub(now,?data->entry_stamp));
    ?printk(KERN_INFO?"%s?returned?%d?and?took?%lld?ns?to?executen",
    ???func_name,?retval,?(long?long)delta);
    ?return?0;
    }
    
    static?struct?kretprobe?my_kretprobe?=?{
    ?.handler??=?ret_handler,
    ?.entry_handler??=?entry_handler,
    ?.data_size??=?sizeof(struct?my_data),
    ?/*?Probe?up?to?20?instances?concurrently.?*/
    ?.maxactive??=?20,
    };
    
    static?int?__init?kretprobe_init(void)
    {
    ?int?ret;
    
    ?my_kretprobe.kp.symbol_name?=?func_name;
    ?ret?=?register_kretprobe(&my_kretprobe);
    ?if?(ret?<?0)?{
    ??printk(KERN_INFO?"register_kretprobe?failed,?returned?%dn",
    ????ret);
    ??return?-1;
    ?}
    ?printk(KERN_INFO?"Planted?return?probe?at?%s:?%pn",
    ???my_kretprobe.kp.symbol_name,?my_kretprobe.kp.addr);
    ?return?0;
    }
    
    static?void?__exit?kretprobe_exit(void)
    {
    ?unregister_kretprobe(&my_kretprobe);
    ?printk(KERN_INFO?"kretprobe?at?%p?unregisteredn",
    ???my_kretprobe.kp.addr);
    
    ?/*?nmissed?>?0?suggests?that?maxactive?was?set?too?low.?*/
    ?printk(KERN_INFO?"Missed?probing?%d?instances?of?%sn",
    ??my_kretprobe.nmissed,?my_kretprobe.kp.symbol_name);
    }
    
    module_init(kretprobe_init)
    module_exit(kretprobe_exit)
    MODULE_LICENSE("GPL");
    

    struct kretprobe

    /*
    ?*?Function-return?probe?-
    ?*?Note:
    ?*?User?needs?to?provide?a?handler?function,?and?initialize?maxactive.
    ?*?maxactive?-?The?maximum?number?of?instances?of?the?probed?function?that
    ?*?can?be?active?concurrently.
    ?*?nmissed?-?tracks?the?number?of?times?the?probed?function's?return?was
    ?*?ignored,?due?to?maxactive?being?too?low.
    ?*
    ?*/
    struct?kretprobe?{
    ?struct?kprobe?kp;
    ?kretprobe_handler_t?handler;
    ?kretprobe_handler_t?entry_handler;
    ?int?maxactive;
    ?int?nmissed;
    ?size_t?data_size;
    ?struct?hlist_head?free_instances;
    ?raw_spinlock_t?lock;
    };
    
    typedef?int?(*kretprobe_handler_t)?(struct?kretprobe_instance?*,
    ????????struct?pt_regs?*);
    
    
    

    其中我們可以看到 struct kretprobe 結(jié)構(gòu)體中 有struct kprobe成員(kretprobe時(shí)基于 kprobe實(shí)現(xiàn)的)。handler:用戶自定義回調(diào)函數(shù),被探測函數(shù)返回后被調(diào)用,一般在這個(gè)函數(shù)中獲取被探測函數(shù)的返回值。

    entry_handler:用戶自定義回調(diào)函數(shù),這是Kretprobes 提供了一個(gè)可選的用戶指定的處理程序,它在函數(shù)入口上運(yùn)行。 每當(dāng) kretprobe 放置在函數(shù)入口處的 kprobe 被命中時(shí),都會調(diào)用用戶定義的 entry_handler,如果有的話。 如果 entry_handler 返回 0(成功),則保證在函數(shù)返回時(shí)調(diào)用相應(yīng)的返回處理程序。 如果 entry_handler 返回非零錯(cuò)誤,則 Kprobes 將返回地址保持原樣,并且 kretprobe 對該特定函數(shù)實(shí)例沒有進(jìn)一步的影響。

    maxactive:被探測函數(shù)可以同時(shí)活動的最大實(shí)例數(shù)。來指定可以同時(shí)探測多少個(gè)指定函數(shù)的實(shí)例。register_kretprobe() 預(yù)分配指定數(shù)量的 kretprobe_instance 對象。

    nmissed:跟蹤被探測函數(shù)的返回被忽略的次數(shù)(maxactive設(shè)置的過低)。

    data_size:表示kretprobe私有數(shù)據(jù)的大小,在注冊kretprobe時(shí)會根據(jù)該大小預(yù)留空間。

    free_instances :表示空閑的kretprobe運(yùn)行實(shí)例鏈表,它鏈接了本kretprobe的空閑實(shí)例struct kretprobe_instance結(jié)構(gòu)體表示。

    struct kretprobe_instance

    struct?kretprobe_instance?{
    ?struct?hlist_node?hlist;
    ?struct?kretprobe?*rp;
    ?kprobe_opcode_t?*ret_addr;
    ?struct?task_struct?*task;
    ?char?data[0];
    };
    
      1. 這個(gè)結(jié)構(gòu)體表示

    kretprobe

      1. 的運(yùn)行實(shí)例,前文說過被探測函數(shù)在跟蹤期間可能存在并發(fā)執(zhí)行的現(xiàn)象,因此kretprobe使用一個(gè)

    kretprobe_instance

      1. 來跟蹤一個(gè)執(zhí)行流,支持的上限為

    maxactive

      1. 。在沒有觸發(fā)探測時(shí),所有的

    kretprobe_instance

      1. 實(shí)例都保存在

    free_instances

      1. 表中,每當(dāng)有執(zhí)行流觸發(fā)一次

    kretprobe

      1. 探測,都會從該表中取出一個(gè)空閑的

    kretprobe_instance

      1. 實(shí)例用來跟蹤。

    kretprobe_instance

      1. 結(jié)構(gòu)提中的rp指針指向所屬的kretprobe;

    ret_addr

      1. 用于保存原始被探測函數(shù)的返回地址(后文會看到被探測函數(shù)返回地址會被暫時(shí)替換);task用于綁定其跟蹤的進(jìn)程;

    data

      1. 保存用戶使用的

    kretprobe

      1. 私有數(shù)據(jù),它會在整個(gè)

    kretprobe

      1. 探測運(yùn)行期間在

    entry_handler

    handler

      回調(diào)函數(shù)之間進(jìn)行傳遞(一般用于實(shí)現(xiàn)統(tǒng)計(jì)被探測函數(shù)的執(zhí)行耗時(shí))。

    register_kretprobe

    kretprobe探測點(diǎn)的blackpoint,用來表示不支持kretprobe探測的函數(shù)的信息。name表示該函數(shù)名,addr表示該函數(shù)的地址。

    struct?kretprobe_blackpoint?{
    ?const?char?*name;
    ?void?*addr;
    };
    1234
    

    blackpoint與架構(gòu)相關(guān),x86架構(gòu)不支持的kretprobe探測點(diǎn)如下:

    //?arch/x86/kernel/kprobes/core.c
    //?不支持kretprobe探測的函數(shù),從blacklist這個(gè)名字中我們也知道其含義了。
    struct?kretprobe_blackpoint?kretprobe_blacklist[]?=?{
    ?{"__switch_to",?},?/*?This?function?switches?only?current?task,?but
    ?????????doesn't?switch?kernel?stack.*/
    ?{NULL,?NULL}?/*?Terminator?*/
    };
    
    const?int?kretprobe_blacklist_size?=?ARRAY_SIZE(kretprobe_blacklist);
    123456789
    

    函數(shù)的開頭首先處理 kretprobe_blacklis t,如果指定的被探測函數(shù)在這個(gè)blacklist中就直接返回EINVAL,表示不支持探測,在x86架構(gòu)中是__switch_to 這個(gè)函數(shù),表示這個(gè)函數(shù)不能被kretprobe

    int?register_kretprobe(struct?kretprobe?*rp)
    {
    ?int?ret?=?0;
    ?struct?kretprobe_instance?*inst;
    ?int?i;
    ?void?*addr;
    
    ?if?(kretprobe_blacklist_size)?{
    ??addr?=?kprobe_addr(&rp->kp);
    ??if?(IS_ERR(addr))
    ???return?PTR_ERR(addr);
    ??//如果kretprobe到kretprobe_blacklist中函數(shù),則返回EINVAL
    ??for?(i?=?0;?kretprobe_blacklist[i].name?!=?NULL;?i++)?{
    ???if?(kretprobe_blacklist[i].addr?==?addr)
    ????return?-EINVAL;
    ??}
    ?}
    
    ?//內(nèi)核設(shè)置回調(diào)函數(shù)?pre_handler_kretprobe?。
    ?//與kprobe不同的是:kretprobe不支持用戶定義pre_handler和post_handler等回調(diào)函數(shù)。
    ?
    ?rp->kp.pre_handler?=?pre_handler_kretprobe;
    ?rp->kp.post_handler?=?NULL;
    ?rp->kp.fault_handler?=?NULL;
    ?rp->kp.break_handler?=?NULL;
    
    ?/*?Pre-allocate?memory?for?max?kretprobe?instances?*/
    ?if?(rp->maxactive?<=?0)?{
    #ifdef?CONFIG_PREEMPT
    ??rp->maxactive?=?max_t(unsigned?int,?10,?2*num_possible_cpus());
    #else
    ??rp->maxactive?=?num_possible_cpus();
    #endif
    ?}
    ?raw_spin_lock_init(&rp->lock);
    ?INIT_HLIST_HEAD(&rp->free_instances);
    ?//根據(jù)maxactive值分配?struct?kretprobe_instance?內(nèi)存空間
    ?for?(i?=?0;?i?<?rp->maxactive;?i++)?{
    ??inst?=?kmalloc(sizeof(struct?kretprobe_instance)?+
    ??????????rp->data_size,?GFP_KERNEL);
    ??if?(inst?==?NULL)?{
    ???free_rp_inst(rp);
    ???return?-ENOMEM;
    ??}
    ??INIT_HLIST_NODE(&inst->hlist);
    ??hlist_add_head(&inst->hlist,?&rp->free_instances);
    ?}
    
    ?rp->nmissed?=?0;
    ?/*?Establish?function?entry?probe?point?*/
    ?//注冊kprobe探測點(diǎn)
    ?ret?=?register_kprobe(&rp->kp);
    ?if?(ret?!=?0)
    ??free_rp_inst(rp);
    ?return?ret;
    }
    EXPORT_SYMBOL_GPL(register_kretprobe);
    

    最后調(diào)用 register_kprobe(&rp->kp),注冊kprobe點(diǎn),可以看出kretprobe也是基于kprobe機(jī)制實(shí)現(xiàn)的,kretprobe也是一種特殊形式的kprobe。

    kretprobe注冊完成后就默認(rèn)啟動探測。

    pre_handler_kretprobe

    pre_handler_kretprobe這個(gè)函數(shù)是內(nèi)核自己定義的,內(nèi)核已經(jīng)指定該回調(diào)函數(shù),不支持用戶自定義。這個(gè) kprobe pre_handler 在每個(gè) kretprobe 中注冊。 當(dāng)探針命中時(shí),它將設(shè)置返回探針。

    #ifdef?CONFIG_KRETPROBES
    /*
    ?*?This?kprobe?pre_handler?is?registered?with?every?kretprobe.?When?probe
    ?*?hits?it?will?set?up?the?return?probe.
    ?*/
    static?int?pre_handler_kretprobe(struct?kprobe?*p,?struct?pt_regs?*regs)
    {
    ?struct?kretprobe?*rp?=?container_of(p,?struct?kretprobe,?kp);
    ?unsigned?long?hash,?flags?=?0;
    ?struct?kretprobe_instance?*ri;
    
    ?/*
    ??*?To?avoid?deadlocks,?prohibit?return?probing?in?NMI?contexts,
    ??*?just?skip?the?probe?and?increase?the?(inexact)?'nmissed'
    ??*?statistical?counter,?so?that?the?user?is?informed?that
    ??*?something?happened:
    ??*/
    ?if?(unlikely(in_nmi()))?{
    ??rp->nmissed++;
    ??return?0;
    ?}
    
    ?/*?TODO:?consider?to?only?swap?the?RA?after?the?last?pre_handler?fired?*/
    ?hash?=?hash_ptr(current,?KPROBE_HASH_BITS);
    ?raw_spin_lock_irqsave(&rp->lock,?flags);
    ?if?(!hlist_empty(&rp->free_instances))?{
    ??ri?=?hlist_entry(rp->free_instances.first,
    ????struct?kretprobe_instance,?hlist);
    ??hlist_del(&ri->hlist);
    ??raw_spin_unlock_irqrestore(&rp->lock,?flags);
    
    ??ri->rp?=?rp;
    ??ri->task?=?current;
    ?(1)
    ??if?(rp->entry_handler?&&?rp->entry_handler(ri,?regs))?{
    ???raw_spin_lock_irqsave(&rp->lock,?flags);
    ???hlist_add_head(&ri->hlist,?&rp->free_instances);
    ???raw_spin_unlock_irqrestore(&rp->lock,?flags);
    ???return?0;
    ??}
    ?(2)
    ??arch_prepare_kretprobe(ri,?regs);
    
    ??/*?XXX(hch):?why?is?there?no?hlist_move_head??*/
    ??INIT_HLIST_NODE(&ri->hlist);
    ??kretprobe_table_lock(hash,?&flags);
    ??hlist_add_head(&ri->hlist,?&kretprobe_inst_table[hash]);
    ??kretprobe_table_unlock(hash,?&flags);
    ?}?else?{
    ??rp->nmissed++;
    ??raw_spin_unlock_irqrestore(&rp->lock,?flags);
    ?}
    ?return?0;
    }
    NOKPROBE_SYMBOL(pre_handler_kretprobe);
    
    
    entry_handler
    struct?kretprobe?*rp
    rp->entry_handler?&&?rp->entry_handler(ri,?regs)
    

    entry_handler這個(gè)回調(diào)函數(shù)就是用戶自己定義的回調(diào)函數(shù)(可選的用戶指定的處理程序),前面我們已經(jīng)介紹過了,在這里不再介紹。

    /*?Here?we?use?the?entry_hanlder?to?timestamp?function?entry?*/
    static?int?entry_handler(struct?kretprobe_instance?*ri,?struct?pt_regs?*regs)
    {
    ?struct?my_data?*data;
    
    ?//內(nèi)核線程?task->mm?==?NULL
    ?if?(!current->mm)
    ??return?1;?/*?Skip?kernel?threads?*/
    
    ?data?=?(struct?my_data?*)ri->data;
    ?data->entry_stamp?=?ktime_get();
    ?return?0;
    }
    
    arch_prepare_kretprobe

    arch_prepare_kretprobe(ri, regs)該函數(shù)架構(gòu)相關(guān),struct kretprobe_instance結(jié)構(gòu)體 的 ret_addr 成員用于保存并替換regs中的返回地址。返回地址被替換為kretprobe_trampoline。

    x86架構(gòu)

    //?arch/x86/kernel/kprobes/core.c
    
    #define?stack_addr(regs)?((unsigned?long?*)kernel_stack_pointer(regs))
    
    //?x86_64
    //?arch/x86/include/asm/ptrace.h
    static?inline?unsigned?long?kernel_stack_pointer(struct?pt_regs?*regs)
    {
    ?return?regs->sp;
    }
    //?arch/x86/kernel/kprobes/core.c
    void?arch_prepare_kretprobe(struct?kretprobe_instance?*ri,?struct?pt_regs?*regs)
    {
    ?unsigned?long?*sara?=?stack_addr(regs);
    
    ?ri->ret_addr?=?(kprobe_opcode_t?*)?*sara;
    
    ?/*?Replace?the?return?addr?with?trampoline?addr?*/
    ?*sara?=?(unsigned?long)?&kretprobe_trampoline;
    }
    NOKPROBE_SYMBOL(arch_prepare_kretprobe);
    
    //struct?kretprobe_instance?*ri;
    //ri->ret_addr;
    
    struct?kretprobe_instance?{
    ?kprobe_opcode_t?*ret_addr;??//用于保存原始被探測函數(shù)的返回地址
    };
    
    

    ARM64架構(gòu)

    //?arch/arm64/kernel/probes/kprobes.c
    
    void?__kprobes?arch_prepare_kretprobe(struct?kretprobe_instance?*ri,
    ??????????struct?pt_regs?*regs)
    {
    ?ri->ret_addr?=?(kprobe_opcode_t?*)regs->regs[30];
    
    ?/*?replace?return?addr?(x30)?with?trampoline?*/
    ?regs->regs[30]?=?(long)&kretprobe_trampoline;
    }
    

    ARM64架構(gòu)中regs->regs[30]是LR(procedure link register)寄存器(X30 :LR)。

    小結(jié)

    kretprobe是基于kprobe實(shí)現(xiàn)的,有一個(gè)固定的pre_handler回調(diào)函數(shù),在內(nèi)核中實(shí)現(xiàn),無需用戶編寫。而在kprobe中pre_handler函數(shù)是提供給用戶的回調(diào)函數(shù)。

    rp->kp.pre_handler?=?pre_handler_kretprobe;??//內(nèi)核中已經(jīng)實(shí)現(xiàn)
    rp->kp.post_handler?=?NULL;
    rp->kp.fault_handler?=?NULL;
    rp->kp.break_handler?=?NULL;
    

    kretprobe提供給用戶的兩個(gè)回調(diào)函數(shù):

    kretprobe_handler_t?handler;
    kretprobe_handler_t?entry_handler;?//?(可選)
    

    pre_handler回調(diào)函數(shù)會為kretprobe探測函數(shù)執(zhí)行的返回值做準(zhǔn)備工作,其中最主要的就是替換掉正常流程的返回地址,讓被探測函數(shù)在執(zhí)行之后能夠跳轉(zhuǎn)到kretprobe設(shè)計(jì)的函數(shù) kretprobe_trampoline中去。

    kretprobe_trampoline

    pre_handler_kretprobe函數(shù)返回后,kprobe流程接著執(zhí)行singlestep流程并返回到正常的執(zhí)行流程,被探測函數(shù)(do_fork)繼續(xù)執(zhí)行,直到它執(zhí)行完畢并返回。

    由于返回地址被替換為kretprobe_trampoline,所以跳轉(zhuǎn)到kretprobe_trampoline執(zhí)行,該函數(shù)架構(gòu)相關(guān)且有嵌入?yún)R編實(shí)現(xiàn)。

    該函數(shù)會獲取被探測函數(shù)的寄存器信息并調(diào)用用戶定義的回調(diào)函數(shù)輸出其中的返回值,最后函數(shù)返回正常的執(zhí)行流程。

    static?int?ret_handler(struct?kretprobe_instance?*ri,?struct?pt_regs?*regs)
    {
    ?unsigned?long?retval?=?regs_return_value(regs);
    ?......
    }
    
    
    static?struct?kretprobe?my_kretprobe?=?{
    ?.handler??=?ret_handler,
    };
    
    x86架構(gòu)

    (1)

    kretprobe_trampoline
    ?-->trampoline_handler
    kretprobe_trampoline
    

    (2)
    kretprobe_trampoline

    //?arch/x86/kernel/kprobes/core.c
    
    /*
    ?*?When?a?retprobed?function?returns,?this?code?saves?registers?and
    ?*?calls?trampoline_handler()?runs,?which?calls?the?kretprobe's?handler.
    ?*/
    asm(
    ?".global?kretprobe_trampolinen"
    ?".type?kretprobe_trampoline,?@functionn"
    ?"kretprobe_trampoline:n"
    #ifdef?CONFIG_X86_64
    ?/*?We?don't?bother?saving?the?ss?register?*/
    ?"?pushq?%rspn"
    ?"?pushfqn"
    ?SAVE_REGS_STRING
    ?"?movq?%rsp,?%rdin"
    ?"?call?trampoline_handlern"
    ?/*?Replace?saved?sp?with?true?return?address.?*/
    ?"?movq?%rax,?152(%rsp)n"
    ?RESTORE_REGS_STRING
    ?"?popfqn"
    #else
    ?"?pushfn"
    ?SAVE_REGS_STRING
    ?"?movl?%esp,?%eaxn"
    ?"?call?trampoline_handlern"
    ?/*?Move?flags?to?cs?*/
    ?"?movl?56(%esp),?%edxn"
    ?"?movl?%edx,?52(%esp)n"
    ?/*?Replace?saved?flags?with?true?return?address.?*/
    ?"?movl?%eax,?56(%esp)n"
    ?RESTORE_REGS_STRING
    ?"?popfn"
    #endif
    ?"?retn"
    ?".size?kretprobe_trampoline,?.-kretprobe_trampolinen"
    );
    NOKPROBE_SYMBOL(kretprobe_trampoline);
    STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
    

    (3)
    trampoline_handler

    //?arch/x86/kernel/kprobes/core.c
    
    /*
    ?*?Called?from?kretprobe_trampoline
    ?*/
    __visible?__used?void?*trampoline_handler(struct?pt_regs?*regs)
    {
    ?struct?kretprobe_instance?*ri?=?NULL;
    ?struct?hlist_head?*head,?empty_rp;
    ?struct?hlist_node?*tmp;
    ?unsigned?long?flags,?orig_ret_address?=?0;
    ?unsigned?long?trampoline_address?=?(unsigned?long)&kretprobe_trampoline;
    ?kprobe_opcode_t?*correct_ret_addr?=?NULL;
    
    ?INIT_HLIST_HEAD(&empty_rp);
    ?kretprobe_hash_lock(current,?&head,?&flags);
    ?/*?fixup?registers?*/
    #ifdef?CONFIG_X86_64
    ?regs->cs?=?__KERNEL_CS;
    #else
    ?regs->cs?=?__KERNEL_CS?|?get_kernel_rpl();
    ?regs->gs?=?0;
    #endif
    ?regs->ip?=?trampoline_address;
    ?regs->orig_ax?=?~0UL;
    
    ?/*
    ??*?It?is?possible?to?have?multiple?instances?associated?with?a?given
    ??*?task?either?because?multiple?functions?in?the?call?path?have
    ??*?return?probes?installed?on?them,?and/or?more?than?one
    ??*?return?probe?was?registered?for?a?target?function.
    ??*
    ??*?We?can?handle?this?because:
    ??*?????-?instances?are?always?pushed?into?the?head?of?the?list
    ??*?????-?when?multiple?return?probes?are?registered?for?the?same
    ??*??function,?the?(chronologically)?first?instance's?ret_addr
    ??*??will?be?the?real?return?address,?and?all?the?rest?will
    ??*??point?to?kretprobe_trampoline.
    ??*/
    ?hlist_for_each_entry_safe(ri,?tmp,?head,?hlist)?{
    ??if?(ri->task?!=?current)
    ???/*?another?task?is?sharing?our?hash?bucket?*/
    ???continue;
    
    ??orig_ret_address?=?(unsigned?long)ri->ret_addr;
    
    ??if?(orig_ret_address?!=?trampoline_address)
    ???/*
    ????*?This?is?the?real?return?address.?Any?other
    ????*?instances?associated?with?this?task?are?for
    ????*?other?calls?deeper?on?the?call?stack
    ????*/
    ???break;
    ?}
    
    ?kretprobe_assert(ri,?orig_ret_address,?trampoline_address);
    
    ?correct_ret_addr?=?ri->ret_addr;
    ?hlist_for_each_entry_safe(ri,?tmp,?head,?hlist)?{
    ??if?(ri->task?!=?current)
    ???/*?another?task?is?sharing?our?hash?bucket?*/
    ???continue;
    
    ??orig_ret_address?=?(unsigned?long)ri->ret_addr;
    ??if?(ri->rp?&&?ri->rp->handler)?{
    ???__this_cpu_write(current_kprobe,?&ri->rp->kp);
    ???get_kprobe_ctlblk()->kprobe_status?=?KPROBE_HIT_ACTIVE;
    ???ri->ret_addr?=?correct_ret_addr;
    ???ri->rp->handler(ri,?regs);
    ???__this_cpu_write(current_kprobe,?NULL);
    ??}
    
    ??recycle_rp_inst(ri,?&empty_rp);
    
    ??if?(orig_ret_address?!=?trampoline_address)
    ???/*
    ????*?This?is?the?real?return?address.?Any?other
    ????*?instances?associated?with?this?task?are?for
    ????*?other?calls?deeper?on?the?call?stack
    ????*/
    ???break;
    ?}
    
    ?kretprobe_hash_unlock(current,?&flags);
    
    ?hlist_for_each_entry_safe(ri,?tmp,?&empty_rp,?hlist)?{
    ??hlist_del(&ri->hlist);
    ??kfree(ri);
    ?}
    ?return?(void?*)orig_ret_address;
    }
    NOKPROBE_SYMBOL(trampoline_handler);
    

    (4)ri->rp->handler(ri, regs)表示執(zhí)行用戶態(tài)自定義的回調(diào)函數(shù)handler(用來獲取_do_fork函數(shù)的返回值),handler回調(diào)函數(shù)執(zhí)行完畢以后,調(diào)用recycle_rp_inst函數(shù)將當(dāng)前的kretprobe_instance實(shí)例從kretprobe_inst_table哈希表釋放,重新鏈入free_instances中,以備后面kretprobe觸發(fā)時(shí)使用,另外如果kretprobe已經(jīng)被注銷則將它添加到銷毀表中待銷毀。

    ri->rp->handler(ri,?regs);
    ?->recycle_rp_inst(ri,?&empty_rp);
    12
    void?recycle_rp_inst(struct?kretprobe_instance?*ri,
    ???????struct?hlist_head?*head)
    {
    ?struct?kretprobe?*rp?=?ri->rp;
    
    ?/*?remove?rp?inst?off?the?rprobe_inst_table?*/
    ?hlist_del(&ri->hlist);
    ?INIT_HLIST_NODE(&ri->hlist);
    ?if?(likely(rp))?{
    ??raw_spin_lock(&rp->lock);
    ??hlist_add_head(&ri->hlist,?&rp->free_instances);
    ??raw_spin_unlock(&rp->lock);
    ?}?else
    ??/*?Unregistering?*/
    ??hlist_add_head(&ri->hlist,?head);
    }
    NOKPROBE_SYMBOL(recycle_rp_inst);
    

    (5)trampoline_handler函數(shù)執(zhí)行完后,返回被探測函數(shù)的原始返回地址,執(zhí)行流程再次回到kretprobe_trampoline函數(shù)中,將保存的 sp 替換為真實(shí)的返回地址。
    從rax寄存器中取出原始的返回地址,然后恢復(fù)原始函數(shù)調(diào)用??臻g,最后跳轉(zhuǎn)到原始返回地址執(zhí)行,至此函數(shù)調(diào)用的流程就回歸正常流程了,整個(gè)kretprobe探測結(jié)束。

    /*?Replace?saved?sp?with?true?return?address.?*/
    ?"?movq?%rax,?152(%rsp)n"
    ?RESTORE_REGS_STRING
    ?"?popfqn"
    1234
    
    ARM64架構(gòu)

    (1)

    kretprobe_trampoline?
    ?-->trampoline_probe_handler
    kretprobe_trampoline?
    

    (2)
    kretprobe_trampoline

    //?arch/arm64/kernel/probes/kprobes_trampoline.S
    
    ENTRY(kretprobe_trampoline)
    ?sub?sp,?sp,?#S_FRAME_SIZE
    
    ?save_all_base_regs
    
    ?mov?x0,?sp
    ?bl?trampoline_probe_handler
    ?/*
    ??*?Replace?trampoline?address?in?lr?with?actual?orig_ret_addr?return
    ??*?address.
    ??*/
    ?mov?lr,?x0
    
    ?restore_all_base_regs
    
    ?add?sp,?sp,?#S_FRAME_SIZE
    ?ret
    
    ENDPROC(kretprobe_trampoline)
    

    (3)
    trampoline_probe_handler

    //?arch/arm64/kernel/probes/kprobes.c
    
    void?__kprobes?__used?*trampoline_probe_handler(struct?pt_regs?*regs)
    {
    ?struct?kretprobe_instance?*ri?=?NULL;
    ?struct?hlist_head?*head,?empty_rp;
    ?struct?hlist_node?*tmp;
    ?unsigned?long?flags,?orig_ret_address?=?0;
    ?unsigned?long?trampoline_address?=
    ??(unsigned?long)&kretprobe_trampoline;
    ?kprobe_opcode_t?*correct_ret_addr?=?NULL;
    
    ?INIT_HLIST_HEAD(&empty_rp);
    ?kretprobe_hash_lock(current,?&head,?&flags);
    
    ?/*
    ??*?It?is?possible?to?have?multiple?instances?associated?with?a?given
    ??*?task?either?because?multiple?functions?in?the?call?path?have
    ??*?return?probes?installed?on?them,?and/or?more?than?one
    ??*?return?probe?was?registered?for?a?target?function.
    ??*
    ??*?We?can?handle?this?because:
    ??*?????-?instances?are?always?pushed?into?the?head?of?the?list
    ??*?????-?when?multiple?return?probes?are?registered?for?the?same
    ??*??function,?the?(chronologically)?first?instance's?ret_addr
    ??*??will?be?the?real?return?address,?and?all?the?rest?will
    ??*??point?to?kretprobe_trampoline.
    ??*/
    ?hlist_for_each_entry_safe(ri,?tmp,?head,?hlist)?{
    ??if?(ri->task?!=?current)
    ???/*?another?task?is?sharing?our?hash?bucket?*/
    ???continue;
    
    ??orig_ret_address?=?(unsigned?long)ri->ret_addr;
    
    ??if?(orig_ret_address?!=?trampoline_address)
    ???/*
    ????*?This?is?the?real?return?address.?Any?other
    ????*?instances?associated?with?this?task?are?for
    ????*?other?calls?deeper?on?the?call?stack
    ????*/
    ???break;
    ?}
    
    ?kretprobe_assert(ri,?orig_ret_address,?trampoline_address);
    
    ?correct_ret_addr?=?ri->ret_addr;
    ?hlist_for_each_entry_safe(ri,?tmp,?head,?hlist)?{
    ??if?(ri->task?!=?current)
    ???/*?another?task?is?sharing?our?hash?bucket?*/
    ???continue;
    
    ??orig_ret_address?=?(unsigned?long)ri->ret_addr;
    ??if?(ri->rp?&&?ri->rp->handler)?{
    ???__this_cpu_write(current_kprobe,?&ri->rp->kp);
    ???get_kprobe_ctlblk()->kprobe_status?=?KPROBE_HIT_ACTIVE;
    ???ri->ret_addr?=?correct_ret_addr;
    ???ri->rp->handler(ri,?regs);
    ???__this_cpu_write(current_kprobe,?NULL);
    ??}
    
    ??recycle_rp_inst(ri,?&empty_rp);
    
    ??if?(orig_ret_address?!=?trampoline_address)
    ???/*
    ????*?This?is?the?real?return?address.?Any?other
    ????*?instances?associated?with?this?task?are?for
    ????*?other?calls?deeper?on?the?call?stack
    ????*/
    ???break;
    ?}
    
    ?kretprobe_hash_unlock(current,?&flags);
    
    ?hlist_for_each_entry_safe(ri,?tmp,?&empty_rp,?hlist)?{
    ??hlist_del(&ri->hlist);
    ??kfree(ri);
    ?}
    ?return?(void?*)orig_ret_address;
    }
    

    (4)
    將 lr寄存器中的trampoline地址替換為實(shí)際的 orig_ret_addr 返回地址。
    x0寄存器中取出原始的返回地址,然后恢復(fù)原始函數(shù)調(diào)用棧空間,最后跳轉(zhuǎn)到原始返回地址執(zhí)行,至此函數(shù)調(diào)用的流程就回歸正常流程了,整個(gè)kretprobe探測結(jié)束。

    /*
    ??*?Replace?trampoline?address?in?lr?with?actual?orig_ret_addr?return
    ??*?address.
    ??*/
    ?mov?lr,?x0
    
    ?restore_all_base_regs
    
    ?add?sp,?sp,?#S_FRAME_SIZE
    ?ret
    

    編譯運(yùn)行

    insmod?kprobe_example.ko
    vim?testfile
    rmmod?kprobe_example.ko
    dmesg
    

    成功打印出函數(shù)的執(zhí)行時(shí)間

    [?1056.875938]?do_sys_open?returned?-2?and?took?10500?ns?to?execute
    [?1057.567400]?do_sys_open?returned?34?and?took?59208?ns?to?execute
    [?1058.382932]?do_sys_open?returned?3?and?took?31469101?ns?to?execute
    [?1058.567046]?do_sys_open?returned?34?and?took?61250?ns?to?execute
    [?1058.975879]?do_sys_open?returned?3?and?took?224084?ns?to?execute
    [?1058.975935]?do_sys_open?returned?3?and?took?16917?ns?to?execute
    [?1058.976041]?do_sys_open?returned?3?and?took?13417?ns?to?execute
    [?1058.976148]?do_sys_open?returned?3?and?took?15167?ns?to?execute
    [?1058.976254]?do_sys_open?returned?3?and?took?15750?ns?to?execute
    [?1058.976356]?do_sys_open?returned?3?and?took?16042?ns?to?execute
    [?1058.978036]?do_sys_open?returned?-2?and?took?23041?ns?to?execute
    [?1058.978074]?do_sys_open?returned?3?and?took?24500?ns?to?execute
    [?1058.978175]?do_sys_open?returned?-2?and?took?9334?ns?to?execute
    [?1058.978211]?do_sys_open?returned?3?and?took?23333?ns?to?execute
    [?1058.978246]?do_sys_open?returned?3?and?took?13417?ns?to?execute
    [?1058.978286]?do_sys_open?returned?3?and?took?14583?ns?to?execute
    [?1058.989701]?kretprobe?at?ffffff80081ed6c8?unregistered
    [?1058.989709]?Missed?probing?0?instances?of?do_sys_open
    

    Kprobe-based Event Tracing

    這些事件類似于基于tracepoint的事件。與Tracepoint不同,它是基于kprobes(kprobe和kretprobe)的。所以它可以探測任何kprobes可以探測的地方。與基于Tracepoint的事件不同的是,它可以動態(tài)地添加和刪除。

    要啟用這個(gè)功能,在編譯內(nèi)核時(shí)CONFIG_KPROBE_EVENTS=y

    Event Tracing類似,這不需要通過current_tracer來激活。可以通過/sys/kernel/debug/tracing/kprobe_events添加探測點(diǎn),并通過/sys/kernel/debug/tracing/events/kprobes/<EVENT>/enable來啟用它。

    你也可以使用/sys/kernel/debug/tracing/dynamic_events,而不是kprobe_events。該接口也將提供對其他動態(tài)事件的統(tǒng)一訪問。

    Synopsis of kprobe_events

    kprobe和內(nèi)核的ftrac結(jié)合使用,需要對內(nèi)核進(jìn)行配置,然后添加探測點(diǎn)、進(jìn)行探測、查看結(jié)果。

    kprobe配置

    CONFIG_KPROBES=y
    CONFIG_OPTPROBES=y
    CONFIG_KPROBES_ON_FTRACE=y
    CONFIG_UPROBES=y
    CONFIG_KRETPROBES=y
    CONFIG_HAVE_KPROBES=y
    CONFIG_HAVE_KRETPROBES=y
    CONFIG_HAVE_OPTPROBES=y
    CONFIG_HAVE_KPROBES_ON_FTRACE=y
    CONFIG_KPROBE_EVENT=y
    

    kprobe trace events使用

    kprobe事件相關(guān)的節(jié)點(diǎn)有如下:

    /sys/kernel/debug/tracing/kprobe_events-----------------------配置kprobe事件屬性,增加事件之后會在kprobes下面生成對應(yīng)目錄。
    /sys/kernel/debug/tracing/kprobe_profile----------------------kprobe事件統(tǒng)計(jì)屬性文件。
    /sys/kernel/debug/tracing/kprobes/<GRP>/<EVENT>/enabled-------使能kprobe事件
    /sys/kernel/debug/tracing/kprobes/<GRP>/<EVENT>/filter--------過濾kprobe事件
    /sys/kernel/debug/tracing/kprobes/<GRP>/<EVENT>/format--------查詢kprobe事件顯示格式
    

    kprobe事件配置

    新增一個(gè)kprobe事件,通過寫kprobe_events來設(shè)置。

    p[:[GRP/]EVENT]?[MOD:]SYM[+offs]|MEMADDR?[FETCHARGS]-------------------設(shè)置一個(gè)probe探測點(diǎn)
    r[:[GRP/]EVENT]?[MOD:]SYM[+0]?[FETCHARGS]------------------------------設(shè)置一個(gè)return?probe探測點(diǎn)
    -:[GRP/]EVENT----------------------------------------------------------刪除一個(gè)探測點(diǎn)
    

    細(xì)節(jié)解釋如下:

    GRP????????:?Group?name.?If?omitted,?use?"kprobes"?for?it.------------設(shè)置后會在events/kprobes下創(chuàng)建<GRP>目錄。
    ?EVENT????????:?Event?name.?If?omitted,?the?event?name?is?generated?based?on?SYM+offs?or?MEMADDR.---指定后在events/kprobes/<GRP>生成<EVENT>目錄。?MOD????????:?Module?name?which?has?given?SYM.--------------------------模塊名,一般不設(shè)
    ?SYM[+offs]????:?Symbol+offset?where?the?probe?is?inserted.-------------被探測函數(shù)名和偏移
    ?MEMADDR????:?Address?where?the?probe?is?inserted.----------------------指定被探測的內(nèi)存絕對地址
    ?FETCHARGS????:?Arguments.?Each?probe?can?have?up?to?128?args.----------指定要獲取的參數(shù)信息。?%REG????????:?Fetch?register?REG---------------------------------------獲取指定寄存器值
    ?@ADDR????????:?Fetch?memory?at?ADDR?(ADDR?should?be?in?kernel)--------獲取指定內(nèi)存地址的值
    ?@SYM[+|-offs]????:?Fetch?memory?at?SYM?+|-?offs?(SYM?should?be?a?data?symbol)---獲取全局變量的值?$stackN????:?Fetch?Nth?entry?of?stack?(N?>=?0)----------------------------------獲取指定??臻g值,即sp寄存器+N后的位置值
    ?$stack????:?Fetch?stack?address.-----------------------------------------------獲取sp寄存器值
    ?$retval????:?Fetch?return?value.(*)--------------------------------------------獲取返回值,用戶return?kprobe
    ?$comm????????:?Fetch?current?task?comm.----------------------------------------獲取對應(yīng)進(jìn)程名稱。
    ?+|-offs(FETCHARG)?:?Fetch?memory?at?FETCHARG?+|-?offs?address.(**)-------------?NAME=FETCHARG?:?Set?NAME?as?the?argument?name?of?FETCHARG.
    ?FETCHARG:TYPE?:?Set?TYPE?as?the?type?of?FETCHARG.?Currently,?basic?types?(u8/u16/u32/u64/s8/s16/s32/s64),?hexadecimal?types
    ??????????(x8/x16/x32/x64),?"string"?and?bitfield?are?supported.----------------設(shè)置參數(shù)的類型,可以支持字符串和比特類型
    ??(*)?only?for?return?probe.
    ??(**)?this?is?useful?for?fetching?a?field?of?data?structures.
    

    執(zhí)行如下兩條命令就會生成目錄/sys/kernel/debug/tracing/events/kprobes/myprobe;第三條命令則可以刪除指定kprobe事件,如果要全部刪除則echo > /sys/kernel/debug/tracing/kprobe_events

    echo?'p:myprobe?do_sys_open?dfd=%x0?filename=%x1?flags=%x2?mode=+4($stack)'?>?/sys/kernel/debug/tracing/kprobe_events
    echo?'r:myretprobe?do_sys_open?ret=$retval'?>>?/sys/kernel/debug/tracing/kprobe_events-----------------------------------------------------這里面一定要用">>",不然就會覆蓋前面的設(shè)置。
    
    echo?'-:myprobe'?>>?/sys/kernel/debug/tracing/kprobe_eventsecho?'-:myretprobe'?>>?/sys/kernel/debug/tracing/kprobe_events
    

    參數(shù)后面的寄存器是跟架構(gòu)相關(guān)的,%x0、%x1、%x2表示第1/2/3個(gè)參數(shù),超出部分使用$stack來存儲參數(shù)。

    函數(shù)返回值保存在$retval

    kprobe使能

    對kprobe事件的是能通過往對應(yīng)事件的enable寫1開啟探測;寫0暫停探測。

    echo?>?/sys/kernel/debug/tracing/trace
    echo?'p:myprobe?do_sys_open?dfd=%x0?filename=%x1?flags=%x2?mode=+4($stack)'?>?/sys/kernel/debug/tracing/kprobe_events
    echo?'r:myretprobe?do_sys_open?ret=$retval'?>>?/sys/kernel/debug/tracing/kprobe_events
    
    echo?1?>?/sys/kernel/debug/tracing/events/kprobes/myprobe/enable
    echo?1?>?/sys/kernel/debug/tracing/events/kprobes/myretprobe/enable
    ls
    echo?0?>?/sys/kernel/debug/tracing/events/kprobes/myprobe/enable
    echo?0?>?/sys/kernel/debug/tracing/events/kprobes/myretprobe/enable
    
    cat?/sys/kernel/debug/tracing/trace
    

    然后在/sys/kernel/debug/tracing/trace中可以看到結(jié)果。

    總結(jié)

    附錄

    ARM32,ARM64,X86寄存器及訪問方式

    ARM32

    "r0",?pt_regs->r0
    "r1",?pt_regs->r1
    "r2",?pt_regs->r2
    "r3",?pt_regs->r3
    "r4",?pt_regs->r4
    "r5",?pt_regs->r5
    "r6",?pt_regs->r6
    "r7",?pt_regs->r7
    "r8",?pt_regs->r8
    "r9",?pt_regs->r9
    "r10",pt_regs->r10
    "fp",?pt_regs->fp
    "ip",?pt_regs->ip
    "sp",?pt_regs->sp
    "lr",?pt_regs->lr
    "pc",?pt_regs->pc
    

    ARM64

    "x0",?pt_regs->regs[0]
    "x1",?pt_regs->regs[1]
    "x2",?pt_regs->regs[2]
    "x3",?pt_regs->regs[3]
    "x4",?pt_regs->regs[4]
    "x5",?pt_regs->regs[5]
    "x6",?pt_regs->regs[6]
    "x7",?pt_regs->regs[7]
    "x8",?pt_regs->regs[8]
    "x9",?pt_regs->regs[9]
    "x10",?pt_regs->regs[10]
    "x11",?pt_regs->regs[11]
    "x12",?pt_regs->regs[12]
    "x13",?pt_regs->regs[13]
    "x14",?pt_regs->regs[14]
    "x15",?pt_regs->regs[15]
    "x16",?pt_regs->regs[16]
    "x17",?pt_regs->regs[17]
    "x18",?pt_regs->regs[18]
    "x19",?pt_regs->regs[19]
    "x20",?pt_regs->regs[20]
    "x21",?pt_regs->regs[21]
    "x22",?pt_regs->regs[22]
    "x23",?pt_regs->regs[23]
    "x24",?pt_regs->regs[24]
    "x25",?pt_regs->regs[25]
    "x26",?pt_regs->regs[26]
    "x27",?pt_regs->regs[27]
    "x28",?pt_regs->regs[28]
    "x29",?pt_regs->regs[29]
    "x30",?pt_regs->regs[30]
    "sp",??pt_regs->sp
    "pc",??pt_regs->pc
    "pstate",pt_regs->pstate
    

    X86

    rax?????pt_regs->ax?
    rcx?????pt_regs->cx?
    rdx?????pt_regs->cx?
    rbx?????pt_regs->bx?
    rsp?????pt_regs->sp?
    rbp?????pt_regs->bp?
    rdi?????pt_regs->di?
    rsi?????pt_regs->si?
    r8??????pt_regs->r8?
    r9??????pt_regs->r9?
    r10?????pt_regs->r10?
    r11?????pt_regs->r11?
    r12?????pt_regs->r12?
    r13?????pt_regs->r13?
    r14?????pt_regs->r14?
    r15?????pt_regs->r15?
    

    本文參考

    https://blog.csdn.net/jakelylll/article/details/123667320

    https://www.cnblogs.com/LiuYanYGZ/p/12643846.html

    https://blog.csdn.net/weixin_45030965/article/details/125922528

    https://www.cnblogs.com/LiuYanYGZ/p/12643846.html

    https://blog.csdn.net/jasonactions/article/details/121065795

    https://blog.csdn.net/mrpre/article/details/106801888

    https://blog.csdn.net/u011622208/article/details/115535291

    kprobe https://blog.csdn.net/WANGYONGZIXUE/article/details/127525367

    https://www.kernel.org/doc/html/latest/trace/kprobetrace.html#kprobe-based-event-tracing

    https://www.cnblogs.com/arnoldlu/p/9752061.html

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

本公眾號專注原創(chuàng)電子軟硬件相關(guān)教程,希望這些教程能夠幫助到大家!