1、基本原理以及使用教程,参考:
https://github.com/dynup/kpatch/blob/meastr/README.mdhttps://github.com/dynup/kpatch/blob/master/doc/patch-author-guide.md

2、测试环境:
系统:CentOS 7
内核版本:3.10.0-957.5.1.el7.x86_64
工具:gcc 4.8.5,ccache(官方建议安装,可以明显提高build速度)
测试用例:
kpatch/test/integration/centos-7目录
kpatch/test/testmod目录

3、给内核(vmlinux)打热补丁:
对应命令:kpatch-build -t vmlinux xxx.patch(会根据当前运行的内核版本找到源码目录)
或者:kpatch-build -s src_dir -t vmlinux xxx.patch (指定内核源码目录)
其中xxx.patch对应生成的patch文件,利用diff工具生成,参照下面命令:

diff  -urNap src.orig  src  > diff.pacth  //比对目录
diff  -uNap  test.c.org  test.c  >  diff.patch  //对比单个文件

用例:kpatch/test/integration/centos-7/data-new.patch

ksp Android 使用示例 ksp教程_kernel


会在当前目录下生成一个livepatch-data-new.ko,目前3.10内核已经原生支持内核热补丁,叫做livepatch,生成的补丁会以livepach作为前缀。如果内核不支持,则生成kpatch为前缀的模块,并且加载热补丁模块前,需要加载kpatch.ko模块。

该patch定义了一个全局变量,值为5,并在/proc/meminfo中加了一行打印,然后输出这个全局变量。加载模块并执行:

insmod  livepatch-data-new.ko  //或 kpatch load livepacth-data-new.ko
cat /proc/meminfo

ksp Android 使用示例 ksp教程_kpatch_02


可以看出,多了一行 kpatch: 5 的打印,热补丁应用成功。

如果需要临时关闭或打开补丁功能,可以执行:

echo 0 > /sys/kernel/livepatch/livepatch_data_new/enabled  //关闭
echo 1 > /sys/kernel/livepatch/livepatch_data_new/enabled  //打开

卸载模块:

ksp Android 使用示例 ksp教程_kernel_03


出于安全考虑,一般卸载并不是立马生效,可以看出会等待一个时间,保证没有别的函数调用。

此处注意:不要用rmmod方式卸载,会报错:

ksp Android 使用示例 ksp教程_kpatch_04

4、给内核模块打热补丁
用例:kpatch/test/integration/centos-7/module-shadow.patch
该用例是给kvm模块打了个patch,构建方式和前面有区别:
命令:

kpatch-build -t modules module-shadow.patch

ksp Android 使用示例 ksp教程_ksp Android 使用示例_05

此处-t后面改为modules,其实-t后面就是你make后面的target,比如你要编译内核模块,是用make modules,所以-t后面用modules,如该你此处用vmlinux,则会提示no changed objects found。 该patch其实就是给kvm-intel模块打了个热补丁。

5、给私有模块打热补丁
用例:kpatch/test/testmod
该用例也是给module打补丁,但是区别是该patche是给一个第三方的module打补丁。官方解释为out-of-tree module,我个人理解就是非linux内核主线维护的,比如你自己写的驱动,或者某些厂家提供的定制驱动等。
运行该用例前需要改造一下代码,用例代码两年没更新了,没有用kpatch-build构建,而是直接用了kpath-build目录下的create-diff-object二进制程序,你跑doit.sh脚本会自动构建,但是会报错,提示参数不对,应该是用例代码没更新,跟不上create-diff-object的更新。
此处我们用kpatch构建,改造一下Makefile:

testmod.ko: testmod_drv.c
#       patch < patch
#       KCFLAGS="-ffunction-sections -fdata-sections" $(MAKE) -C $(BUILD) M=$(PWD) testmod.ko
#       strip --keep-file-symbols -d testmod_drv.o
#       cp testmod_drv.o testmod_drv.o.patched
#       patch -R < patch
#       KCFLAGS="-ffunction-sections -fdata-sections" $(MAKE) -C $(BUILD) M=$(PWD) testmod.ko
#       strip --keep-file-symbols -d testmod_drv.o
#       cp testmod_drv.o testmod_drv.o.orig
#       $(MAKE) -C $(BUILD) M=$(PWD) clean
        $(MAKE) -C $(BUILD) M=$(PWD) testmod.ko

all: testmod.ko

clean:
        $(MAKE) -C $(BUILD) M=$(PWD) clean
#       rm *.orig *.patched

kbuild rules:
obj-m := testmod.o
testmod-y := testmod_drv.o

注释掉一些行。注释的行其实在kpatch-build命令中,已经帮我们做了。执行make all,会生成testmod.ko模块,加载模块看看:

ksp Android 使用示例 ksp教程_Linux_06


patch内容:

--- testmod_drv.c.orig	2014-06-02 16:49:49.428509600 -0500
+++ testmod_drv.c	2014-06-02 16:49:56.973656791 -0500
@@ -11,7 +11,7 @@
 static ssize_t value_show(struct kobject *kobj,
                           struct kobj_attribute *attr, char *buf)
 {
-	return sprintf(buf, "%d\n", value);
+	return sprintf(buf, "%d\n", value+1);
 }
 
 static struct kobj_attribute testmod_value_attr = __ATTR_RO(value);

可以看出只是简单做了+1操作,所以预期执行结果应该是那个value变成3。
进入testmod目录执行:

kpatch-build  -s ./ -t all  -e  ./testmod.ko  patch  //-e 参数指明你编译出来的原始的module

执行后报错:
ERROR: /root/kpatch/test/testmod/patch file failed to apply. Check
/root/.kpatch/build.log for more details.
原因是因为patch出了问题,改造一下patch文件:
先复制一份:cp pacth diff.patch,修改如下:

--- patch	2019-02-26 10:54:16.428000000 +0800
+++ diff.patch	2019-03-13 11:28:52.824000000 +0800
@@ -1,5 +1,5 @@
---- testmod_drv.c.orig	2014-06-02 16:49:49.428509600 -0500
-+++ testmod_drv.c	2014-06-02 16:49:56.973656791 -0500
+--- a/testmod_drv.c	2014-06-02 16:49:49.428509600 -0500
++++ b/testmod_drv.c	2014-06-02 16:49:56.973656791 -0500
 @@ -11,7 +11,7 @@
  static ssize_t value_show(struct kobject *kobj,
                            struct kobj_attribute *attr, char *buf)

再次执行,仍然会报错,这次是由于kpatch-build的bug导致,参看:

https://github.com/dynup/kpatch/issues/941 解决方法:

https://github.com/joe-lawrence/kpatch/commit/afcfbfa9ba6c77abbbd1caef3323632df52b982d

修改后再次执行构建:

ksp Android 使用示例 ksp教程_ksp Android 使用示例_07


加载livepacth-diff.ko后:

ksp Android 使用示例 ksp教程_热补丁_08


可以看出,确实和预期一样,value变为3,关闭后,又恢复原来值。内核热补丁生效。