菜鸟入门,如有任何不当之处,烦请各位路过的大佬指正,不胜感激!(顺便吐槽一句llvm的官方文档对于一些基本信息的分布真的很不合理诶!)
总述
该文件完成的主要内容就是编写一个能在基本块前插入相关计算代码的pass。所以这里只是实现将计算代码插入到相应的位置的功能,并没有实质上完成边的编号计算。只有在实际运行中,程序运行到改变的到达块时,才开始计算该边的编号。
详细剖析
首先在匿名的命名空间中,定义一个继承自ModulePass的AFLCoverage类。而对于ModulePass的理解我们可以参考官网对其的介绍:
The ModulePass class is the most general of all superclasses that you can use. Deriving from ModulePass indicates that your pass uses the entire program as a unit, referring to function bodies in no predictable order, or adding and removing functions.
因此选择继承不同类型的Pass父类就是从不同的粒度对程序进行处理。这里选择继承ModulePass就是对整个程序都进行处理。
namespace {
class AFLCoverage : public ModulePass {
然后在类外对类中的静态成员变量ID进行定义及初始化。该变量是pass标识符,即LLVM用于标识pass的。同时,在类外定义runOnModule()函数,该函数内容便是实现真正需求的部分。
char AFLCoverage::ID = 0; bool AFLCoverage::runOnModule(Module &M) {
然后获取LLVMContext。关于LLVMContext:
This is an important class for using LLVM in a threaded context. It (opaquely) owns and manages the core “global” data of LLVM’s core infrastructure, including the type and constant uniquing tables.
LLVMContext itself provides no locking guarantees, so you should be careful to have one context per thread.
LLVMContext &C = M.getContext();
然后获取插桩率来决定插桩密度。
char* inst_ratio_str = getenv("AFL_INST_RATIO");
unsigned int inst_ratio = 100; if (inst_ratio_str) {
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || inst_ratio > 100)FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)"); }
定义两个全局变量。AFLMapPtr是一个用来指向共享内存映射到进程空间的地址;AFLPreLoc是一个用来表示前一个基本块编号的值。
关于GlobalVariable的构造函数官方介绍如下:
GlobalVariable(const Type *Ty, bool isConstant, LinkageTypes &Linkage, Constant Initializer = 0, const std::string &Name = “”, Module Parent = 0)
Create a new global variable of the specified type. If isConstant is true then the global variable will be marked as unchanging for the program. The Linkage parameter specifies the type of linkage (internal, external, weak, linkonce, appending) for the variable. If the linkage is InternalLinkage, WeakAnyLinkage, WeakODRLinkage, LinkOnceAnyLinkage or LinkOnceODRLinkage, then the resultant global variable will have internal linkage. AppendingLinkage concatenates together all instances (in different translation units) of the variable into a single variable but is only applicable to arrays. See the LLVM Language Reference for further details on linkage types. Optionally an initializer, a name, and the module to put the variable into may be specified for the global variable as well.
GlobalVariable *AFLMapPtr =new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");GlobalVariable *AFLPrevLoc = new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
对Module中(其实就是对整个程序)的每个函数中的每个基本块获取其第一条指令的迭代器。
for (auto &F : M)for (auto &BB : F) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
使用上面获取的迭代器创建一个IRBuilder类的示例,通过该实例就能很方便地创建一些指令,并插在插入点。官方说明如下:
IRBuilder is used as a convenient way to create LLVM instruvtions with a consistent and simplified interface. And then insert them into a basic block.
IRBuilder<> IRB(&(*IP));
给当前的基本块赋予一个随机值。AFL_R是一个宏,表示random()%X。这里就是取0~MAP_SIZE之间的一个值作为当前基本块的编号。
unsigned int cur_loc = AFL_R(MAP_SIZE);
获取前驱基本块的编号并转换为相应类型。在LLVM中,无论是全局变量还是局部变量都是指针类型的,所以需要使用CreatedLoad()方法来获取值,CreatedStore()方法来赋值,Load和Store就是一对访问内存的指令。然后使用CreateZExt来完成相应的类型转换。Value类是其他众多重要类的超类,如Instruction和Function。
LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc);
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
获取指向共享内存的指针的值。
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
计算当前边的编号并得到一个指向其在共享内存中相应位置的指针。CreateGEP是CreateGetElementPtr。
Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
用CreateLoad()函数将MapPtrIdx地址处的值取出。
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
将Counter值加一后,赋值给MapPtrIdx。
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));IRB.CreateStore(Incr, MapPtrIdx)->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
将当前基本块的编号右移1位并赋值给AFLPreLoc
StoreInst *Store =IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
插桩完成后,向LLVM编译过程中添加AFL所需的Pass模块,也就是说把该Pass注册为现有管道中的一步。
static void registerAFLPass(const PassManagerBuilder &,legacy::PassManagerBase &PM) {
PM.add(new AFLCoverage());}