c++ - 使用LLVM检测C/C++代码

标签 c++ llvm static-analysis clang instrumentation

我刚刚阅读了有关LLVM项目的信息,该项目可用于使用LLVM前端的分析器Clang对C/C++代码进行静态分析。我想知道是否可以使用LLVM在源代码中提取对内存的所有访问(变量,本地以及全局)。

LLVM中是否有任何内置库可供我用来提取此信息。
如果不是,请建议我如何编写函数以完成此操作。(现有源代码,引用,教程,示例...)
我想过的是,我会先将源代码转换为LLVM bc,然后对其进行分析以进行分析,但不知道该怎么做。

我试图弄清楚自己应该使用哪种IR(Clang的抽象语法树(AST)或LLVM的SSA中间表示(IR)。),但实际上无法弄清楚要使用哪个IR。
这就是我想要做的。
给定任何C/C++程序(如以下给出的程序),我试图在对内存进行读/写的每条指令之前和之后插入对某个函数的调用。例如,考虑下面的C++程序(Account.cpp)

#include <stdio.h>

class Account {
  int balance;

public:
  Account(int b) {
    balance = b;
  }

  int read() {
    int r;
    r = balance;
    return r;
  }

  void deposit(int n) {
    balance = balance + n;
  }

  void withdraw(int n) {
    int r = read();
    balance = r - n;
  }
};

int main () {
  Account* a = new Account(10);
  a->deposit(1);
  a->withdraw(2);
  delete a;
}

因此,检测之后,我的程序应如下所示:
#include <stdio.h>

class Account {
  int balance;

public:
  Account(int b) {
    balance = b;
  }

  int read() {
    int r;
    foo();
    r = balance;
    foo();
    return r;
  }

  void deposit(int n) {
    foo();
    balance = balance + n;
    foo();
  }

  void withdraw(int n) {
    foo();
    int r = read();
    foo();
    foo();
    balance = r - n;
    foo();
  }
};

int main () {
  Account* a = new Account(10);
  a->deposit(1);
  a->withdraw(2);
  delete a;
}

其中foo()可以是任何函数,例如获取当前系统时间或增加计数器..等等。我知道要插入上述功能,我必须先获取IR,然后在IR上运行检测传递,以将此类调用插入IR,但是我真的不知道如何实现。请通过示例向我提出建议。

我也知道,一旦将程序编译到IR中,在原始程序和已检测的IR之间很难获得1:1映射。因此,是否有可能将IR中的更改(由于检测)反射(reflect)到原始程序中。

为了开始使用LLVM传递以及如何自己制作一个传递,我看了一个传递的示例,该传递将运行时检查添加到LLVM IR加载和存储,SAFECode的加载/存储检测传递(http://llvm.org/viewvc/llvm-project/safecode/trunk/include/safecode/LoadStoreChecks.h?view=markuphttp://llvm.org/viewvc/llvm-project/safecode/trunk/lib/InsertPoolChecks/LoadStoreChecks.cpp?view=markup) 。但是我不知道如何运行此通行证。请给我有关如何在上面的Account.cpp程序上运行此通行证的步骤。

最佳答案

首先,您必须决定要使用clang还是LLVM。它们都在非常不同的数据结构上运行,这些数据结构各有利弊。

从对问题的稀疏描述中,我建议您在LLVM中进行优化。与IR一起使用将使清理,分析和注入(inject)代码变得更加容易,因为这正是它的设计目标。缺点是您的项目将依赖于LLVM,这可能对您造成或可能不会成问题。您可以使用C后端输出结果,但人工无法使用。

使用优化过程时,另一个重要的缺点是,您还会丢失原始源代码中的所有符号。即使Value类(稍后会详细介绍)具有getName方法,您也应该永远不要依靠它包含任何有意义的内容。这是为了帮助您调试通行证而已。

您还必须对编译器有基本的了解。例如,了解basic blocksstatic single assignment form有点必要。幸运的是,它们并不是很难学习或理解的概念(Wikipedia文章应该足够)。

在开始编码之前,您首先必须阅读一些内容,因此这里有一些链接可以帮助您入门:

  • Architecture Overview :LLVM的快速体系结构概述。将为您提供一个很好的想法,即您正在使用什么工具以及LLVM是否适合您。
  • Documentation Head :您可以在此处找到以下所有链接以及更多链接。如果我错过任何事情,请引用此。
  • LLVM's IR reference :这是LLVM IR的完整描述,您将对其进行操作。该语言相对简单,因此无需太多学习。
  • Programmer's manual :使用LLVM时需要了解的基本知识的快速概述。
  • Writting Passes :编写转换或分析过程所需的一切。
  • LLVM Passes :您可以并且应该使用的LLVM提供的所有通行证的完整列表。这些确实可以帮助清理代码并使其更易于分析。例如,使用循环时,lcssasimplify-loopindvar传递将挽救您的生命。
  • Value Inheritance Tree :这是Value类的doxygen页面。这里重要的一点是继承树,您可以遵循它来获取IR引用页中定义的所有说明的文档。只需忽略他们称之为协作图的邪恶怪诞。
  • Type Inheritance Tree :与上述相同,但类型相同。

  • 一旦您了解了所有内容,那就好了。要查找内存访问?搜索storeload指令。要仪器吗?只需使用Value类的适当子类创建所需的内容,然后将其插入到store和load指令之前或之后。由于您的问题有点太笼统了,因此我不能为您提供更多帮助。 (请参见下面的更正)

    顺便说一句,几周前我不得不做类似的事情。在大约2-3周的时间里,我能够学习到有关LLVM的所有知识,创建分析遍历以在循环中查找内存访问(以及更多),并使用我创建的转换遍历对它们进行检测。不需要花哨的算法(LLVM提供的算法除外),并且一切都非常简单。故事的寓意是LLVM易于学习和使用。

    更正:我说您要做的就是搜索loadstore指令时出错了。
    loadstore指令将仅提供使用指针对堆进行的访问。为了获得所有的内存访问,您还必须查看可以代表堆栈中内存位置的值。该值是写入堆栈还是存储在寄存器中,是在寄存器分配阶段确定的,这在后端的优化过程中发生。这意味着它依赖于平台,因此不应被依赖。

    现在,除非您提供有关正在寻找的内存访问类型,在何种上下文中以及打算如何进行内存访问的更多信息,否则我将为您提供更多帮助。

    关于c++ - 使用LLVM检测C/C++代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7526550/

    相关文章:

    xcode - macOS 上的系统 llvm-config 在哪里?

    c - 在 C 宏中,是否应该更喜欢 do { ... } while(0,0) 而不是 do { ... } while(0)?

    c++ - 指针 vector 的内存管理

    c++ - Python中低级标准输入的重复重定向

    c++ - 在成员初始值设定项列表之前的构造函数中引发异常?

    c++ - 尝试使用 C++ 创建管道模式

    c - 尝试掌握 C 字节码...GNU/gcc 是否/可以像 Clang/LLVM 一样生成 C 字节码?

    Dockerfile 因 numba 安装的 llvm-config 错误而失败

    c++ - 如何为具有 bool 参数的位运算符启用 C++ 警告

    c - 有没有一种工具可以检查 C 文件依赖于哪些预定义的宏?