blob: a80f56fbc144fb4de134718b98e83e0eb8d07bf8 [file] [log] [blame]
Reid Spencer41b21bf2004-09-11 04:57:25 +00001//===- Unix/Memory.cpp - Generic UNIX System Configuration ------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
Chris Lattner4ee451d2007-12-29 20:36:04 +00005// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
Reid Spencer41b21bf2004-09-11 04:57:25 +00007//
8//===----------------------------------------------------------------------===//
9//
10// This file defines some functions for various memory management utilities.
11//
12//===----------------------------------------------------------------------===//
13
14#include "Unix.h"
Reid Kleckner10b4fc52009-07-23 21:46:56 +000015#include "llvm/Support/DataTypes.h"
Reid Spencer07c00d32004-12-27 06:17:03 +000016#include "llvm/System/Process.h"
17
18#ifdef HAVE_SYS_MMAN_H
19#include <sys/mman.h>
20#endif
Reid Spencer41b21bf2004-09-11 04:57:25 +000021
Evan Chengbc4707a2008-09-18 07:54:21 +000022#ifdef __APPLE__
23#include <mach/mach.h>
24#endif
25
Evan Cheng19a341a2008-09-16 17:28:18 +000026/// AllocateRWX - Allocate a slab of memory with read/write/execute
Reid Spencer07c00d32004-12-27 06:17:03 +000027/// permissions. This is typically used for JIT applications where we want
28/// to emit code to the memory then jump to it. Getting this type of memory
29/// is very OS specific.
30///
Chris Lattnerbed22d82006-07-07 17:32:37 +000031llvm::sys::MemoryBlock
Reid Kleckner10b4fc52009-07-23 21:46:56 +000032llvm::sys::Memory::AllocateRWX(size_t NumBytes, const MemoryBlock* NearBlock,
Chris Lattnerbed22d82006-07-07 17:32:37 +000033 std::string *ErrMsg) {
Reid Spencer07c00d32004-12-27 06:17:03 +000034 if (NumBytes == 0) return MemoryBlock();
35
Reid Kleckner10b4fc52009-07-23 21:46:56 +000036 size_t pageSize = Process::GetPageSize();
37 size_t NumPages = (NumBytes+pageSize-1)/pageSize;
Reid Spencer07c00d32004-12-27 06:17:03 +000038
39 int fd = -1;
40#ifdef NEED_DEV_ZERO_FOR_MMAP
41 static int zero_fd = open("/dev/zero", O_RDWR);
42 if (zero_fd == -1) {
Reid Spencer51c5a282006-08-23 20:34:57 +000043 MakeErrMsg(ErrMsg, "Can't open /dev/zero device");
Chris Lattnerbed22d82006-07-07 17:32:37 +000044 return MemoryBlock();
Reid Spencer07c00d32004-12-27 06:17:03 +000045 }
46 fd = zero_fd;
47#endif
48
49 int flags = MAP_PRIVATE |
50#ifdef HAVE_MMAP_ANONYMOUS
51 MAP_ANONYMOUS
52#else
53 MAP_ANON
54#endif
55 ;
Andrew Lenhartha00269b2005-07-29 23:40:16 +000056
Chris Lattnerbed22d82006-07-07 17:32:37 +000057 void* start = NearBlock ? (unsigned char*)NearBlock->base() +
58 NearBlock->size() : 0;
Andrew Lenhartha00269b2005-07-29 23:40:16 +000059
Evan Chengbc4707a2008-09-18 07:54:21 +000060#if defined(__APPLE__) && defined(__arm__)
61 void *pa = ::mmap(start, pageSize*NumPages, PROT_READ|PROT_EXEC,
62 flags, fd, 0);
63#else
Andrew Lenhartha00269b2005-07-29 23:40:16 +000064 void *pa = ::mmap(start, pageSize*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC,
Reid Spencer07c00d32004-12-27 06:17:03 +000065 flags, fd, 0);
Evan Chengbc4707a2008-09-18 07:54:21 +000066#endif
Reid Spencer07c00d32004-12-27 06:17:03 +000067 if (pa == MAP_FAILED) {
Andrew Lenhartha00269b2005-07-29 23:40:16 +000068 if (NearBlock) //Try again without a near hint
69 return AllocateRWX(NumBytes, 0);
Chris Lattnerbed22d82006-07-07 17:32:37 +000070
Reid Spencer51c5a282006-08-23 20:34:57 +000071 MakeErrMsg(ErrMsg, "Can't allocate RWX Memory");
Chris Lattnerbed22d82006-07-07 17:32:37 +000072 return MemoryBlock();
Reid Spencer07c00d32004-12-27 06:17:03 +000073 }
Evan Chengbc4707a2008-09-18 07:54:21 +000074
75#if defined(__APPLE__) && defined(__arm__)
76 kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)pa,
77 (vm_size_t)(pageSize*NumPages), 0,
78 VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
79 if (KERN_SUCCESS != kr) {
Daniel Dunbara7f2a9e2009-04-20 20:50:13 +000080 MakeErrMsg(ErrMsg, "vm_protect max RX failed");
Evan Chengbc4707a2008-09-18 07:54:21 +000081 return sys::MemoryBlock();
82 }
83
84 kr = vm_protect(mach_task_self(), (vm_address_t)pa,
85 (vm_size_t)(pageSize*NumPages), 0,
86 VM_PROT_READ | VM_PROT_WRITE);
87 if (KERN_SUCCESS != kr) {
Daniel Dunbara7f2a9e2009-04-20 20:50:13 +000088 MakeErrMsg(ErrMsg, "vm_protect RW failed");
Evan Chengbc4707a2008-09-18 07:54:21 +000089 return sys::MemoryBlock();
90 }
91#endif
92
Reid Spencer07c00d32004-12-27 06:17:03 +000093 MemoryBlock result;
94 result.Address = pa;
95 result.Size = NumPages*pageSize;
Evan Chengbc4707a2008-09-18 07:54:21 +000096
Reid Spencer07c00d32004-12-27 06:17:03 +000097 return result;
98}
99
Chris Lattnerbed22d82006-07-07 17:32:37 +0000100bool llvm::sys::Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) {
101 if (M.Address == 0 || M.Size == 0) return false;
102 if (0 != ::munmap(M.Address, M.Size))
Reid Spencer51c5a282006-08-23 20:34:57 +0000103 return MakeErrMsg(ErrMsg, "Can't release RWX Memory");
Chris Lattnerbed22d82006-07-07 17:32:37 +0000104 return false;
Reid Spencer41b21bf2004-09-11 04:57:25 +0000105}
106
Jim Grosbachcce6c292008-10-03 16:17:20 +0000107bool llvm::sys::Memory::setWritable (MemoryBlock &M, std::string *ErrMsg) {
108#if defined(__APPLE__) && defined(__arm__)
109 if (M.Address == 0 || M.Size == 0) return false;
110 sys::Memory::InvalidateInstructionCache(M.Address, M.Size);
111 kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address,
112 (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_WRITE);
113 return KERN_SUCCESS == kr;
114#else
115 return true;
116#endif
117}
118
119bool llvm::sys::Memory::setExecutable (MemoryBlock &M, std::string *ErrMsg) {
120#if defined(__APPLE__) && defined(__arm__)
121 if (M.Address == 0 || M.Size == 0) return false;
122 sys::Memory::InvalidateInstructionCache(M.Address, M.Size);
123 kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address,
124 (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
125 return KERN_SUCCESS == kr;
126#else
127 return false;
128#endif
129}
130
Jim Grosbach932a32d2008-10-20 21:39:23 +0000131bool llvm::sys::Memory::setRangeWritable(const void *Addr, size_t Size) {
132#if defined(__APPLE__) && defined(__arm__)
133 kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr,
134 (vm_size_t)Size, 0,
135 VM_PROT_READ | VM_PROT_WRITE);
136 return KERN_SUCCESS == kr;
137#else
138 return true;
139#endif
140}
141
142bool llvm::sys::Memory::setRangeExecutable(const void *Addr, size_t Size) {
143#if defined(__APPLE__) && defined(__arm__)
144 kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr,
145 (vm_size_t)Size, 0,
146 VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
147 return KERN_SUCCESS == kr;
148#else
149 return true;
150#endif
151}