LLVM/Clang 提供了编译器插件机制可供扩展编译器的功能,实际上 LLVM/Clang 本身的很多功能也是以插件方式注册到编译器系统中进行实现的。在一些对安全要求极端苛刻的环境下,或是一些特殊的硬件平台中,总有一些奇怪的需求,而这些定制化的功能如果不是很复杂,就没有必要大动干戈去修改编译器,可以使用编译器插件来完成。这个系列文章设想一种需要进行除法保护的应用场景,需要在编译层面对除法进行强制检查,以杜绝除零异常的发生。
前言
需求描述
某硬件平台由于安全原因不允许除零异常的发生,由于C++无法捕获这种CPU异常进行处理,所以考虑在编译器层面进行预防。约定:所有除法指令必须进行除数检查,当除数为0时,直接返回被除数本身。
为了更明确目标,并提供测试方法,构造了以下示例代码:
在上面的测试代码中,有两个除法函数,其中一个是手动进行了除数检测的,而另一个是有“缺陷”的代码。在实际使用中,如果要完全实现对除数的检查,那就要在代码审查上多下功夫,确保足够仔细的去发现这类问题。但是,如果一个除法运算是混在复杂的逻辑中,或是在其它的头文件中定义的宏等等,此时除法运算就不是很明显,审核起来就很容易遗漏。同时在代码中加过多的if-else测试语句也对逻辑本身的可读性有所损害。
所以,编写这个编译器插件的目的就在于:无论代码中是否对除数进行了检查,都要保证运算结果和我们约定的结果相同,同时不能损害已有的逻辑,保证程序的正确性。
目标
我们先使用正常的编译器编译并运行示例代码,可以看到在遇到6/0这个计算时,出现了例外,程序崩溃了:
然后我们使用同一个编译器,并加载我们的插件,再编译运行:
这两次的编译我们没有更改任何源代码,也没有更改编译器及基本的编译参数,仅仅是加载了一个编译插件。可以看到,这次编译出的二进制中,两个函数的执行结果是相同的,而且避免了除零错误导致的崩溃问题。
下一篇,将开始从零建立开发环境,一步步实现这个编译器插件。