用LLVM/Clang编译器插件实现除法保护(4)[完] - sbw Blog

用LLVM/Clang编译器插件实现除法保护(4)[完]

来源: 石博文博客 | 浏览: 2613 | 评论: 0 发表时间: 2019-03-29

上一篇已经成功深入到指令层面了,这一篇就开始使用 IRBuilder 对函数流程进行修改,使得“不安全”版本的除法函数也增加对除数的零检查。因为除法指令分为整数除法、浮点数除法等等,而例如整数除法又可以细分为有符号数除法、无符号数除法,同时每种除法又根据类型不同分为32位除法、64位除法....。这里为了演示效果,我们只实现了32位的有符号除法。仅仅满足了测试代码的要求,如果要真的在生产中实现这个功能,细节上的很多问题还是要仔细分类处理的。



IRBuilder

参考 doc: IRBuilder
除法指令处理函数

添加一个processSignedDiv函数用来处理有符号除法:

我们先获取到指令并转成一个BinaryOperator,二元指令就是加减乘除这些指令的类型,它包含一个代表操作类型的操作码及两个操作数。这里取出它的第二个操作数即除数,然后我们构造两个常量0和1。


接下来我们构造一个IRBuilder,构造参数中的inst代表代码的插入点是inst这条指令之前。然后使用IRBuilder插入一条比较指令,比较除数与0,将这条指令的结果用作跳转条件。


这里我没有使用CreatePHI来创建PHI节点,而是使用了CreateSelect这条指令。其实它们的作用一样,只不过PHI节点更强大,可以分发处理多个分支的选择,而我们的需求比较简单,仅仅是二选一,所以这里就偷懒使用CreateSelect了。select指令根据cond指令的结果来进行判断,如果cond成立,即除数等于零时,选择1为结果,否则选择除数为结果。这样我们就成功进行了除法检查,然后再把这个“新”的,经过处理后的除数,设置到原来的除法指令中去,这样就把整个流程闭合住了。


一点额外的处理

记录处理指令

由于我们是遍历除法指令,然后添加比较指令,修改原除法指令的操作数,所以我们需要记录一下哪些除法指令是已经被处理过的了,否则就陷入无限循环之中了。添加set procceedInstructions;来记录已经处理完的指令列表,并在调用processSignedDiv之前进行重复检查:


测试

检查中间代码结果

编译生成新的插件模块,然后使用系统的clang编译器加载我们的插件编译示例代码为LLVM IR,可以看到我们新添加的cmp指令及select指令已经生效了:


编译并运行测试程序

使用编译器加载插件编译并运行测试程序,可以看到无论是“安全”版本的除法函数,还是“不安全”版本的除法函数,都正确处理了除零检查,没有发生崩溃的问题:


这样,我们这个功能就完成了。如果需要对无符号整数或其它位宽的整数,又或者浮点数进行相同的处理,只需要参考文档加更多的条件判断即可,万变不离其宗。


附:完整代码



没有人评论过此文,还不快抢个沙发
  • 昵称: *
  • 邮箱:
  • 网址:
  • 记住我的信息
  • Color
  • Red
  • Blue
  • Code
  • bash
  • cpp
  • css
  • java
  • js
  • perl
  • php
  • python
  • ruby
  • sql
  • xml