How to do it...

On registration, the kprobe probe places a breakpoint (or jump, if optimized) instruction at the start of the probed instruction. When the breakpoint is hit, a trap occurs, the registers are saved, and control passes to kprobes, which calls the pre-handler. It then single steps the breakpoint and calls the post-handler. If a fault occurs, the fault handler is called. Handlers can be null if desired.

A kprobe probe can be inserted either in a function symbol or into an address, using the offset field, but not in both.

On occasions, kprobe will still be too intrusive to debug certain problems, as it slows the functions and may affect scheduling and be problematic when called from interrupt context.

For example, to place a kprobe probe in the open syscall, we would use the meta-bsp-custom/recipes-kernel/open-kprobe/files/kprobe_open.c custom module as follows:

#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/kprobes.h> 
 
static struct kprobe kp = { 
  .symbol_name  = "do_sys_open", 
}; 
 
static int handler_pre(struct kprobe *p, struct pt_regs *regs) 
{ 
  pr_info("pre_handler: p->addr = 0x%p, lr = 0x%lx," 
    " sp = 0x%lx
", 
  p->addr, regs->ARM_lr, regs->ARM_sp); 
 
  /* A dump_stack() here will give a stack backtrace */ 
  return 0; 
} 
 
static void handler_post(struct kprobe *p, struct pt_regs *regs, 
      unsigned long flags) 
{ 
  pr_info("post_handler: p->addr = 0x%p, status = 0x%lx
", 
    p->addr, regs->ARM_cpsr); 
} 
 
static int handler_fault(struct kprobe *p, struct pt_regs *regs, 
int trapnr) { pr_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 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) { pr_err("register_kprobe failed, returned %d ", ret); return ret; } pr_info("Planted kprobe at %p ", kp.addr); return 0; } static void kprobe_exit(void) { unregister_kprobe(&kp); pr_info("kprobe at %p unregistered ", kp.addr); } module_init(kprobe_init) module_exit(kprobe_exit) MODULE_LICENSE("GPL");

We compile it with a Yocto recipe, as explained in the Building external kernel modules recipe in Chapter 2, The BSP Layer. Here is the code for the meta-bsp-custom/recipes-kernel/open-kprobe/open-kprobe.bb Yocto recipe file:

SUMMARY = "kprobe on do_sys_open kernel module." 
LICENSE = "GPLv2" 
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL- 
2.0;md5=801f80980d171dd6425610833a22dbe6" inherit module PV = "0.1" SRC_URI = " file://kprobe_open.c file://Makefile " S = "${WORKDIR}"

With the Makefile file in meta-bsp-custom/recipes-kernel/open-kprobe/files/Makefile being as follows:

obj-m  := kprobe_open.o 
 
SRC := $(shell pwd) 
 
all: 
  $(MAKE) -C "$(KERNEL_SRC)" M="$(SRC)" 
 
modules_install: 
  $(MAKE) -C "$(KERNEL_SRC)" M="$(SRC)" modules_install 
 
clean: 
  rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c 
  rm -f Module.markers Module.symvers modules.order 
  rm -rf .tmp_versions Modules.symvers 

Copy it to a target running the same kernel it has been linked against, and load it with the following:

$ insmod kprobe_open.ko
Planted kprobe at 8010da84  

We can now see the handlers printing in the console when a file is opened:

pre_handler: p->addr = 0x8014d608, lr = 0x8014d858, sp = 0xd8b99f98 
pre_handler: p->addr = 0x8014d608, lr = 0x8014d858, sp = 0xd89abf98 
post_handler: p->addr = 0x8014d608, status = 0x800e0013 
post_handler: p->addr = 0x8014d608, status = 0x80070013 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset