216 lines · cpp
1//==-- handle_llvm.cpp - Helper function for Clang fuzzers -----------------==//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Implements HandleLLVM for use by the Clang fuzzers. First runs a loop10// vectorizer optimization pass over the given IR code. Then mimics lli on both11// versions to JIT the generated code and execute it. Currently, functions are 12// executed on dummy inputs.13//14//===----------------------------------------------------------------------===//15 16#include "handle_llvm.h"17#include "input_arrays.h"18 19#include "llvm/Analysis/TargetLibraryInfo.h"20#include "llvm/Analysis/TargetTransformInfo.h"21#include "llvm/CodeGen/CommandFlags.h"22#include "llvm/CodeGen/MachineModuleInfo.h"23#include "llvm/CodeGen/TargetPassConfig.h"24#include "llvm/ExecutionEngine/JITEventListener.h"25#include "llvm/ExecutionEngine/JITSymbol.h"26#include "llvm/ExecutionEngine/MCJIT.h"27#include "llvm/ExecutionEngine/ObjectCache.h"28#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"29#include "llvm/ExecutionEngine/SectionMemoryManager.h"30#include "llvm/IR/IRPrintingPasses.h"31#include "llvm/IR/LLVMContext.h"32#include "llvm/IR/Module.h"33#include "llvm/IR/Verifier.h"34#include "llvm/IRPrinter/IRPrintingPasses.h"35#include "llvm/IRReader/IRReader.h"36#include "llvm/MC/TargetRegistry.h"37#include "llvm/Passes/OptimizationLevel.h"38#include "llvm/Passes/PassBuilder.h"39#include "llvm/Support/MemoryBuffer.h"40#include "llvm/Support/SourceMgr.h"41#include "llvm/Support/TargetSelect.h"42#include "llvm/Target/TargetMachine.h"43#include "llvm/TargetParser/Triple.h"44 45using namespace llvm;46 47// Define a type for the functions that are compiled and executed48typedef void (*LLVMFunc)(int*, int*, int*, int);49 50// Helper function to parse command line args and find the optimization level51static CodeGenOptLevel getOptLevel(const std::vector<const char *> &ExtraArgs) {52 // Find the optimization level from the command line args53 CodeGenOptLevel OLvl = CodeGenOptLevel::Default;54 for (auto &A : ExtraArgs) {55 if (A[0] == '-' && A[1] == 'O') {56 if (auto Level = CodeGenOpt::parseLevel(A[2])) {57 OLvl = *Level;58 } else {59 errs() << "error: opt level must be between 0 and 3.\n";60 std::exit(1);61 }62 }63 }64 return OLvl;65}66 67static void ErrorAndExit(std::string message) {68 errs()<< "ERROR: " << message << "\n";69 std::exit(1);70}71 72// Helper function to add optimization passes to the TargetMachine at the 73// specified optimization level, OptLevel74static void RunOptimizationPasses(raw_ostream &OS, Module &M,75 CodeGenOptLevel OptLevel) {76 llvm::OptimizationLevel OL;77 switch (OptLevel) {78 case CodeGenOptLevel::None:79 OL = OptimizationLevel::O0;80 break;81 case CodeGenOptLevel::Less:82 OL = OptimizationLevel::O1;83 break;84 case CodeGenOptLevel::Default:85 OL = OptimizationLevel::O2;86 break;87 case CodeGenOptLevel::Aggressive:88 OL = OptimizationLevel::O3;89 break;90 }91 92 LoopAnalysisManager LAM;93 FunctionAnalysisManager FAM;94 CGSCCAnalysisManager CGAM;95 ModuleAnalysisManager MAM;96 97 PassBuilder PB;98 99 PB.registerModuleAnalyses(MAM);100 PB.registerCGSCCAnalyses(CGAM);101 PB.registerFunctionAnalyses(FAM);102 PB.registerLoopAnalyses(LAM);103 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);104 105 ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OL);106 MPM.addPass(PrintModulePass(OS));107 108 MPM.run(M, MAM);109}110 111// Mimics the opt tool to run an optimization pass over the provided IR112static std::string OptLLVM(const std::string &IR, CodeGenOptLevel OLvl) {113 // Create a module that will run the optimization passes114 SMDiagnostic Err;115 LLVMContext Context;116 std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context);117 if (!M || verifyModule(*M, &errs()))118 ErrorAndExit("Could not parse IR");119 120 Triple ModuleTriple(M->getTargetTriple());121 const TargetOptions Options =122 codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple);123 std::string E;124 const Target *TheTarget =125 TargetRegistry::lookupTarget(codegen::getMArch(), ModuleTriple, E);126 if (!TheTarget)127 ErrorAndExit(E);128 129 std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine(130 M->getTargetTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(),131 Options, codegen::getExplicitRelocModel(),132 codegen::getExplicitCodeModel(), OLvl));133 if (!TM)134 ErrorAndExit("Could not create target machine");135 136 codegen::setFunctionAttributes(codegen::getCPUStr(),137 codegen::getFeaturesStr(), *M);138 139 // Add a pass that writes the optimized IR to an output stream140 std::string outString;141 raw_string_ostream OS(outString);142 RunOptimizationPasses(OS, *M, OLvl);143 144 return outString;145}146 147// Takes a function and runs it on a set of inputs148// First determines whether f is the optimized or unoptimized function149static void RunFuncOnInputs(LLVMFunc f, int Arr[kNumArrays][kArraySize]) {150 for (int i = 0; i < kNumArrays / 3; i++)151 f(Arr[i], Arr[i + (kNumArrays / 3)], Arr[i + (2 * kNumArrays / 3)],152 kArraySize);153}154 155// Takes a string of IR and compiles it using LLVM's JIT Engine156static void CreateAndRunJITFunc(const std::string &IR, CodeGenOptLevel OLvl) {157 SMDiagnostic Err;158 LLVMContext Context;159 std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context);160 if (!M)161 ErrorAndExit("Could not parse IR");162 163 Function *EntryFunc = M->getFunction("foo");164 if (!EntryFunc)165 ErrorAndExit("Function not found in module");166 167 std::string ErrorMsg;168 Triple ModuleTriple(M->getTargetTriple());169 170 EngineBuilder builder(std::move(M));171 builder.setMArch(codegen::getMArch());172 builder.setMCPU(codegen::getCPUStr());173 builder.setMAttrs(codegen::getFeatureList());174 builder.setErrorStr(&ErrorMsg);175 builder.setEngineKind(EngineKind::JIT);176 builder.setMCJITMemoryManager(std::make_unique<SectionMemoryManager>());177 builder.setOptLevel(OLvl);178 builder.setTargetOptions(179 codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple));180 181 std::unique_ptr<ExecutionEngine> EE(builder.create());182 if (!EE)183 ErrorAndExit("Could not create execution engine");184 185 EE->finalizeObject();186 EE->runStaticConstructorsDestructors(false);187 188 LLVMFunc f = reinterpret_cast<LLVMFunc>(EE->getPointerToFunction(EntryFunc));189 190 // Figure out if we are running the optimized func or the unoptimized func191 RunFuncOnInputs(f, (OLvl == CodeGenOptLevel::None) ? UnoptArrays : OptArrays);192 193 EE->runStaticConstructorsDestructors(true);194}195 196// Main fuzz target called by ExampleClangLLVMProtoFuzzer.cpp197// Mimics the lli tool to JIT the LLVM IR code and execute it198void clang_fuzzer::HandleLLVM(const std::string &IR,199 const std::vector<const char *> &ExtraArgs) {200 // Populate OptArrays and UnoptArrays with the arrays from InputArrays201 memcpy(OptArrays, InputArrays, kTotalSize);202 memcpy(UnoptArrays, InputArrays, kTotalSize);203 204 // Parse ExtraArgs to set the optimization level205 CodeGenOptLevel OLvl = getOptLevel(ExtraArgs);206 207 // First we optimize the IR by running a loop vectorizer pass208 std::string OptIR = OptLLVM(IR, OLvl);209 210 CreateAndRunJITFunc(OptIR, OLvl);211 CreateAndRunJITFunc(IR, CodeGenOptLevel::None);212 213 if (memcmp(OptArrays, UnoptArrays, kTotalSize))214 ErrorAndExit("!!!BUG!!!");215}216