Skip to content

Commit afcb70d

Browse files
committed
[Support] Support NetBSD PaX MPROTECT in sys::Memory.
Removes AllocateRWX, setWritable and setExecutable from sys::Memory and standardizes on allocateMappedMemory / protectMappedMemory. The allocateMappedMemory method is updated to request full permissions for memory blocks so that they can be marked executable later. llvm-svn: 318464
1 parent 854a874 commit afcb70d

File tree

4 files changed

+30
-256
lines changed

4 files changed

+30
-256
lines changed

llvm/include/llvm/Support/Memory.h

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -109,51 +109,10 @@ namespace sys {
109109
static std::error_code protectMappedMemory(const MemoryBlock &Block,
110110
unsigned Flags);
111111

112-
/// This method allocates a block of Read/Write/Execute memory that is
113-
/// suitable for executing dynamically generated code (e.g. JIT). An
114-
/// attempt to allocate \p NumBytes bytes of virtual memory is made.
115-
/// \p NearBlock may point to an existing allocation in which case
116-
/// an attempt is made to allocate more memory near the existing block.
117-
///
118-
/// On success, this returns a non-null memory block, otherwise it returns
119-
/// a null memory block and fills in *ErrMsg.
120-
///
121-
/// @brief Allocate Read/Write/Execute memory.
122-
static MemoryBlock AllocateRWX(size_t NumBytes,
123-
const MemoryBlock *NearBlock,
124-
std::string *ErrMsg = nullptr);
125-
126-
/// This method releases a block of Read/Write/Execute memory that was
127-
/// allocated with the AllocateRWX method. It should not be used to
128-
/// release any memory block allocated any other way.
129-
///
130-
/// On success, this returns false, otherwise it returns true and fills
131-
/// in *ErrMsg.
132-
/// @brief Release Read/Write/Execute memory.
133-
static bool ReleaseRWX(MemoryBlock &block, std::string *ErrMsg = nullptr);
134-
135112
/// InvalidateInstructionCache - Before the JIT can run a block of code
136113
/// that has been emitted it must invalidate the instruction cache on some
137114
/// platforms.
138115
static void InvalidateInstructionCache(const void *Addr, size_t Len);
139-
140-
/// setExecutable - Before the JIT can run a block of code, it has to be
141-
/// given read and executable privilege. Return true if it is already r-x
142-
/// or the system is able to change its previlege.
143-
static bool setExecutable(MemoryBlock &M, std::string *ErrMsg = nullptr);
144-
145-
/// setWritable - When adding to a block of code, the JIT may need
146-
/// to mark a block of code as RW since the protections are on page
147-
/// boundaries, and the JIT internal allocations are not page aligned.
148-
static bool setWritable(MemoryBlock &M, std::string *ErrMsg = nullptr);
149-
150-
/// setRangeExecutable - Mark the page containing a range of addresses
151-
/// as executable.
152-
static bool setRangeExecutable(const void *Addr, size_t Size);
153-
154-
/// setRangeWritable - Mark the page containing a range of addresses
155-
/// as writable.
156-
static bool setRangeWritable(const void *Addr, size_t Size);
157116
};
158117

159118
/// Owning version of MemoryBlock.

llvm/lib/Support/Unix/Memory.inc

Lines changed: 4 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ Memory::allocateMappedMemory(size_t NumBytes,
102102

103103
int Protect = getPosixProtectionFlags(PFlags);
104104

105+
#if defined(__NetBSD__) && defined(PROT_MPROTECT)
106+
Protect |= PROT_MPROTECT(PROT_READ | PROT_WRITE | PROT_EXEC);
107+
#endif
108+
105109
// Use any near hint and the page size to set a page-aligned starting address
106110
uintptr_t Start = NearBlock ? reinterpret_cast<uintptr_t>(NearBlock->base()) +
107111
NearBlock->size() : 0;
@@ -166,129 +170,6 @@ Memory::protectMappedMemory(const MemoryBlock &M, unsigned Flags) {
166170
return std::error_code();
167171
}
168172

169-
/// AllocateRWX - Allocate a slab of memory with read/write/execute
170-
/// permissions. This is typically used for JIT applications where we want
171-
/// to emit code to the memory then jump to it. Getting this type of memory
172-
/// is very OS specific.
173-
///
174-
MemoryBlock
175-
Memory::AllocateRWX(size_t NumBytes, const MemoryBlock* NearBlock,
176-
std::string *ErrMsg) {
177-
if (NumBytes == 0) return MemoryBlock();
178-
179-
static const size_t PageSize = Process::getPageSize();
180-
size_t NumPages = (NumBytes+PageSize-1)/PageSize;
181-
182-
int fd = -1;
183-
184-
int flags = MAP_PRIVATE |
185-
#ifdef MAP_ANONYMOUS
186-
MAP_ANONYMOUS
187-
#else
188-
MAP_ANON
189-
#endif
190-
;
191-
192-
void* start = NearBlock ? (unsigned char*)NearBlock->base() +
193-
NearBlock->size() : nullptr;
194-
195-
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
196-
void *pa = ::mmap(start, PageSize*NumPages, PROT_READ|PROT_EXEC,
197-
flags, fd, 0);
198-
#elif defined(__NetBSD__) && defined(PROT_MPROTECT)
199-
void *pa =
200-
::mmap(start, PageSize * NumPages,
201-
PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC), flags, fd, 0);
202-
#else
203-
void *pa = ::mmap(start, PageSize*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC,
204-
flags, fd, 0);
205-
#endif
206-
if (pa == MAP_FAILED) {
207-
if (NearBlock) //Try again without a near hint
208-
return AllocateRWX(NumBytes, nullptr);
209-
210-
MakeErrMsg(ErrMsg, "Can't allocate RWX Memory");
211-
return MemoryBlock();
212-
}
213-
214-
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
215-
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)pa,
216-
(vm_size_t)(PageSize*NumPages), 0,
217-
VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
218-
if (KERN_SUCCESS != kr) {
219-
MakeErrMsg(ErrMsg, "vm_protect max RX failed");
220-
return MemoryBlock();
221-
}
222-
223-
kr = vm_protect(mach_task_self(), (vm_address_t)pa,
224-
(vm_size_t)(PageSize*NumPages), 0,
225-
VM_PROT_READ | VM_PROT_WRITE);
226-
if (KERN_SUCCESS != kr) {
227-
MakeErrMsg(ErrMsg, "vm_protect RW failed");
228-
return MemoryBlock();
229-
}
230-
#endif
231-
232-
MemoryBlock result;
233-
result.Address = pa;
234-
result.Size = NumPages*PageSize;
235-
236-
return result;
237-
}
238-
239-
bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) {
240-
if (M.Address == nullptr || M.Size == 0) return false;
241-
if (0 != ::munmap(M.Address, M.Size))
242-
return MakeErrMsg(ErrMsg, "Can't release RWX Memory");
243-
return false;
244-
}
245-
246-
bool Memory::setWritable (MemoryBlock &M, std::string *ErrMsg) {
247-
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
248-
if (M.Address == 0 || M.Size == 0) return false;
249-
Memory::InvalidateInstructionCache(M.Address, M.Size);
250-
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address,
251-
(vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_WRITE);
252-
return KERN_SUCCESS == kr;
253-
#else
254-
return true;
255-
#endif
256-
}
257-
258-
bool Memory::setExecutable (MemoryBlock &M, std::string *ErrMsg) {
259-
if (M.Address == nullptr || M.Size == 0) return false;
260-
Memory::InvalidateInstructionCache(M.Address, M.Size);
261-
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
262-
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address,
263-
(vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
264-
return KERN_SUCCESS == kr;
265-
#else
266-
return true;
267-
#endif
268-
}
269-
270-
bool Memory::setRangeWritable(const void *Addr, size_t Size) {
271-
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
272-
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr,
273-
(vm_size_t)Size, 0,
274-
VM_PROT_READ | VM_PROT_WRITE);
275-
return KERN_SUCCESS == kr;
276-
#else
277-
return true;
278-
#endif
279-
}
280-
281-
bool Memory::setRangeExecutable(const void *Addr, size_t Size) {
282-
#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__))
283-
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr,
284-
(vm_size_t)Size, 0,
285-
VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
286-
return KERN_SUCCESS == kr;
287-
#else
288-
return true;
289-
#endif
290-
}
291-
292173
/// InvalidateInstructionCache - Before the JIT can run a block of code
293174
/// that has been emitted it must invalidate the instruction cache on some
294175
/// platforms.

llvm/lib/Support/Windows/Memory.inc

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -160,85 +160,5 @@ void Memory::InvalidateInstructionCache(
160160
FlushInstructionCache(GetCurrentProcess(), Addr, Len);
161161
}
162162

163-
164-
MemoryBlock Memory::AllocateRWX(size_t NumBytes,
165-
const MemoryBlock *NearBlock,
166-
std::string *ErrMsg) {
167-
MemoryBlock MB;
168-
std::error_code EC;
169-
MB = allocateMappedMemory(NumBytes, NearBlock,
170-
MF_READ|MF_WRITE|MF_EXEC, EC);
171-
if (EC != std::error_code() && ErrMsg) {
172-
MakeErrMsg(ErrMsg, EC.message());
173-
}
174-
return MB;
175-
}
176-
177-
bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) {
178-
std::error_code EC = releaseMappedMemory(M);
179-
if (EC == std::error_code())
180-
return false;
181-
MakeErrMsg(ErrMsg, EC.message());
182-
return true;
183-
}
184-
185-
static DWORD getProtection(const void *addr) {
186-
MEMORY_BASIC_INFORMATION info;
187-
if (sizeof(info) == ::VirtualQuery(addr, &info, sizeof(info))) {
188-
return info.Protect;
189-
}
190-
return 0;
191-
}
192-
193-
bool Memory::setWritable(MemoryBlock &M, std::string *ErrMsg) {
194-
if (!setRangeWritable(M.Address, M.Size)) {
195-
return MakeErrMsg(ErrMsg, "Cannot set memory to writeable");
196-
}
197-
return true;
198-
}
199-
200-
bool Memory::setExecutable(MemoryBlock &M, std::string *ErrMsg) {
201-
if (!setRangeExecutable(M.Address, M.Size)) {
202-
return MakeErrMsg(ErrMsg, "Cannot set memory to executable");
203-
}
204-
return true;
205-
}
206-
207-
bool Memory::setRangeWritable(const void *Addr, size_t Size) {
208-
DWORD prot = getProtection(Addr);
209-
if (!prot)
210-
return false;
211-
212-
if (prot == PAGE_EXECUTE || prot == PAGE_EXECUTE_READ) {
213-
prot = PAGE_EXECUTE_READWRITE;
214-
} else if (prot == PAGE_NOACCESS || prot == PAGE_READONLY) {
215-
prot = PAGE_READWRITE;
216-
}
217-
218-
DWORD oldProt;
219-
Memory::InvalidateInstructionCache(Addr, Size);
220-
return ::VirtualProtect(const_cast<LPVOID>(Addr), Size, prot, &oldProt)
221-
== TRUE;
222-
}
223-
224-
bool Memory::setRangeExecutable(const void *Addr, size_t Size) {
225-
DWORD prot = getProtection(Addr);
226-
if (!prot)
227-
return false;
228-
229-
if (prot == PAGE_NOACCESS) {
230-
prot = PAGE_EXECUTE;
231-
} else if (prot == PAGE_READONLY) {
232-
prot = PAGE_EXECUTE_READ;
233-
} else if (prot == PAGE_READWRITE) {
234-
prot = PAGE_EXECUTE_READWRITE;
235-
}
236-
237-
DWORD oldProt;
238-
Memory::InvalidateInstructionCache(Addr, Size);
239-
return ::VirtualProtect(const_cast<LPVOID>(Addr), Size, prot, &oldProt)
240-
== TRUE;
241-
}
242-
243163
} // namespace sys
244164
} // namespace llvm

llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,14 @@ class TrivialMemoryManager : public RTDyldMemoryManager {
178178
void deregisterEHFrames() override {}
179179

180180
void preallocateSlab(uint64_t Size) {
181-
std::string Err;
182-
sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err);
181+
std::error_code EC;
182+
sys::MemoryBlock MB =
183+
sys::Memory::allocateMappedMemory(Size, nullptr,
184+
sys::Memory::MF_READ |
185+
sys::Memory::MF_WRITE,
186+
EC);
183187
if (!MB.base())
184-
report_fatal_error("Can't allocate enough memory: " + Err);
188+
report_fatal_error("Can't allocate enough memory: " + EC.message());
185189

186190
PreallocSlab = MB;
187191
UsePreallocation = true;
@@ -222,10 +226,14 @@ uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size,
222226
if (UsePreallocation)
223227
return allocateFromSlab(Size, Alignment, true /* isCode */);
224228

225-
std::string Err;
226-
sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err);
229+
std::error_code EC;
230+
sys::MemoryBlock MB =
231+
sys::Memory::allocateMappedMemory(Size, nullptr,
232+
sys::Memory::MF_READ |
233+
sys::Memory::MF_WRITE,
234+
EC);
227235
if (!MB.base())
228-
report_fatal_error("MemoryManager allocation failed: " + Err);
236+
report_fatal_error("MemoryManager allocation failed: " + EC.message());
229237
FunctionMemory.push_back(MB);
230238
return (uint8_t*)MB.base();
231239
}
@@ -242,10 +250,14 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size,
242250
if (UsePreallocation)
243251
return allocateFromSlab(Size, Alignment, false /* isCode */);
244252

245-
std::string Err;
246-
sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err);
253+
std::error_code EC;
254+
sys::MemoryBlock MB =
255+
sys::Memory::allocateMappedMemory(Size, nullptr,
256+
sys::Memory::MF_READ |
257+
sys::Memory::MF_WRITE,
258+
EC);
247259
if (!MB.base())
248-
report_fatal_error("MemoryManager allocation failed: " + Err);
260+
report_fatal_error("MemoryManager allocation failed: " + EC.message());
249261
DataMemory.push_back(MB);
250262
return (uint8_t*)MB.base();
251263
}
@@ -453,9 +465,11 @@ static int executeInput() {
453465

454466
// Make sure the memory is executable.
455467
// setExecutable will call InvalidateInstructionCache.
456-
std::string ErrorStr;
457-
if (!sys::Memory::setExecutable(FM, &ErrorStr))
458-
ErrorAndExit("unable to mark function executable: '" + ErrorStr + "'");
468+
if (auto EC = sys::Memory::protectMappedMemory(FM,
469+
sys::Memory::MF_READ |
470+
sys::Memory::MF_EXEC))
471+
ErrorAndExit("unable to mark function executable: '" + EC.message() +
472+
"'");
459473
}
460474

461475
// Dispatch to _main().

0 commit comments

Comments
 (0)