static void CreateInstrBreakpoint(llvm::BasicBlock *B, VA pc) { auto F = B->getParent(); auto M = F->getParent(); std::stringstream ss; ss << "breakpoint_" << std::hex << pc; auto instr_func_name = ss.str(); auto IFT = M->getFunction(instr_func_name); if (!IFT) { IFT = llvm::Function::Create( LiftedFunctionType(), llvm::GlobalValue::ExternalLinkage, instr_func_name, M); IFT->addFnAttr(llvm::Attribute::OptimizeNone); IFT->addFnAttr(llvm::Attribute::NoInline); auto &C = M->getContext(); llvm::IRBuilder<> ir(llvm::BasicBlock::Create(C, "", IFT)); ir.CreateRetVoid(); } auto state_ptr = &*F->arg_begin(); llvm::CallInst::Create(IFT, {state_ptr}, "", B); }
// Add a function that can be used to transition from native code into lifted // code. // isCallback defaults to false llvm::Function *ArchAddEntryPointDriver(llvm::Module *M, const std::string &name, VA entry, bool isCallback) { //convert the VA into a string name of a function, try and look it up std::stringstream ss; ss << "sub_" << std::hex << entry; auto s = ss.str(); llvm::Function *F = M->getFunction(s); if (!F) { llvm::errs() << "Could not find lifted function " << s << " for entry point " << name; return nullptr; } auto &C = F->getContext(); auto W = M->getFunction(name); if (W) { return W; } auto VoidTy = llvm::Type::getVoidTy(C); auto WTy = llvm::FunctionType::get(VoidTy, false); W = llvm::Function::Create( WTy, llvm::GlobalValue::ExternalLinkage, name, M); W->addFnAttr(llvm::Attribute::NoInline); W->addFnAttr(llvm::Attribute::Naked); const auto Arch = SystemArch(M); const auto OS = SystemOS(M); if (llvm::Triple::Linux == OS) { if (_X86_64_ == Arch) { LinuxAddPushJumpStub(M, F, W, "__mcsema_attach_call"); } else { LinuxAddPushJumpStub(M, F, W, "__mcsema_attach_call_cdecl"); } } else if (llvm::Triple::Win32 == OS) { // if we are creating and entry point for a callback // then we need to decorate the function. // if we are creating an entry point specified via -entrypoint // then the name is pre-decorated, and we don't decorate twice if (_X86_64_ == Arch) { WindowsAddPushJumpStub(isCallback, M, F, W, "__mcsema_attach_call"); } else { WindowsAddPushJumpStub(isCallback, M, F, W, "__mcsema_attach_call_cdecl"); } } else { TASSERT(false, "Unsupported OS for entry point driver."); } F->setLinkage(llvm::GlobalValue::ExternalLinkage); if (F->doesNotReturn()) { W->setDoesNotReturn(); } return W; }
static void InitADFeatures(llvm::Module *M, const char *name, llvm::FunctionType *EPTy) { auto FC = M->getOrInsertFunction(name, EPTy); auto F = llvm::dyn_cast<llvm::Function>(FC); F->setLinkage(llvm::GlobalValue::ExternalLinkage); F->addFnAttr(llvm::Attribute::Naked); }
llvm::Function* createTranslationStubFunction(Module& module, llvm::Function* function, const ArgInfo& translatedArgInfo) { const auto functionName = module.getCString("translateStub_") + function->getName(); const auto linkage = llvm::Function::InternalLinkage; const auto llvmFunction = createLLVMFunction(module, translatedArgInfo, linkage, functionName); // Always inline if possible. llvmFunction->addFnAttr(llvm::Attribute::AlwaysInline); return llvmFunction; }
static bool InsertFunctionIntoModule(NativeModulePtr mod, NativeFunctionPtr func, llvm::Module *M) { auto &C = M->getContext(); auto F = M->getFunction(func->get_name()); if (!F) { throw TErr(__LINE__, __FILE__, "Could not get func " + func->get_name()); } if (!F->empty()) { std::cout << "WARNING: Asking to re-insert function: " << func->get_name() << std::endl << "\tReturning current function instead" << std::endl; return true; } auto entryBlock = llvm::BasicBlock::Create(F->getContext(), "entry", F); ArchAllocRegisterVars(entryBlock); TranslationContext ctx; ctx.natM = mod; ctx.natF = func; ctx.natI = nullptr; ctx.M = M; ctx.F = F; // Create basic blocks for each basic block in the original function. for (auto block_info : func->get_blocks()) { ctx.va_to_bb[block_info.first] = llvm::BasicBlock::Create( C, block_info.second->get_name(), F); } // Create a branch from the end of the entry block to the first block llvm::BranchInst::Create(ctx.va_to_bb[func->get_start()], entryBlock); try { // Lift every basic block into the functions. auto error = false; for (auto block_info : func->get_blocks()) { ctx.natB = block_info.second; error = LiftBlockIntoFunction(ctx) || error; ctx.natB = nullptr; } // For ease of debugging generated code, don't allow lifted functions to // be inlined. This will make lifted and native call graphs one-to-one. F->addFnAttr(llvm::Attribute::NoInline); //we should be done, having inserted every block into the module return !error; } catch (std::exception &e) { std::cerr << "error: " << std::endl << e.what() << std::endl << "in function " << std::hex << ctx.natF->get_start() << std::endl; if (ctx.natB) { std::cerr << "in block " << std::hex << ctx.natB->get_base() << std::endl; if (ctx.natI) { std::cerr << "in inst " << std::hex << ctx.natI->get_loc() << std::endl; } } return false; } }
// Wrap `F` in a function that will transition from lifted code into native // code, where `F` is an external reference to a native function. llvm::Function *ArchAddExitPointDriver(llvm::Function *F) { std::stringstream ss; auto M = F->getParent(); const auto OS = SystemOS(M); if(llvm::Triple::Win32 == OS) { ss << "mcsema_" << F->getName().str(); } else { ss << "_" << F->getName().str(); } auto &C = M->getContext(); auto name = ss.str(); auto W = M->getFunction(name); if (W) { return W; } W = llvm::Function::Create(F->getFunctionType(), F->getLinkage(), name, M); W->setCallingConv(F->getCallingConv()); W->addFnAttr(llvm::Attribute::NoInline); W->addFnAttr(llvm::Attribute::Naked); const auto Arch = SystemArch(M); if (llvm::Triple::Linux == OS) { if (_X86_64_ == Arch) { LinuxAddPushJumpStub(M, F, W, "__mcsema_detach_call"); } else { switch (F->getCallingConv()) { case llvm::CallingConv::C: LinuxAddPushJumpStub(M, F, W, "__mcsema_detach_call_cdecl"); break; case llvm::CallingConv::X86_StdCall: LinuxAddPushJumpStub(M, F, W, "__mcsema_detach_call_stdcall"); break; case llvm::CallingConv::X86_FastCall: LinuxAddPushJumpStub(M, F, W, "__mcsema_detach_call_fastcall"); break; default: TASSERT(false, "Unsupported Calling Convention for 32-bit Linux"); break; } } } else if (llvm::Triple::Win32 == OS) { if (_X86_64_ == Arch) { WindowsAddPushJumpStub(true, M, F, W, "__mcsema_detach_call"); } else { switch (F->getCallingConv()) { case llvm::CallingConv::C: WindowsAddPushJumpStub(true, M, F, W, "__mcsema_detach_call_cdecl"); break; case llvm::CallingConv::X86_StdCall: WindowsAddPushJumpStub(true, M, F, W, "__mcsema_detach_call_stdcall"); break; case llvm::CallingConv::X86_FastCall: WindowsAddPushJumpStub(true, M, F, W, "__mcsema_detach_call_fastcall"); break; default: TASSERT(false, "Unsupported Calling Convention for 32-bit Windows"); break; } } } else { TASSERT(false, "Unsupported OS for exit point driver."); } if (F->doesNotReturn()) { W->setDoesNotReturn(); } return W; }