// 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; }
llvm::Function* createLLVMFunction(Module& module, const ArgInfo& argInfo, llvm::GlobalValue::LinkageTypes linkage, const String& name) { const auto llvmFunction = llvm::Function::Create(argInfo.makeFunctionType(), linkage, name.c_str(), module.getLLVMModulePtr()); if (argInfo.noMemoryAccess()) { llvmFunction->setDoesNotAccessMemory(); } if (argInfo.noExcept()) { llvmFunction->setDoesNotThrow(); } if (argInfo.noReturn()) { llvmFunction->setDoesNotReturn(); } return llvmFunction; }
// Iterate over the list of external functions and insert them as // global functions. static void InitExternalCode(NativeModulePtr natMod, llvm::Module *M) { for (auto e : natMod->getExtCalls()) { auto conv = e->getCallingConvention(); auto argCount = e->getNumArgs(); auto symName = e->getSymbolName(); auto funcSign = e->getFunctionSignature(); // Create the function if it is not already there. auto &C = M->getContext(); auto F = M->getFunction(symName); if (F) { continue; } if (ExternalCodeRef::McsemaCall == conv) { // normal mcsema function prototypes F = llvm::dyn_cast<llvm::Function>(M->getOrInsertFunction( ArchNameMcSemaCall(symName), LiftedFunctionType())); ArchSetCallingConv(M, F); F->setLinkage(llvm::GlobalValue::ExternalLinkage); continue; } std::vector<llvm::Type *> arguments; llvm::Type *returnType = nullptr; // Create arguments. const auto Arch = SystemArch(M); const auto OS = SystemOS(M); for (auto i = 0; i < argCount; i++) { if (_X86_64_ == Arch) { if (llvm::Triple::Win32 == OS) { if (funcSign.c_str()[i] == 'F') { arguments.push_back(llvm::Type::getDoubleTy(C)); } else { arguments.push_back(llvm::Type::getInt64Ty(C)); } } else if (llvm::Triple::Linux == OS) { arguments.push_back(llvm::Type::getInt64Ty(C)); } else { TASSERT(false, "Unknown OS Type!"); } } else { arguments.push_back(llvm::Type::getInt32Ty(C)); } } //create function type switch (e->getReturnType()) { case ExternalCodeRef::NoReturn: case ExternalCodeRef::VoidTy: returnType = llvm::Type::getVoidTy(C); break; case ExternalCodeRef::Unknown: case ExternalCodeRef::IntTy: if (natMod->is64Bit()) { returnType = llvm::Type::getInt64Ty(C); } else { returnType = llvm::Type::getInt32Ty(C); } break; default: throw TErr( __LINE__, __FILE__, "Encountered an unknown return type while translating function"); } auto FTy = llvm::FunctionType::get(returnType, arguments, false); if (e->isWeak()) { F = llvm::Function::Create(FTy, llvm::GlobalValue::ExternalWeakLinkage, symName, M); } else { F = llvm::Function::Create(FTy, llvm::GlobalValue::ExternalLinkage, symName, M); } if (e->getReturnType() == ExternalCodeRef::NoReturn) { F->setDoesNotReturn(); } //set calling convention if (natMod->is64Bit()) { ArchSetCallingConv(M, F); } else { F->setCallingConv(getLLVMCC(conv)); } } }
// 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; }