Index: docs/BitCodeFormat.rst =================================================================== --- docs/BitCodeFormat.rst +++ docs/BitCodeFormat.rst @@ -1057,6 +1057,7 @@ * code 56: ``nocf_check`` * code 57: ``optforfuzzing`` * code 58: ``shadowcallstack`` +* code 59: ``pagerando`` .. note:: The ``allocsize`` attribute has a special encoding for its arguments. Its two Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1609,6 +1609,10 @@ If an argmemonly function reads or writes memory other than the pointer arguments, or has other side-effects, the behavior is undefined. +``pagerando`` + This attribute indicates that the function is compatible with pagerando and + will be located in a section which the dynamic loader can place at an + independent offset from any other section in the binary. ``returns_twice`` This attribute indicates that this function can return twice. The C ``setjmp`` is an example of such a function. The compiler disables Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -592,6 +592,7 @@ ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_SHADOWCALLSTACK = 58, ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, + ATTR_KIND_PAGERANDO = 60, }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.td =================================================================== --- include/llvm/IR/Attributes.td +++ include/llvm/IR/Attributes.td @@ -199,6 +199,9 @@ /// Zero extended before/after call. def ZExt : EnumAttr<"zeroext">; +/// Function is placed into a Pagerando bin. +def Pagerando : EnumAttr<"pagerando">; + /// Target-independent string attributes. def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">; def NoInfsFPMath : StrBoolAttr<"no-infs-fp-math">; Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -581,6 +581,12 @@ AttributeSets.hasParamAttribute(1, Attribute::StructRet); } + /// True if the function is compatible with Pagerando and should be placed + /// into a randomized bin. + bool isPagerando() const { + return hasFnAttribute(Attribute::Pagerando); + } + /// Determine if the parameter or return value is marked with NoAlias /// attribute. bool returnDoesNotAlias() const { Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -290,6 +290,7 @@ void initializeOptimizationRemarkEmitterWrapperPassPass(PassRegistry&); void initializeOptimizePHIsPass(PassRegistry&); void initializePAEvalPass(PassRegistry&); +void initializePagerandoWrappersPass(PassRegistry&); void initializePEIPass(PassRegistry&); void initializePGOIndirectCallPromotionLegacyPassPass(PassRegistry&); void initializePGOInstrumentationGenLegacyPassPass(PassRegistry&); Index: include/llvm/LinkAllPasses.h =================================================================== --- include/llvm/LinkAllPasses.h +++ include/llvm/LinkAllPasses.h @@ -148,6 +148,7 @@ (void) llvm::createObjCARCContractPass(); (void) llvm::createObjCARCOptPass(); (void) llvm::createPAEvalPass(); + (void) llvm::createPagerandoWrappersPass(); (void) llvm::createPromoteMemoryToRegisterPass(); (void) llvm::createDemoteRegisterToMemoryPass(); (void) llvm::createPruneEHPass(); Index: include/llvm/Transforms/IPO.h =================================================================== --- include/llvm/Transforms/IPO.h +++ include/llvm/Transforms/IPO.h @@ -271,6 +271,10 @@ ModulePass *createWriteThinLTOBitcodePass(raw_ostream &Str, raw_ostream *ThinLinkOS = nullptr); +/// \brief This pass creates Pagerando entry wrappers that properly set up the +/// POT base register. +ModulePass *createPagerandoWrappersPass(); + } // End llvm namespace #endif Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -685,6 +685,7 @@ KEYWORD(uwtable); KEYWORD(writeonly); KEYWORD(zeroext); + KEYWORD(pagerando); KEYWORD(type); KEYWORD(opaque); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1282,6 +1282,8 @@ case lltok::kw_strictfp: B.addAttribute(Attribute::StrictFP); break; case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break; case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break; + case lltok::kw_pagerando: + B.addAttribute(Attribute::Pagerando); break; // Error handling. case lltok::kw_inreg: Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -227,6 +227,7 @@ kw_uwtable, kw_writeonly, kw_zeroext, + kw_pagerando, kw_type, kw_opaque, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1167,6 +1167,7 @@ case Attribute::ShadowCallStack: return 1ULL << 59; case Attribute::SpeculativeLoadHardening: return 1ULL << 60; + case Attribute::Pagerando: return 1ULL << 61; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; @@ -1403,6 +1404,8 @@ return Attribute::WriteOnly; case bitc::ATTR_KIND_Z_EXT: return Attribute::ZExt; + case bitc::ATTR_KIND_PAGERANDO: + return Attribute::Pagerando; } } Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -702,6 +702,8 @@ return bitc::ATTR_KIND_WRITEONLY; case Attribute::ZExt: return bitc::ATTR_KIND_Z_EXT; + case Attribute::Pagerando: + return bitc::ATTR_KIND_PAGERANDO; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -351,6 +351,8 @@ return "zeroext"; if (hasAttribute(Attribute::Cold)) return "cold"; + if (hasAttribute(Attribute::Pagerando)) + return "pagerando"; // FIXME: These should be output like this: // Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1481,6 +1481,7 @@ case Attribute::SpeculativeLoadHardening: case Attribute::Speculatable: case Attribute::StrictFP: + case Attribute::Pagerando: return true; default: break; Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -277,6 +277,8 @@ PMB.populateThinLTOPassManager(passes); else PMB.populateLTOPassManager(passes); + if (TM->isPagerando()) + passes.add(createPagerandoWrappersPass()); passes.run(Mod); } Index: lib/Transforms/IPO/CMakeLists.txt =================================================================== --- lib/Transforms/IPO/CMakeLists.txt +++ lib/Transforms/IPO/CMakeLists.txt @@ -27,6 +27,7 @@ MergeFunctions.cpp PartialInlining.cpp PassManagerBuilder.cpp + PagerandoWrappers.cpp PruneEH.cpp SampleProfile.cpp SCCP.cpp Index: lib/Transforms/IPO/IPO.cpp =================================================================== --- lib/Transforms/IPO/IPO.cpp +++ lib/Transforms/IPO/IPO.cpp @@ -45,6 +45,7 @@ initializeSingleLoopExtractorPass(Registry); initializeLowerTypeTestsPass(Registry); initializeMergeFunctionsPass(Registry); + initializePagerandoWrappersPass(Registry); initializePartialInlinerLegacyPassPass(Registry); initializePostOrderFunctionAttrsLegacyPassPass(Registry); initializeReversePostOrderFunctionAttrsLegacyPassPass(Registry); Index: lib/Transforms/IPO/PagerandoWrappers.cpp =================================================================== --- /dev/null +++ lib/Transforms/IPO/PagerandoWrappers.cpp @@ -0,0 +1,473 @@ +//===-- PagerandoWrappers.cpp - Pagerando entry wrappers ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass creates wrappers for pagerando-enabled functions. A function needs +// a wrapper if it has non-local linkage or its address taken, i.e., if it can +// be used from outside the module. (As an optimization we could use pointer +// escape analysis for address-taken functions instead of creating wrappers for +// all of them.) +// Vararg functions require special treatment: their variable arguments on the +// stack need to be preserved even when indirecting through the POT. We replace +// the original function with a new function that takes an explicit 'va_list' +// parameter: foo(int, ...) -> foo$$origva(int, *va_list). The wrapper captures +// its variable arguments and explicitly passes it to the adapted function to +// preserve the variable arguments passed by the caller. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallSet.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +#define DEBUG_TYPE "pagerando" + +// SkipTrivialWrappers - Do not wrap trivial functions that are only a call to +// another function and a return. +static cl::opt SkipTrivialWrappers( + "pagerando-skip-trivial", + cl::desc("Do not apply pagerando to wrapper functions consisting of only a single call."), + cl::Hidden, cl::init(false)); + +namespace { +class PagerandoWrappers : public ModulePass { +public: + static char ID; + explicit PagerandoWrappers() : ModulePass(ID) { + initializePagerandoWrappersPass(*PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + // Requires nothing, preserves nothing + ModulePass::getAnalysisUsage(AU); + } + +private: + static constexpr const char *OrigSuffix = "$$orig"; + static constexpr const char *OrigVASuffix = "$$origva"; + + void processFunction(Function *F); + Function *rewriteVarargs(Function &F, Type *&VAListTy); + Function *createWrapper(Function &F, const SmallVectorImpl &AddressUses); + void createWrapperBody(Function *Wrapper, Function *Callee, Type *VAListTy); +}; +} // end anonymous namespace + +char PagerandoWrappers::ID = 0; +INITIALIZE_PASS(PagerandoWrappers, "pagerando-wrappers", + "Pagerando entry wrappers", false, false) + +ModulePass *llvm::createPagerandoWrappersPass() { + return new PagerandoWrappers(); +} + +// We can safely skip functions consisting of only debug, trap, and unreachable +// instructions. Such functions are created for abstract, non-base +// destructors. We do not need to randomize these functions since they are +// trivial and not useful for an attacker to reuse. +// +// We may want to skip functions that consist of only a single call and a +// return. Wrapping these functions for pagerando introduces a proportionally +// larger overhead than for functions with non-trivial bodies. Reusing the +// content of such a function is equivalent to reusing the whole function, since +// the content is only a single function call (modulo any move operations to get +// arguments into the right order). +static bool isTrivialFunction(const Function &F, bool singleCallTrivial = false) { + bool sawCall = false; + for (auto &I : F.getEntryBlock()) { + if (isa(&I)) + continue; + if (auto *Int = dyn_cast(&I)) + if (Int->getIntrinsicID() == Intrinsic::trap) + continue; + if (isa(&I)) + continue; + if (singleCallTrivial) { + auto *CI = dyn_cast(&I); + // We cannot call a binned function via a tail-call if we need to load the + // POT register, since the POT register is callee-saved and must be + // restored after the call. + if (!sawCall && CI && !CI->isMustTailCall()) { + sawCall = true; + continue; + } + if (isa(&I)) + continue; + } + + // We found an instruction that is not debug, trap, or unreachable. + return false; + } + + // We only found debug, trap, or unreachable instructions. + return true; +} + +// We skip functions that are only declarations, comdat, trivial trap functions, +// and naked functions. Skipping naked functions is important so that CFI jump +// tables are not placed in pagerando sections. CFI jump tables are marked as +// naked in LowerTypeTests::createJumpTable. If this ever changes, this function +// will also need to be updated. +// +// TODO: Support COMDAT +static bool skipFunction(const Function &F) { + return F.isDeclaration() + || F.hasAvailableExternallyLinkage() + || F.hasComdat() + || isTrivialFunction(F, SkipTrivialWrappers) + || F.hasFnAttribute(Attribute::Naked) + || F.hasFnAttribute("thunk"); +} + +bool PagerandoWrappers::runOnModule(Module &M) { + std::vector Worklist; + for (auto &F : M) { + if (!F.hasFnAttribute(Attribute::Pagerando)) + continue; + if (skipFunction(F)) { + F.removeFnAttr(Attribute::Pagerando); + continue; + } + Worklist.push_back(&F); + } + + for (auto F : Worklist) + processFunction(F); + + return !Worklist.empty(); +} + +static bool skipFunctionUse(const Use &U); +static bool IsDirectCallOfBitcast(User *Usr) { + auto CE = dyn_cast(Usr); + return CE && CE->getOpcode() == Instruction::BitCast + && std::all_of(CE->use_begin(), CE->use_end(), skipFunctionUse); +} + +static bool skipFunctionUse(const Use &U) { + auto User = U.getUser(); + auto UserFn = dyn_cast(User); + ImmutableCallSite CS(User); + + return (CS && CS.isCallee(&U)) // Used as the callee + || isa(User) // Handled in AsmPrinter::EmitBasicBlockStart + || (UserFn && UserFn->getPersonalityFn() == U.get()) // Skip pers. fn uses + || IsDirectCallOfBitcast(User); // Calls to bitcasted functions end up as direct calls +} + +void PagerandoWrappers::processFunction(Function *F) { + SmallVector AddressUses; + for (Use &U : F->uses()) { + if (!skipFunctionUse(U)) + AddressUses.push_back(&U); + } + + if (!F->hasLocalLinkage() || !AddressUses.empty()) { + auto Wrapper = createWrapper(*F, AddressUses); + Type *VAListTy = nullptr; + if (F->isVarArg()) { + // Reassign F, it might have been deleted + F = rewriteVarargs(*F, /* out */ VAListTy); + } + createWrapperBody(Wrapper, F, VAListTy); + } + + F->setSection(""); +} + +// Replace F with Wrapper when applicable according to the following rules: +// - Calls to vararg functions must always go through the wrapper to ensure that +// we preserve the arguments on the stack when we indirect through the POT. +// - Calls to a non-local, non-protected function must go through the wrapper +// since they could be redirected by the dynamic linker (i.e, LD_PRELOAD). +// - Calls to protected visibility functions do not need to go through a wrapper +// since protected functions cannot be preempted at load time. +// - Address-taken uses of local functions might escape, so we must replace +// these addresses with the address of a wrapper. +static void replaceWithWrapper(Function &F, Function *Wrapper, + const SmallVectorImpl &AddressUses) { + if (F.isVarArg() || (!F.hasLocalLinkage() && !F.hasProtectedVisibility())) { + F.replaceAllUsesWith(Wrapper); + if (!F.hasLocalLinkage()) + F.setVisibility(GlobalValue::HiddenVisibility); + } else { + // Replace all address-taken uses so we don't leak a binned address to + // another DSO. + SmallSet Constants; + for (auto U : AddressUses) { + // Already replaced this use? + if (!U->get()) + continue; + + if (auto GV = dyn_cast(U->getUser())) { + assert(GV->getInitializer() == &F); + GV->setInitializer(Wrapper); + } else if (auto C = dyn_cast(U->getUser())) { + // Constant::handleOperandChange must not be called more than once per user + if (Constants.insert(C).second) { + // Aliases cannot handle operand change, so we need to change the + // aliasee directly. + if (auto *GA = dyn_cast(C)) { + assert(GA->getAliasee() == &F); + GA->setAliasee(Wrapper); + } else { + C->handleOperandChange(&F, Wrapper); + } + } + } else { + U->set(Wrapper); + } + } + } +} + +Function *PagerandoWrappers::createWrapper(Function &F, + const SmallVectorImpl &AddressUses) { + std::string OriginalName = F.getName(); + F.setName(Twine(OriginalName, F.isVarArg() ? OrigVASuffix : OrigSuffix)); + + // We add the function to the module explicitly because we want to control + // ordering. The new wrapper should go at the beginning of the module to + // ensure that at least one function in the normal text section will be + // emitted first. Without this, the linker may try to place a bin at the + // beginning of the file instead of the normal text section. + auto Wrapper = Function::Create(F.getFunctionType(), F.getLinkage(), OriginalName); + F.getParent()->getFunctionList().push_front(Wrapper); + Wrapper->setComdat(F.getComdat()); + + // Copy all attributes, then reset and explicitly remove blacklisted + // attributes in various categories. This saves us from having to maintain all + // explicit parameters here and in copyAttributesFrom. + Wrapper->copyAttributesFrom(&F); + + // Wrappers cannot throw, so we don't need a personality function + Wrapper->setPersonalityFn(nullptr); + + // Blacklist target independent attributes that should not be inherited + for (const auto &Attr : F.getAttributes().getFnAttributes()) { + if (Attr.isStringAttribute()) + continue; + + switch (Attr.getKindAsEnum()) { + // These attributes cannot be propagated safely. Explicitly list them here + // so we get a warning if new attributes are added. This list also includes + // non-function attributes. + case Attribute::Alignment: + case Attribute::AlwaysInline: + case Attribute::ArgMemOnly: + case Attribute::Builtin: + case Attribute::ByVal: + case Attribute::Dereferenceable: + case Attribute::DereferenceableOrNull: + case Attribute::InAlloca: + case Attribute::InReg: + case Attribute::InlineHint: + case Attribute::MinSize: + case Attribute::Naked: + case Attribute::Nest: + case Attribute::NoAlias: + case Attribute::NoCapture: + case Attribute::NoInline: + case Attribute::NoRedZone: + case Attribute::NonNull: + case Attribute::None: + case Attribute::OptForFuzzing: + case Attribute::OptimizeForSize: + case Attribute::OptimizeNone: + case Attribute::Pagerando: + case Attribute::ReadNone: + case Attribute::Returned: + case Attribute::ReturnsTwice: + case Attribute::SExt: + case Attribute::StructRet: + case Attribute::SwiftError: + case Attribute::SwiftSelf: + case Attribute::WriteOnly: + case Attribute::ZExt: + case Attribute::EndAttrKinds: + break; + // These attributes should be safe to propagate to the wrapper function + case Attribute::AllocSize: + case Attribute::Cold: + case Attribute::Convergent: + case Attribute::InaccessibleMemOnly: + case Attribute::InaccessibleMemOrArgMemOnly: + case Attribute::JumpTable: + case Attribute::NoBuiltin: + case Attribute::NoCfCheck: + case Attribute::NoDuplicate: + case Attribute::NoImplicitFloat: + case Attribute::NoRecurse: + case Attribute::NoReturn: + case Attribute::NoUnwind: + case Attribute::NonLazyBind: + case Attribute::ReadOnly: + case Attribute::SafeStack: + case Attribute::SanitizeAddress: + case Attribute::SanitizeHWAddress: + case Attribute::SanitizeMemory: + case Attribute::SanitizeThread: + case Attribute::ShadowCallStack: + case Attribute::Speculatable: + case Attribute::StackAlignment: + case Attribute::StackProtectReq: + case Attribute::StrictFP: + case Attribute::UWTable: + continue; + // These attributes should be propagated iff F is varargs (i.e. we need an + // Alloca) + case Attribute::StackProtect: + case Attribute::StackProtectStrong: + if (!F.isVarArg()) + break; + continue; + } + + Wrapper->removeFnAttr(Attr.getKindAsEnum()); + } + + Wrapper->addFnAttr(Attribute::NoInline); + Wrapper->addFnAttr(Attribute::OptimizeForSize); + + replaceWithWrapper(F, Wrapper, AddressUses); + + return Wrapper; +} + +static SmallVector findVAStarts(Function &F) { + SmallVector Insts; + for (auto &I : instructions(F)) { + if (isa(&I)) + Insts.push_back(cast(&I)); + } + return Insts; +} + +static AllocaInst *findAlloca(VAStartInst *VAStart) { + Instruction *Inst = VAStart; + while (Inst && !isa(Inst)) + Inst = dyn_cast(Inst->op_begin()); + + assert(Inst && "Could not find va_list alloca in var args function"); + return cast(Inst); +} + +static AllocaInst *createVAList(Module *M, IRBuilder<> &Builder, Type *VAListTy) { + auto VAListAlloca = Builder.CreateAlloca(VAListTy); + Builder.CreateCall( // @llvm.va_start(i8* ) + Intrinsic::getDeclaration(M, Intrinsic::vastart), + { Builder.CreateBitCast(VAListAlloca, Builder.getInt8PtrTy()) }); + + return VAListAlloca; +} + +static void createVAEndCall(IRBuilder<> &Builder, AllocaInst *VAListAlloca) { + Builder.CreateCall( // @llvm.va_end(i8* ) + Intrinsic::getDeclaration(VAListAlloca->getModule(), Intrinsic::vaend), + { Builder.CreateBitCast(VAListAlloca, Builder.getInt8PtrTy()) }); +} + +static void replaceWithVACopyCall(IRBuilder<> &Builder, VAStartInst *VAStart, + Argument *VAListArg) { + Builder.SetInsertPoint(VAStart); + Builder.CreateCall( // @llvm.va_copy(i8* , i8* ) + Intrinsic::getDeclaration(VAStart->getModule(), Intrinsic::vacopy), + { VAStart->getArgOperand(0), + Builder.CreateBitCast(VAListArg, Builder.getInt8PtrTy()) }); + VAStart->eraseFromParent(); +} + +void PagerandoWrappers::createWrapperBody(Function *Wrapper, Function *Callee, + Type *VAListTy) { + auto BB = BasicBlock::Create(Wrapper->getContext(), "", Wrapper); + IRBuilder<> Builder(BB); + + // Arguments + SmallVector Args; + for (auto &A : Wrapper->args()) + Args.push_back(&A); + + // va_list alloca and va_start + AllocaInst* VAListAlloca; + if (VAListTy) { + VAListAlloca = createVAList(Wrapper->getParent(), Builder, VAListTy); + Args.push_back(VAListAlloca); + } + + // Call + auto Call = Builder.CreateCall(Callee, Args); + Call->setCallingConv(Callee->getCallingConv()); + + // va_end + if (VAListTy) + createVAEndCall(Builder, VAListAlloca); + + // Return + if (Wrapper->getReturnType()->isVoidTy()) { + Builder.CreateRetVoid(); + } else { + Builder.CreateRet(Call); + } +} + +// Replaces the original function with a new function that takes a va_list +// parameter but is not varargs: foo(int, ...) -> foo$$origva(int, *va_list) +Function *PagerandoWrappers::rewriteVarargs(Function &F, Type *&VAListTy) { + auto VAStarts = findVAStarts(F); + if (VAStarts.empty()) return &F; + + // Determine va_list type + auto VAListAlloca = findAlloca(VAStarts[0]); + VAListTy = VAListAlloca->getAllocatedType(); + + // Adapt function type + auto FTy = F.getFunctionType(); + SmallVector Params(FTy->param_begin(), FTy->param_end()); + Params.push_back(VAListTy->getPointerTo()); + auto NonVAFty = FunctionType::get(FTy->getReturnType(), Params, false); + + // Create new function definition + auto NF = Function::Create(NonVAFty, F.getLinkage(), "", F.getParent()); + NF->takeName(&F); + NF->copyAttributesFrom(&F); + NF->setComdat(F.getComdat()); + NF->setSubprogram(F.getSubprogram()); + + // Move basic blocks into new function; F is now dysfunctional + NF->getBasicBlockList().splice(NF->begin(), F.getBasicBlockList()); + + // Adapt arguments (FN's additional 'va_list' arg does not need adaption) + auto DestArg = NF->arg_begin(); + for (auto &A : F.args()) { + A.replaceAllUsesWith(DestArg); + DestArg->takeName(&A); + ++DestArg; + } + + // Replace va_start with va_copy + IRBuilder<> Builder(NF->getContext()); + auto VAListArg = NF->arg_end() - 1; + for (auto VAStart : VAStarts) + replaceWithVACopyCall(Builder, VAStart, VAListArg); + + // Delete original function + F.eraseFromParent(); + + return NF; +} Index: lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- lib/Transforms/Utils/CodeExtractor.cpp +++ lib/Transforms/Utils/CodeExtractor.cpp @@ -747,6 +747,7 @@ case Attribute::OptForFuzzing: case Attribute::OptimizeNone: case Attribute::OptimizeForSize: + case Attribute::Pagerando: case Attribute::SafeStack: case Attribute::ShadowCallStack: case Attribute::SanitizeAddress: Index: test/Transforms/PagerandoWrappers/address-taken.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/address-taken.ll @@ -0,0 +1,16 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +; CHECK-LABEL: @fn_ptr1 = global void ()* @global +; CHECK-LABEL: @fn_ptr2 = global void ()* @internal + +@fn_ptr1 = global void ()* @global +@fn_ptr2 = global void ()* @internal + +; CHECK-LABEL: define internal void @internal() #0 +; CHECK-LABEL: define void @global() #0 + +define void @global() pagerando { ret void } +define internal void @internal() pagerando { ret void } + +; CHECK-LABEL: define hidden void @"global$$orig"() #1 +; CHECK-LABEL: define internal void @"internal$$orig"() #1 \ No newline at end of file Index: test/Transforms/PagerandoWrappers/calls.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/calls.ll @@ -0,0 +1,22 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +; CHECK-LABEL: define void @global() #0 { +; CHECK-LABEL: define hidden void @"global$$orig"() #1 { +; CHECK-LABEL: define internal void @internal() #1 { + +define void @global() pagerando { ret void } +define internal void @internal() pagerando { ret void } + +; CHECK-LABEL: define internal void @user() #1 { +define internal void @user() pagerando { +; CHECK-NEXT: call void @global() +; CHECK-NEXT: call void @internal() +; CHECK-NEXT: ret void + call void @global() + call void @internal() + ret void +} + + +; CHECK-LABEL: attributes #0 = { noinline optsize } +; CHECK-LABEL: attributes #1 = { pagerando } \ No newline at end of file Index: test/Transforms/PagerandoWrappers/varargs.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/varargs.ll @@ -0,0 +1,106 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +declare void @llvm.va_start(i8*) +declare void @llvm.va_end(i8*) +declare void @llvm.va_copy(i8*, i8*) + +define internal void @user() { + call i32 (...) @varags(i32 13, i32 37) + ret void +} +; CHECK-LABEL: define void @real_va_type(...) +; CHECK-NEXT: %1 = alloca %struct.va_list +; CHECK-NEXT: %2 = bitcast %struct.va_list* %1 to i8* +; CHECK-NEXT: call void @llvm.va_start(i8* %2) +; CHECK-NEXT: call void @"real_va_type$$origva"(%struct.va_list* %1) +; CHECK-NEXT: %3 = bitcast %struct.va_list* %1 to i8* +; CHECK-NEXT: call void @llvm.va_end(i8* %3) +; CHECK-NEXT: ret void + +; CHECK-LABEL: define void @copy(...) +; CHECK-NEXT: %1 = alloca i8 +; CHECK-NEXT: call void @llvm.va_start(i8* %1) +; CHECK-NEXT: call void @"copy$$origva"(i8* %1) +; CHECK-NEXT: call void @llvm.va_end(i8* %1) +; CHECK-NEXT: ret void + +; CHECK-LABEL: define void @multiple_starts(...) +; CHECK-NEXT: %1 = alloca i8 +; CHECK-NEXT: call void @llvm.va_start(i8* %1) +; CHECK-NEXT: call void @"multiple_starts$$origva"(i8* %1) +; CHECK-NEXT: call void @llvm.va_end(i8* %1) +; CHECK-NEXT: ret void + +; CHECK-LABEL: define i32 @varags(...) +; CHECK-NEXT: %1 = alloca i8 +; CHECK-NEXT: call void @llvm.va_start(i8* %1) +; CHECK-NEXT: %2 = call i32 @"varags$$origva"(i8* %1) +; CHECK-NEXT: call void @llvm.va_end(i8* %1) +; CHECK-NEXT: ret i32 %2 + +; CHECK-LABEL: define hidden i32 @"varags$$origva"(i8*) +; CHECK-NEXT: %va = alloca i8 +; CHECK-NEXT: call void @llvm.va_copy(i8* %va, i8* %0) +; CHECK-NEXT: %ret = va_arg i8* %va, i32 +; CHECK-NEXT: call void @llvm.va_end(i8* %va) +; CHECK-NEXT: ret i32 %ret +define i32 @varags(...) pagerando { + %va = alloca i8 + call void @llvm.va_start(i8* %va) + %ret = va_arg i8* %va, i32 + call void @llvm.va_end(i8* %va) + ret i32 %ret +} + +; CHECK-LABEL: define hidden void @"multiple_starts$$origva"(i8*) +; CHECK-NEXT: %va1 = alloca i8 +; CHECK-NEXT: %va2 = alloca i8 +; CHECK-NEXT: call void @llvm.va_copy(i8* %va1, i8* %0) +; CHECK-NEXT: call void @llvm.va_copy(i8* %va2, i8* %0) +; CHECK-NEXT: call void @llvm.va_end(i8* %va1) +; CHECK-NEXT: call void @llvm.va_end(i8* %va2) +; CHECK-NEXT: ret void +define void @multiple_starts(...) pagerando { + %va1 = alloca i8 + %va2 = alloca i8 + call void @llvm.va_start(i8* %va1) + call void @llvm.va_start(i8* %va2) + call void @llvm.va_end(i8* %va1) + call void @llvm.va_end(i8* %va2) + ret void +} + +; CHECK-LABEL: define hidden void @"copy$$origva"(i8*) +; CHECK-NEXT: %va1 = alloca i8 +; CHECK-NEXT: %va2 = alloca i8 +; CHECK-NEXT: call void @llvm.va_copy(i8* %va1, i8* %0) +; CHECK-NEXT: call void @llvm.va_copy(i8* %va2, i8* %va1) +; CHECK-NEXT: call void @llvm.va_end(i8* %va1) +; CHECK-NEXT: call void @llvm.va_end(i8* %va2) +; CHECK-NEXT: ret void +define void @copy(...) pagerando { + %va1 = alloca i8 + %va2 = alloca i8 + call void @llvm.va_start(i8* %va1) + call void @llvm.va_copy(i8* %va2, i8* %va1) + call void @llvm.va_end(i8* %va1) + call void @llvm.va_end(i8* %va2) + ret void +} + +%struct.va_list = type { i8*, i8*, i8*, i32, i32 } + +; CHECK-LABEL: define hidden void @"real_va_type$$origva"(%struct.va_list*) +; CHECK-NEXT: %va = alloca %struct.va_list +; CHECK-NEXT: %va_ptr = bitcast %struct.va_list* %va to i8* +; CHECK-NEXT: %2 = bitcast %struct.va_list* %0 to i8* +; CHECK-NEXT: call void @llvm.va_copy(i8* %va_ptr, i8* %2) +; CHECK-NEXT: call void @llvm.va_end(i8* %va_ptr) +; CHECK-NEXT: ret void +define void @real_va_type(...) pagerando { + %va = alloca %struct.va_list + %va_ptr = bitcast %struct.va_list* %va to i8* + call void @llvm.va_start(i8* %va_ptr) + call void @llvm.va_end(i8* %va_ptr) + ret void +} \ No newline at end of file Index: test/Transforms/PagerandoWrappers/wrapper-body.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/wrapper-body.ll @@ -0,0 +1,15 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +; CHECK-LABEL: define i32 @foo(i32) #0 +; CHECK-NEXT: %2 = call i32 @"foo$$orig"(i32 %0) +; CHECK-NEXT: ret i32 %2 + +define i32 @foo(i32 %arg) pagerando { + ret i32 %arg +} + +; CHECK-LABEL: define hidden i32 @"foo$$orig"(i32 %arg) #1 +; CHECK-NEXT: ret i32 %arg + +; CHECK-LABEL: attributes #0 = { noinline optsize } +; CHECK-LABEL: attributes #1 = { pagerando } \ No newline at end of file