From 53608a34ce3f0969e9fb01eaa983422761011e03 Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Thu, 15 Nov 2012 23:50:01 +0000 Subject: [PATCH] Interface changes to allow RuntimeDyld memory managers to set memory permissions after an object has been loaded. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@168114 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ExecutionEngine/RuntimeDyld.h | 11 ++++++++++- lib/ExecutionEngine/JIT/JITMemoryManager.cpp | 6 +++++- lib/ExecutionEngine/MCJIT/MCJIT.cpp | 9 +++++++++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp | 12 ++++++++++-- tools/lli/RecordingMemoryManager.cpp | 3 ++- tools/lli/RecordingMemoryManager.h | 5 ++++- tools/lli/lli.cpp | 7 +++++-- tools/llvm-rtdyld/llvm-rtdyld.cpp | 7 +++++-- unittests/ExecutionEngine/JIT/JITTest.cpp | 5 +++-- .../ExecutionEngine/MCJIT/SectionMemoryManager.cpp | 3 ++- .../ExecutionEngine/MCJIT/SectionMemoryManager.h | 4 +++- 11 files changed, 58 insertions(+), 14 deletions(-) diff --git a/include/llvm/ExecutionEngine/RuntimeDyld.h b/include/llvm/ExecutionEngine/RuntimeDyld.h index 891f534862f..c3d160f0125 100644 --- a/include/llvm/ExecutionEngine/RuntimeDyld.h +++ b/include/llvm/ExecutionEngine/RuntimeDyld.h @@ -48,7 +48,7 @@ public: /// assigned by the JIT engine, and optionally recorded by the memory manager /// to access a loaded section. virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) = 0; + unsigned SectionID, bool IsReadOnly) = 0; /// getPointerToNamedFunction - This method returns the address of the /// specified function. As such it is only useful for resolving library @@ -59,6 +59,15 @@ public: /// message to stderr and aborts. virtual void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true) = 0; + + /// applyPermissions - This method is called when object loading is + /// complete and section page permissions can be applied. It is up to + /// the memory manager implementation to decide whether or not to act + /// on this method. The memory manager will typically allocate all + /// sections as read-write and then apply specific permissions when + /// this method is called. Returns true if an error occurred, false + /// otherwise. + virtual bool applyPermissions(std::string *ErrMsg = 0) = 0; }; class RuntimeDyld { diff --git a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp index 61bc119d305..bd0519e9c46 100644 --- a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp +++ b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp @@ -501,10 +501,14 @@ namespace { /// allocateDataSection - Allocate memory for a data section. uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) { + unsigned SectionID, bool IsReadOnly) { return (uint8_t*)DataAllocator.Allocate(Size, Alignment); } + bool applyPermissions(std::string *ErrMsg) { + return false; + } + /// startExceptionTable - Use startFunctionBody to allocate memory for the /// function's exception table. uint8_t* startExceptionTable(const Function* F, uintptr_t &ActualSize) { diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/lib/ExecutionEngine/MCJIT/MCJIT.cpp index 752c5b73ea3..d72e56378ba 100644 --- a/lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ b/lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -118,17 +118,26 @@ void MCJIT::emitObject(Module *m) { // FIXME: Add a parameter to identify which object is being finalized when // MCJIT supports multiple modules. +// FIXME: Provide a way to separate code emission, relocations and page +// protection in the interface. void MCJIT::finalizeObject() { // If the module hasn't been compiled, just do that. if (!isCompiled) { // If the call to Dyld.resolveRelocations() is removed from emitObject() // we'll need to do that here. emitObject(M); + + // Set page permissions. + MemMgr->applyPermissions(); + return; } // Resolve any relocations. Dyld.resolveRelocations(); + + // Set page permissions. + MemMgr->applyPermissions(); } void *MCJIT::getPointerToBasicBlock(BasicBlock *BB) { diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index f6dccb106d9..4118e8ad509 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -182,7 +182,7 @@ void RuntimeDyldImpl::emitCommonSymbols(ObjectImage &Obj, // Allocate memory for the section unsigned SectionID = Sections.size(); uint8_t *Addr = MemMgr->allocateDataSection(TotalSize, sizeof(void*), - SectionID); + SectionID, false); if (!Addr) report_fatal_error("Unable to allocate memory for common symbols!"); uint64_t Offset = 0; @@ -237,11 +237,13 @@ unsigned RuntimeDyldImpl::emitSection(ObjectImage &Obj, bool IsRequired; bool IsVirtual; bool IsZeroInit; + bool IsReadOnly; uint64_t DataSize; StringRef Name; Check(Section.isRequiredForExecution(IsRequired)); Check(Section.isVirtual(IsVirtual)); Check(Section.isZeroInit(IsZeroInit)); + Check(Section.isReadOnlyData(IsReadOnly)); Check(Section.getSize(DataSize)); Check(Section.getName(Name)); @@ -256,7 +258,7 @@ unsigned RuntimeDyldImpl::emitSection(ObjectImage &Obj, Allocate = DataSize + StubBufSize; Addr = IsCode ? MemMgr->allocateCodeSection(Allocate, Alignment, SectionID) - : MemMgr->allocateDataSection(Allocate, Alignment, SectionID); + : MemMgr->allocateDataSection(Allocate, Alignment, SectionID, IsReadOnly); if (!Addr) report_fatal_error("Unable to allocate section memory!"); @@ -451,6 +453,12 @@ void RuntimeDyldImpl::resolveExternalSymbols() { //===----------------------------------------------------------------------===// // RuntimeDyld class implementation RuntimeDyld::RuntimeDyld(RTDyldMemoryManager *mm) { + // FIXME: There's a potential issue lurking here if a single instance of + // RuntimeDyld is used to load multiple objects. The current implementation + // associates a single memory manager with a RuntimeDyld instance. Even + // though the public class spawns a new 'impl' instance for each load, + // they share a single memory manager. This can become a problem when page + // permissions are applied. Dyld = 0; MM = mm; } diff --git a/tools/lli/RecordingMemoryManager.cpp b/tools/lli/RecordingMemoryManager.cpp index 9e1cff55277..f54f17461e1 100644 --- a/tools/lli/RecordingMemoryManager.cpp +++ b/tools/lli/RecordingMemoryManager.cpp @@ -28,7 +28,8 @@ allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID) { } uint8_t *RecordingMemoryManager:: -allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID) { +allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, bool IsReadOnly) { // The recording memory manager is just a local copy of the remote target. // The alignment requirement is just stored here for later use. Regular // heap storage is sufficient here. diff --git a/tools/lli/RecordingMemoryManager.h b/tools/lli/RecordingMemoryManager.h index 1590235a793..20fd0c2e6ed 100644 --- a/tools/lli/RecordingMemoryManager.h +++ b/tools/lli/RecordingMemoryManager.h @@ -47,10 +47,13 @@ public: unsigned SectionID); uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID); + unsigned SectionID, bool IsReadOnly); void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true); + + bool applyPermissions(std::string *ErrMsg) { return false; } + // The following obsolete JITMemoryManager calls are stubbed out for // this model. void setMemoryWritable(); diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index d41a595de85..fa4669dec63 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -231,11 +231,13 @@ public: unsigned SectionID); virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID); + unsigned SectionID, bool IsReadOnly); virtual void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true); + virtual bool applyPermissions(std::string *ErrMsg) { return false; } + // Invalidate instruction cache for code sections. Some platforms with // separate data cache and instruction cache require explicit cache flush, // otherwise JIT code manipulations (like resolved relocations) will get to @@ -301,7 +303,8 @@ public: uint8_t *LLIMCJITMemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) { + unsigned SectionID, + bool IsReadOnly) { if (!Alignment) Alignment = 16; // Ensure that enough memory is requested to allow aligning. diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 7b5bd0388d8..e06d798cd51 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -58,13 +58,15 @@ public: uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID); uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID); + unsigned SectionID, bool IsReadOnly); virtual void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true) { return 0; } + bool applyPermissions(std::string *ErrMsg) { return false; } + // Invalidate instruction cache for sections with execute permissions. // Some platforms with separate data cache and instruction cache require // explicit cache flush, otherwise JIT code manipulations (like resolved @@ -82,7 +84,8 @@ uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) { + unsigned SectionID, + bool IsReadOnly) { sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, 0, 0); DataMemory.push_back(MB); return (uint8_t*)MB.base(); diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp index 59604dfbf5c..6e54449beba 100644 --- a/unittests/ExecutionEngine/JIT/JITTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITTest.cpp @@ -118,13 +118,14 @@ public: Base->endFunctionBody(F, FunctionStart, FunctionEnd); } virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) { - return Base->allocateDataSection(Size, Alignment, SectionID); + unsigned SectionID, bool IsReadOnly) { + return Base->allocateDataSection(Size, Alignment, SectionID, IsReadOnly); } virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID) { return Base->allocateCodeSection(Size, Alignment, SectionID); } + virtual bool applyPermissions(std::string *ErrMsg) { return false; } virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { return Base->allocateSpace(Size, Alignment); } diff --git a/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp index d6baf3c9bb8..225106ecab2 100644 --- a/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp +++ b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp @@ -32,7 +32,8 @@ namespace llvm { uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) { + unsigned SectionID, + bool IsReadOnly) { if (!Alignment) Alignment = 16; // Ensure that enough memory is requested to allow aligning. diff --git a/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h index e44217c9063..968ee63ffdf 100644 --- a/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h +++ b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h @@ -34,7 +34,9 @@ public: unsigned SectionID); virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID); + unsigned SectionID, bool IsReadOnly); + + virtual bool applyPermissions(std::string *ErrMsg) { return false; } virtual void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true); -- 2.34.1