ClangChecker扩展插件

ClangCheckerExpandPlug

  1. 添加检查器的实现代码(检查用户是否调用了main函数)

    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;
    

    注:

    • 新建的目录名称不必是CheckerMainCall,也可以是其他名称。
    • 新建的源文件名称不必是MainCallChecker.cpp,也可以是其他名称。
    • 以插件方式扩展 Clang 静态分析器时,不需要修改Checkers.td文件。
    • 函数clang_registerCheckers()用于注册以插件方式提供的检查器。
    • registry.addChecker();中的XXX必须与检查器的类名保持一致。其第一个参数表示检查器的全称(这里是"plugin.MainCallChecker"),第二个参数表示检查器的描述(这里是"Disallows calls to functions called main"),第三个参数表示检查器文档的URI地址(这里是""),第四个参数表示检查器是否对用户可见(默认值为false,表示对用户可见)。
    • 全局变量clang_analyzerAPIVersionString表示生成该插件时所使用的 Clang API 版本。如果与使用该插件的 Clang API 版本不兼容,那么检查器会注册失败。
  2. 添加 MainCallCheckerPlugin.exports

    在目录clang/lib/Analysis/plugins/CheckerMainCall/中新建MainCallCheckerPlugin.exports文件。其内容如下:

    clang_registerCheckers
    clang_analyzerAPIVersionString
    

    注:

    • 新建的文件名称不必是MainCallCheckerPlugin.exports,也可以是其他名称。
    • 这里的检查器注册函数名称clang_registerCheckers必须与MainCallChecker.cpp中的保持一致。
    • 这里的全局变量名称clang_analyzerAPIVersionString必须与MainCallChecker.cpp中的保持一致。
  3. 添加 CMakeLists.txt

    在目录clang/lib/Analysis/plugins/CheckerMainCall/中新建CMakeLists.txt文件。其内容如下:

    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
      )
    

    注:

    • 这里的MainCallCheckerPlugin表示要生成的插件的名称为libMainCallCheckerPlugin.so
    • 这里的MainCallCheckerPlugin.exports即为上文中新添加的文件名称。
  4. 修改插件目录中的 CMakeLists.txt

    clang/lib/Analysis/plugins/CMakeLists.txt文件中添加如下内容:

    add_subdirectory(CheckerMainCall)
    

    注: 这里的CheckerMainCall即为上文中新建的目录名称。

    添加后的内容为:

    if(CLANG_ENABLE_STATIC_ANALYZER AND LLVM_ENABLE_PLUGINS)
      add_subdirectory(SampleAnalyzer)
      add_subdirectory(CheckerDependencyHandling)
      add_subdirectory(CheckerOptionHandling)
      add_subdirectory(CheckerMainCall)
    endif()
    

    需要注意的是, 要生成插件,需要在编译 Clang 时打开CLANG_ENABLE_STATIC_ANALYZERLLVM_ENABLE_PLUGINS选项。默认情况下,这两个选项是打开的。

  5. 编译插件

    省略

  6. 查看所生成插件中的检查器

    clang -cc1 -load /home/wxs/clang_build/lib/MainCallCheckerPlugin.so -analyzer-checker-help | grep MainCall
    

    output

    root@shjx-wxs:/home/wxs/clang_build/bin# clang -cc1 -load /home/wxs/clang_build/lib/MainCallCheckerPlugin.so -analyzer-checker-help | grep MainCall
      plugin.MainCallChecker        Disallows calls to functions called main
    

    从上面的输出可以看出:

    • plugin.MainCallChecker,即我们在插件中自定义的检查器的完整名称。
    • Disallows calls to functions called main,即我们在插件中为自定义的检查器所添加的描述。

    需要注意的是, 插件的路径(这里是/home/wxs/clang_build/lib/MainCallCheckerPlugin.so)可以是相对路径,也可以是绝对路径。

  7. 编写测试用例 Example_Test.c

    typedef int (*main_t)(int, char **);
    int main(int argc, char **argv) {
      main_t foo = main;
      int exit_code = foo(argc, argv);   // actually calls main()!
      return exit_code;
    }
    

    上述测试程序存在这样的错误:通过函数指针变量foo调用main函数。

  8. 运行插件

    以插件方式运行检查器,命令如下:

    clang -cc1 -load /home/wxs/clang_build/lib/MainCallCheckerPlugin.so -analyze -analyzer-checker=plugin.MainCallChecker Example_Test.c
    

    output

    root@shjx-wxs:/home/wxs/demo/clang-checker# clang -cc1 -load /home/wxs/clang_build/lib/MainCallCheckerPlugin.so -analyze -analyzer-checker=plugin.MainCallChecker Example_Test.c
    Example_Test.c:4:19: warning: Call to main [plugin.MainCallChecker]
      int exit_code = foo(argc, argv);   // actually calls main()!
                      ^~~~~~~~~~~~~~~
    1 warning generated.
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值