/// \file AnalyzeHelperArguments.cpp // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/DenseMap.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/Casting.h" #include "AnnotationWriter.h" #include "ArgumentUsageAnalysis.h" #include "CPUStateUsage.h" #include "Context.h" #include "Function.h" #include "Value.h" #include "llvm-c/Types.h" using namespace llvm; static cl::opt ModuleDumpPath("analyze-helper-arguments-output", cl::desc("path where to save the " "module annotated with the " "results of the Analyze " "Helper Arguments Analysis" "results"), cl::init("")); // TODO: use ConstantExpr to perform the constant folding instead of inventing // our own arithmetic framework static aua::StructPointers collectOffsetTypes(Module &Module, StructType &ArchCPU) { LLVMContext &Context = Module.getContext(); aua::StructPointers Result(Module, ArchCPU); for (Function &F : Module) { for (Instruction &I : llvm::instructions(F)) { auto *Dbg = dyn_cast(&I); if (Dbg == nullptr) continue; auto *MAV0 = dyn_cast(Dbg->getArgOperand(0)); auto *MAV1 = cast(Dbg->getArgOperand(1))->getMetadata(); if (MAV0 == nullptr or MAV1 == nullptr or not isa(MAV0->getMetadata()) or not isa(MAV1)) continue; auto *Value = cast(MAV0->getMetadata())->getValue(); // TODO: go beyond arguments? auto *Argument = dyn_cast_or_null(Value); auto *Type = cast(MAV1)->getType(); if (Argument == nullptr or Type == nullptr) continue; revng_log(Log, "Considering argument " << Argument->getArgNo() << " of " << F.getName().str()); LoggerIndent Indent(Log); auto *DIArgumentType = dyn_cast_or_null(Type); if (DIArgumentType == nullptr or DIArgumentType->getTag() != dwarf::DW_TAG_pointer_type) { revng_log(Log, "Not a pointer"); continue; } auto *BaseType = DIArgumentType->getBaseType(); // Skip typedefs auto *DerivedType = dyn_cast_or_null(BaseType); while (DerivedType != nullptr and DerivedType->getTag() == llvm::dwarf::DW_TAG_typedef) { BaseType = DerivedType->getBaseType(); DerivedType = dyn_cast_or_null(BaseType); } if (BaseType == nullptr or BaseType->getRawName() == nullptr) { revng_log(Log, "Pointee has no name"); continue; } for (const char *Prefix : std::vector{ "struct.", "union." }) { auto *Struct = StructType::getTypeByName(Context, (Prefix + BaseType->getName()) .str()); if (Struct != nullptr) { revng_log(Log, "Registering as a pointer to " << Struct->getName()); Result.registerPointer(*Value, *Struct); } } } } Result.propagateFromActualArguments(); if (Log.isEnabled()) { Result.dump(Log); Log << DoLog; } return Result; } class AnalyzeHelperArguments : public ModulePass { public: static char ID; public: AnalyzeHelperArguments() : ModulePass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); } bool runOnModule(Module &M) override { Task T(6, "Analyze helper arguments"); T.advance("Prepare module"); LLVMContext &LLVMContext = M.getContext(); auto &DL = M.getDataLayout(); // NOTE: patch these things on the QEMU side wherever possible! std::array NoOpFunctionNames = { // Ignore debugging primitives "printf", // Ignore assertion-like "g_assertion_message_expr", }; std::vector NoOpFunctions; for (auto &&Name : NoOpFunctionNames) if (Function *F = M.getFunction(Name)) NoOpFunctions.push_back(F); // NOTE: patch these things on the QEMU side wherever possible! std::array AbortFunctionNames = { // glib features we don't support "g_hash_table_lookup", "g_string_free", }; std::vector AbortFunctions; for (auto &&Name : AbortFunctionNames) if (Function *F = M.getFunction(Name)) AbortFunctions.push_back(F); for (Function &F : M) { if (F.getSection() == "revng_noop") { NoOpFunctions.push_back(&F); } else if (F.getSection() == "revng_abort") { AbortFunctions.push_back(&F); } } for (Function *F : NoOpFunctions) { auto *ReturnType = F->getFunctionType()->getReturnType(); revng_assert(ReturnType->isVoidTy() or ReturnType->isPointerTy() or ReturnType->isIntegerTy()); // Purge the function body { for (BasicBlock &BB : *F) BB.dropAllReferences(); while (not F->empty()) F->begin()->eraseFromParent(); } auto *Block = BasicBlock::Create(LLVMContext, "", F); if (ReturnType->isVoidTy()) { ReturnInst::Create(LLVMContext, Block); } else if (ReturnType->isPointerTy()) { auto *Null = ConstantPointerNull::get(cast(ReturnType)); ReturnInst::Create(LLVMContext, Null, Block); } else { auto *Zero = ConstantInt::get(ReturnType, 0); ReturnInst::Create(LLVMContext, Zero, Block); } } for (Function *F : AbortFunctions) { { for (BasicBlock &BB : *F) BB.dropAllReferences(); while (not F->empty()) F->begin()->eraseFromParent(); } F->setDoesNotReturn(); auto *Block = BasicBlock::Create(LLVMContext, "", F); new UnreachableInst(LLVMContext, Block); } // Remove noreturn from cpu_loop_exit and its call sites for (auto &F : M) { if (F.getName().starts_with("cpu_loop_exit")) { F.removeFnAttr(Attribute::NoReturn); for (CallBase *Call : callers(&F)) Call->removeFnAttr(Attribute::NoReturn); } } aua::Context Context; T.advance("Run argument usage analysis"); aua::ArgumentUsageAnalysis AUA(Context, M); AUA.run(); T.advance("Collect offsets types"); auto *CPUStruct = StructType::getTypeByName(LLVMContext, "struct.ArchCPU"); aua::StructPointers OffsetTypes = collectOffsetTypes(M, *CPUStruct); T.advance("Analyzing helpers"); aua::CPUStateUsageAnalysis CSUA(Context, AUA, DL, *CPUStruct, std::move(OffsetTypes)); SmallVector HelperDefinitions; for (Function &F : M) if (not F.isDeclaration() and F.getName().starts_with("helper_")) HelperDefinitions.push_back(&F); { Task T(HelperDefinitions.size(), "Analyzing helpers"); for (Function *F : HelperDefinitions) { T.advance(F->getName()); revng_assert(not F->isVarArg()); CSUA.analyze(*F); } } T.advance("Adding annotations"); CSUA.annotate(M); T.advance("Dumping module"); if (ModuleDumpPath.getNumOccurrences() != 0) { aua::AnnotationWriter Annotator = aua::AnnotationWriter(AUA, CSUA); std::error_code EC; raw_fd_ostream Stream(ModuleDumpPath.getValue(), EC); revng_assert(not EC); M.print(Stream, &Annotator); } return false; } }; char AnalyzeHelperArguments::ID = 0; using Register = RegisterPass; static Register X("analyze-helper-arguments", "Analyze usage of arguments of helper functions");