libcare Hello World测试例
荣涛 2021年9月14日

本文从网络转载。

  • GitHub Libcare:https://github.com/paboldin/libcare
  • Libcare HelloWorld测试例:https://gitee.com/openeuler/docs/blob/stable2-20.03_LTS_SP1/docs/zh/docs/Virtualization/LibcarePlus.md

1. LibcarePlus

  • 概述
  • 软硬件要求
  • 注意事项和约束
  • 安装 LibcarePlus
  • 制作 LibcarePlus 热补丁
  • 应用 LibcarePlus 热补丁

1.1. 概述

LibcarePlus 是一个用户态进程热补丁框架,可以在不重启进程的情况下对 Linux 系统上运行的目标进程进行热补丁操作。热补丁可以应用于 CVE 漏洞修复,也可以应用于不中断应用服务的紧急 bug 修复。

1.2. 软硬件要求

在 openEuler 上使用 LibcarePlus,需要满足一定的软硬件要求:

  • 当前仅支持 x86 体系架构。

  • LibcarePlus 可以在任何支持安装 libunwindelfutils 以及 binutils 的 Linux 发行版系统上运行。但目前仅在 openEuler 20.03 LTS SP1 版本进行了验证。

1.3. 注意事项和约束

使用 LibcarePlus,需遵循以下热补丁规范和约束:

  • 仅支持对 C 语言编写的代码,不支持汇编语言等。
  • 仅支持用户态程序,不支持动态库打补丁。
  • 代码文件名必须符合 C 语言标识符命名规范:由字母(A-Z,a-z)、数字 (0-9)、下划线“_”组成;并且首字符不能是数字,但可以是字母或者下划线;不能包含“-”、“$”等特殊符号。
  • 不支持增量补丁,即必须卸载原有补丁才能加载第二个补丁。
  • 不支持补丁自动加载。
  • 不支持补丁查询。
  • 被打热补丁的目标函数的出参和入参不能增加和删除。
  • 静态函数补丁受限于系统中能找到该函数的符号表。
  • 动态库热补丁只能对调用这个动态库的进程打补丁。
  • 以下场景不支持热补丁:
    • 死循环函数、不退出函数、inline 函数、初始化函数、NMI 中断处理函数
    • 替换全局变量
    • 修改头文件
    • 数据结构成员变化(新增、删除、修改)
    • 动态库、静态函数、静态变量
    • 修改全局变量、TLS 变量、RCU 变量
    • 修改包含 LINEFILE 等gcc编译宏的 C 文件
    • 修改 intel 矢量汇编指令

1.4. 安装 LibcarePlus

1.4.1. 安装软件依赖

LibcarePlus 运行依赖于 libunwindelfutilsbinutils,在配置了 yum 源的 openEuler 系统上,可以参考如下命令安装 LibcarePlus 的依赖软件。

$ sudo yum install -y binutils elfutils elfutils-libelf-devel libunwind-devel

1.4.1.1. 安装 LibcarePlus

$ yum install LibcarePlus -y

查看安装是否成功:

$ libcare-ctl -help
usage: libcare-ctl [options] <cmd> [args]Options:-v          - verbose mode-h          - this messageCommands:patch  - apply patch to a user-space processunpatch- unapply patch from a user-space processinfo   - show info on applied patchesserver - listen on a unix socket for commands

1.5. 制作 LibcarePlus 热补丁

1.5.1. 概述

LibcarePlus 支持如下方式制作热补丁:

  • 手动制作
  • 通过脚本制作

手动制作热补丁的过程繁琐,对于代码量较大的工程,例如QEMU,手动制作热补丁极其困难。建议使用 LibcarePlus 自带脚本一键式地生成热补丁文件。

1.5.1.1. 手动制作

本节以原文件 foo.c 和补丁文件 bar.c 为例,给出手动制作热补丁的指导。

  1. 准备 C 语言编写的原文件和补丁文件。例如原文件 foo.c 和补丁文件 bar.c 。

    点击展开 foo.c

    // foo.c
    #include <stdio.h>
    #include <time.h>void print_hello(void)
    {printf("Hello world!\n");
    }int main(void)
    {while (1) {print_hello();sleep(1);}
    }
    

    点击展开 bar.c

    // bar.c
    #include <stdio.h>
    #include <time.h>void print_hello(void)
    {printf("Hello world %s!\n", "being patched");
    }int main(void)
    {while (1) {print_hello();sleep(1);}
    }
    
  2. 编译得到原文件和补丁文件的汇编文件 foo.sbar.s,参考命令如下:

    $ gcc -S foo.c
    $ gcc -S bar.c
    $ ls
    bar.c  bar.s  foo.c  foo.s
    
  3. 使用 kpatch_gensrc 对比 foo.s 和 bar.s 差异,生成包含原文件的汇编内容和差异内容的 foobar.s,参考命令如下:

    $ sed -i 's/bar.c/foo.c/' bar.s
    $ kpatch_gensrc --os=rhel6 -i foo.s -i bar.s -o foobar.s --force-global
    

    由于 kpatch_gensrc 默认对同一 C 语言原文件进行对比,所以对比前需要使用 sed 命令将补丁汇编文件 bar.s 中的 bar.c 改为原文件名称 foo.c。随后调用 kpatch_gensrc,指定输入文件为 foo.s 与 bar.s,输出文件为 foobar.s。

  4. 编译原文件的汇编文件 foo.s 和生成的汇编文件 foobar.s,得到可执行文件 foo 和 foobar,参考命令如下:

    $ gcc -o foo foo.s
    $ gcc -o foobar foobar.s -Wl,-q
    
  5. 利用 kpatch_strip 去除可执行程序 foo 和 foobar 的相同内容,保留制作热补丁所需要的内容。

    $ kpatch_strip --strip foobar foobar.stripped
    $ kpatch_strip --rel-fixup foo foobar.stripped
    $ strip --strip-unneeded foobar.stripped
    $ kpatch_strip --undo-link foo foobar.stripped
    

    上述命令中的各参数含义为:

    • –strip 用于去除 foobar 中对于补丁制作无用的 section;
    • –rel-fixup 用于修复补丁内所访问的变量以及函数的地址;
    • strip --strip-unneeded 用于去除对于热补丁重定位操作无用的符号信息;
    • –undo-link 用于将补丁内符号的地址从绝对地址更改为相对地址。
  6. 制作热补丁文件。

    通过以上操作,已经得到了热补丁制作所需的主要内容。接下来需要使用 kpatch_make 将原可执行文件的 Build ID 以及 kpatch_strip 的输出文件 foobar.stripped 作为参数传递给 kpatch_make,最终生成热补丁文件,参考命令如下:

    $ str=$(readelf -n foo | grep 'Build ID')
    $ substr=${str##* }
    $ kpatch_make -b $substr foobar.stripped -o foo.kpatch
    $ ls
    bar.c  bar.s  foo  foobar  foobar.s  foobar.stripped  foo.c  foo.kpatch  foo.s
    

    至此,就得到了最终的热补丁文件 foo.kpatch。

1.5.1.2. 通过脚本制作

本节介绍如何利用 LibcarePlus 自带的 libcare-patch-make 脚本制作热补丁文件,仍以原文件 foo.c 和补丁文件 bar.c 为例。

  1. 利用 diff 命令生成 foo.c 和 bar.c 的对比文件,命令如下所示:

    $ diff -up foo.c bar.c > foo.patch
    

    foo.patch 文件内容如下所示:

    点击展开 foo.patch

    --- foo.c 2020-12-09 15:39:51.159632075 +0800
    +++ bar.c    2020-12-09 15:40:03.818632220 +0800
    @@ -1,10 +1,10 @@
    -// foo.c
    +// bar.c
    #include <stdio.h>
    #include <time.h>void i_m_being_patched(void)
    {
    -    printf("i'm unpatched!\n");
    +    printf("you patched my %s\n", "tralala");
    }int main(void)
    
  2. 编写编译 foo.c 的 MakeFile 文件,具体如下所示:

    点击展开 MakeFile

    all: foofoo: foo.c$(CC) -o $@ $<clean:rm -f fooinstall: foomkdir $$DESTDIR || :cp foo $$DESTDIR
    
  3. 编写好 MakeFile 之后,直接调用 libcare-patch-make 即可。若 libcare-patch-make 询问选择哪个文件进行打补丁操作,输入原文件名即可,具体如下所示:

    $ libcare-patch-make --clean foo.patch
    rm -f foo
    BUILDING ORIGINAL CODE
    /usr/local/bin/libcare-cc -o foo foo.c
    INSTALLING ORIGINAL OBJECTS INTO /libcareplus/test/lpmake
    mkdir $DESTDIR || :
    cp foo $DESTDIR
    applying foo.patch...
    can't find file to patch at input line 3
    Perhaps you used the wrong -p or --strip option?
    The text leading up to this was:
    --------------------------
    |--- foo.c  2020-12-10 09:43:04.445375845 +0800
    |+++ bar.c   2020-12-10 09:48:36.778379648 +0800
    --------------------------
    File to patch: foo.c
    patching file foo.c
    BUILDING PATCHED CODE
    /usr/local/bin/libcare-cc -o foo foo.c
    INSTALLING PATCHED OBJECTS INTO /libcareplus/test/.lpmaketmp/patched
    mkdir $DESTDIR || :
    cp foo $DESTDIR
    MAKING PATCHES
    Fixing up relocation printf@@GLIBC_2.2.5+fffffffffffffffc
    Fixing up relocation print_hello+0
    patch for /libcareplus/test/lpmake/foo is in /libcareplus/test/patchroot/700297b7bc56a11e1d5a6fb564c2a5bc5b282082.kpatch
    

    执行成功之后,输出显示:热补丁文件位于当前目录的 patchroot 目录下,可执行文件则在 lpmake 目录下。脚本生成的热补丁文件默认是采用 Build ID 作为热补丁文件的文件名。

1.6. 应用 LibcarePlus 热补丁

本节以原文件 foo.c 和补丁文件 bar.c 为例,介绍 LibcarePlus 热补丁的应用指导。

1.6.1. 前期准备

应用 LibcarePlus 热补丁之前,需要提前准备好原可执行程序 foo、以及热补丁文件 foo.kpatch。

1.6.2. 加载热补丁

本节介绍应用 LibcarePlus 热补丁的具体流程。

  1. 首先在第一个 shell 窗口运行需要打补丁的可执行程序,如下所示:

    $ ./lpmake/foo
    Hello world!
    Hello world!
    Hello world!
    
  2. 随后在第二个 shell 窗口运行 libcare-ctl 应用热补丁,命令如下所示:

    $ libcare-ctl -v patch -p $(pidof foo) ./foo.kpatch
    

    若此时热补丁应用成功,第二个 shell 窗口会有如下输出:

    1 patch hunk(s) have been successfully applied to PID '10999'
    

    而第一个 shell 窗口内运行的目标进程则会出现如下输出:

    Hello world!
    Hello world!
    Hello world being patched!
    Hello world being patched!
    

1.6.3. 卸载热补丁

本节介绍卸载 LibcarePlus 热补丁的具体流程。

  1. 在第二个 shell 窗口执行如下命令:

    $ libcare-ctl unpatch -p $(pidof foo)
    

    此时若热补丁卸载成功,第二个 shell 窗口会有如下输出:

    1 patch hunk(s) were successfully cancelled from PID '10999'
    
  2. 第一个 shell 窗口内运行的目标进程则会出现如下输出:

    Hello world being patched!
    Hello world being patched!
    Hello world!
    Hello world!
    

将热补丁应用于项目

正如上面提到的补丁应用,只是一个简单的补丁,当我们的项目比较复杂的情况下,如何应用热补丁呢。那么就需要用到libcare提供的脚本libcare-patch-make,它可以根据补丁patch文本文件直接生成热补丁,这是非常有用的。

在脚本libcare-patch-make中有:

 # 修改编译器到 libcare 库export IS_LIBCARE_CC=yexport CC=$KPATCH_PATH/libcare-ccexport CXX=$CC

这也就说明了,在我们的项目中,编译器应该使用环境变量($(CC)),而不应该使用编译器名称(如gccicc),如下:

当我们要使用libcare进行热补丁修复时,应该在Make file中采用下面的编译方法:

$(CC) -c master/main.c

而不是

gcc -c master/main.c

因为,这在脚本libcare-patch-make运行过程中,不会识别到gcc

构建复杂的项目

生成补丁

应用热补丁

Copyright (C) CESTC Com.

libcare Hello World测试例相关推荐

  1. c语言拱猪计分测试例,北理C语言作业及答案3.doc

    北理C语言作业及答案3北理C语言作业及答案3 C语言作业3 43. 缩写展开 成绩: 10 / 折扣: 0.8 在保存字符串时,对出现在连续位置的若干个字符,如这些字符在ASCII表中也是连续出现,则 ...

  2. 软件测试模糊搜索用例,模糊测试中测试用例生成方法.pdf

    模糊测试中测试用例生成方法 2015 年 第 24 卷 第 4 期 计 算 机 系 统 应 用 模糊测试中测试用例生成方法① 李 彤, 黄 轩, 黄 睿 (装甲兵工程学院 信息工程系, 北京 1000 ...

  3. 全球SDN测试认证中心发布OpenDaylight测试报告

    随着软件定义网络(Software Defined Network, SDN)商业部署速度地加快,关乎整个SDN 网络性能表现的控制平面核心组件--SDN 控制器也越来越成为网络用户关心的焦点.日前, ...

  4. ginkgo测试介绍

    1. 安装Ginkgo go get github.com/onsi/ginkgo/ginkgo go get github.com/onsi/gomega/... 附加的库,配合使用,下一篇会详细讲 ...

  5. linux系统测试报告,[Linux-文件系统测试] -- Bonnie++测试

    简述: Bonnie++是一个硬盘和文件系统的基准性能测试工具,它通过一系列的简单测试来生成硬盘和文件系统的性能参数.主要对三个方面做基准测试:数据读.写速度,每秒可以完成的磁盘寻道次数和每秒可以完成 ...

  6. dos窗口ping命令测试实战

    以下内容在win10 dos窗口下测试(win+r -> cmd) 1.ping +域名 C:\Users\Administrator>ping baidu.com正在 Ping baid ...

  7. TestNG执行测试

    使用TestNG类执行测试用例.这个类的主入口点在TestNG的框架运行测试.用户可以创建自己的TestNG的对象,并调用它以许多不同的方式: 在现有的testng.xml 合成testng.xml, ...

  8. 【测试基础】测试用例的设计方法

    等价类 等价类划分法将程序所有可能的输入数据(有效的和无效的)划分成若干个等价类.然后从每个部分中选取具有代表性的数据当做测试用例进行合理的分类,测试用例由有效等价类和无效等价类的代表组成,从而保证测 ...

  9. 【安全测试】可怕的越权

    之前看了一篇越权文章深受启发,于是就产生了下面的一系列想法,纯属个人观点,但不局限于此,如有更好想法的朋友,可留言自己观点. 一.登录权限越权 1.登录时长失效,这时当用户仍在此功能页面时,进行充值. ...

最新文章

  1. YOLOv4 论文翻译
  2. 【软件工程-Teamwork 3】团队角色分配和团队贡献分分配规则
  3. 【Android Developers Training】 68. 序言:添加动画
  4. I - 查找练习 hash——出现过的数字(水题A的)
  5. 【bzoj3289】 Mato的文件管理
  6. JVM从入门到精通(七):GC常用参数,Method Area,JVM调优案例分析
  7. 怎么简单的锁定文件夹_简单性与鲁棒性–在锁定文件处理中展示
  8. numpy.linspace()的使用方法
  9. 再谈table组件:固定表头和表列
  10. python装饰器解析请求参数_我如何在装饰器中获得Flask可选的URL参数?
  11. 基于中颖SH79F168单片机的航模无刷电调方案
  12. 取消Windows操作系统中自动播放
  13. oracle不完全恢复类型,Oracle——不完全恢復
  14. 一种适用于FDD+TDD基站天线阵列的多天线共存方式
  15. 技术贴 - 收藏集 - 掘金
  16. 电脑开机显示两个用户名怎么解决?
  17. 农妇守护瘫痪丈夫27年 单独抚育女儿撑起家庭
  18. [信息论与编码]离散信道及信道容量(三)
  19. 用Python调用OpenAI API进行文本创作
  20. vue中px 转 vh/vw

热门文章

  1. 仿百度,豆瓣读书文库阅读器
  2. lisp语言100以内勾股数_三个视频搞定:函数的最值、对勾函数、分式函数性质与图像、分段函数最值...
  3. mongodb数据库显示obj_Mongodb使用
  4. Linux 5.10将解决2038年问题
  5. Riophae/Vue-treeselect 的文档 及一些相关的问题
  6. MySQL学习-排序与分组函数
  7. Byobu:打造多任务的Terminal
  8. java IO包装流如何关闭
  9. Android笔记:invalidate()和postInvalidate() 的区别及使用(转载)
  10. 屏幕距离和坐便转换工具_【软件推荐】你和大神的距离,只差这几个效率工具!...