一、摘要
本文介绍如何通过编写插件实现对CSA检测项的扩展,以及当遇到 c++: fatal error: Killed signal terminated program cc1plus 问题的解决方案。
本文所介绍方法在 clang11、clang12、clang15 版本已验证。
二、详细步骤
(1)编写自定义Checker
在 clang/lib/Analysis/plugins 目录中新建 CheckerMainCall 文件夹。并在该目录下新建 MainCallChecker.cpp 文件,内容如下:
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
using namespace clang;
using namespace ento;
namespace {
class MainCallChecker : public Checker<check::PreCall> {
public:
void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const;
private:
mutable std::unique_ptr<BugType> BT;
};
} // anonymous namespace
void MainCallChecker::checkPreCall(const CallEvent &Call,
CheckerContext &Ctx) const {
if (const IdentifierInfo *II = Call.getCalleeIdentifier()) {
if (II->isStr("main")) {
if (!BT) {
BT.reset(new BugType(this, "Call to main", "Example checker"));
}
ExplodedNode *Node = Ctx.generateErrorNode();
auto Report
= std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), Node);
Ctx.emitReport(std::move(Report));
}
}
}
// Register plugin
extern "C" void clang_registerCheckers(CheckerRegistry ®istry) {
registry.addChecker<MainCallChecker>(
"plugin.MainCallChecker", "Disallows calls to functions called main",
"");
}
extern "C" const char clang_analyzerAPIVersionString[] =
CLANG_ANALYZER_API_VERSION_STRING;
-
在编译为插件的情况下,注册检查器的语法会发生变化。你不需要包含 ClangSACheckers.h 头文件,而是包含 CheckerRegistry.h 头文件:
#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
-
然后我们在我们的库中定义了一个外部可见的函数 clang_registerCheckers(),它将在分析器的 CheckerRegistry 中动态注册检查器:
extern "C" void clang_registerCheckers(CheckerRegistry ®istry) { registry.addChecker<MainCallChecker>( "plugin.MainCallChecker", "Disallows calls to functions called main", ""); }
- registry.addChecker
()中的 XXX 必须与检查器的类名保持一致。 - 其第一个参数表示检查器的全称(这里是"plugin.MainCallChecker")
- 第二个参数表示检查器的描述(这里是"Disallows calls to functions called main")
- 参数表示检查器文档的URI地址(这里是"")
- 第四个参数表示检查器是否对用户可见(默认值为false,表示对用户可见)。
- registry.addChecker
(2)编写exports文件
在目录 clang/lib/Analysis/plugins/CheckerMainCall/ 中新建 MainCallCheckerPlugin.exports 文件。其内容如下:
clang_registerCheckers
clang_analyzerAPIVersionString
- 这里的检查器注册函数名称 clang_registerCheckers 必须与 MainCallChecker.cpp 中的保持一致。
- 这里的全局变量名称 clang_analyzerAPIVersionString 必须与 MainCallChecker.cpp 中的保持一致。
(3)配置CMakeLists
在目录 clang/lib/Analysis/plugins/CheckerMainCall/ 中新建 CMakeLists.txt 文件。其内容如下:
# MainCallCheckerPlugin 为生成.so 插件名称
# MainCallCheckerPlugin.exports 为上文中文件
set(LLVM_LINK_COMPONENTS
Support
)
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/MainCallCheckerPlugin.exports)
add_llvm_library(MainCallCheckerPlugin MODULE BUILDTREE_ONLY MainCallChecker.cpp PLUGIN_TOOL clang)
clang_target_link_libraries(MainCallCheckerPlugin PRIVATE
clangAnalysis
clangAST
clangStaticAnalyzerCore
clangStaticAnalyzerFrontend
)
在 clang/lib/Analysis/plugins/CMakeLists.txt 文件中添加如下内容:
# CheckerMainCall即为上文中新建的目录名称。
add_subdirectory(CheckerMainCall)
(4)编译
生成插件需要在编译 Clang 时打开 CLANG_ENABLE_STATIC_ANALYZER 和 LLVM_ENABLE_PLUGINS 选项。默认情况下,这两个选项是打开的。
在构建目录,通常为 build 文件夹中执行如下命令:
cmake -DLLVM_TARGETS_TO_BUILD="X86;AArch64;RISCV" -DLLVM_ENABLE_PROJECTS=clang -DBUILD_SHARED_LIBS=ON -G Ninja ../llvm/
使用ninja编译,-jx可以根据自身情况设置。
ninja -j4
编译完成后,执行如下命令查看所生成插件中的检查器:
clang -cc1 -load ./lib/MainCallCheckerPlugin.so -analyzer-checker-help | grep MainCall
(5)测试插件
执行如下命令执行插件,注意修改 .so 插件的路径。
clang -cc1 -load /usr/local/llvm-project/clang-build/lib/MainCallCheckerPlugin.so -analyze -analyzer-checker=plugin.MainCallChecker Example.cpp
三、遇到问题及解决方案
(1)问题一
编译过程如果出现c++: fatal error: Killed signal terminated program cc1plus,表示虚拟内存(swap)不够,需要扩充,我们采用临时扩充的方法。
查看当前swap分区大小,发现只有2G。
free -m
增加到50G,空间大小为bs*count=50G
sudo dd if=/dev/zero of=/swapfile bs=1024 count=50000000
把刚才增加的空间转换为swap格式
sudo mkswap /swapfile
修改录权限
chmod 0600 /swapfile
使用刚才创建的50G swap空间
sudo swapon /swapfile
此时再查看,发现swap空间变为50G
free -m
内存扩充后继续执行
ninja -j4
最后释放临时内存
swapoff -a
检查swap空间是否变为2G
free -m
四、参考文献
[1] LLVM 之 Clang 静态分析器篇(2):如何扩展 Clang 静态分析器
[2] Windows 11系统中 GROMACS-GPU版在WSL2-Ubuntu18.04中的编译
[3] Artem Degrachev: Clang Static Analyzer: A Checker Developer's Guide