首页 > 其他分享 >CSA开发(1):基于插件扩展

CSA开发(1):基于插件扩展

时间:2023-02-04 20:56:24浏览次数:60  
标签:插件 MainCallCheckerPlugin CSA 扩展 clang 编译 const MainCallChecker

一、摘要

本文介绍如何通过编写插件实现对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 &registry) {
  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 &registry) {
      registry.addChecker<MainCallChecker>(
          "plugin.MainCallChecker", "Disallows calls to functions called main",
          "");
      }
    
    • registry.addChecker()中的 XXX 必须与检查器的类名保持一致。
    • 其第一个参数表示检查器的全称(这里是"plugin.MainCallChecker")
    • 第二个参数表示检查器的描述(这里是"Disallows calls to functions called main")
    • 参数表示检查器文档的URI地址(这里是"")
    • 第四个参数表示检查器是否对用户可见(默认值为false,表示对用户可见)。

(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_ANALYZERLLVM_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

标签:插件,MainCallCheckerPlugin,CSA,扩展,clang,编译,const,MainCallChecker
From: https://www.cnblogs.com/duclid/p/17092287.html

相关文章