summaryrefslogtreecommitdiff
path: root/lib/System/Unix/Memory.inc
blob: a80f56fbc144fb4de134718b98e83e0eb8d07bf8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//===- Unix/Memory.cpp - Generic UNIX System Configuration ------*- C++ -*-===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// 
//===----------------------------------------------------------------------===//
//
// This file defines some functions for various memory management utilities.
//
//===----------------------------------------------------------------------===//

#include "Unix.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/System/Process.h"

#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif

#ifdef __APPLE__
#include <mach/mach.h>
#endif

/// AllocateRWX - Allocate a slab of memory with read/write/execute
/// permissions.  This is typically used for JIT applications where we want
/// to emit code to the memory then jump to it.  Getting this type of memory
/// is very OS specific.
///
llvm::sys::MemoryBlock 
llvm::sys::Memory::AllocateRWX(size_t NumBytes, const MemoryBlock* NearBlock,
                               std::string *ErrMsg) {
  if (NumBytes == 0) return MemoryBlock();

  size_t pageSize = Process::GetPageSize();
  size_t NumPages = (NumBytes+pageSize-1)/pageSize;

  int fd = -1;
#ifdef NEED_DEV_ZERO_FOR_MMAP
  static int zero_fd = open("/dev/zero", O_RDWR);
  if (zero_fd == -1) {
    MakeErrMsg(ErrMsg, "Can't open /dev/zero device");
    return MemoryBlock();
  }
  fd = zero_fd;
#endif

  int flags = MAP_PRIVATE |
#ifdef HAVE_MMAP_ANONYMOUS
  MAP_ANONYMOUS
#else
  MAP_ANON
#endif
  ;

  void* start = NearBlock ? (unsigned char*)NearBlock->base() + 
                            NearBlock->size() : 0;

#if defined(__APPLE__) && defined(__arm__)
  void *pa = ::mmap(start, pageSize*NumPages, PROT_READ|PROT_EXEC,
                    flags, fd, 0);
#else
  void *pa = ::mmap(start, pageSize*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC,
                    flags, fd, 0);
#endif
  if (pa == MAP_FAILED) {
    if (NearBlock) //Try again without a near hint
      return AllocateRWX(NumBytes, 0);

    MakeErrMsg(ErrMsg, "Can't allocate RWX Memory");
    return MemoryBlock();
  }

#if defined(__APPLE__) && defined(__arm__)
  kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)pa,
                                (vm_size_t)(pageSize*NumPages), 0,
                                VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
  if (KERN_SUCCESS != kr) {
    MakeErrMsg(ErrMsg, "vm_protect max RX failed");
    return sys::MemoryBlock();
  }

  kr = vm_protect(mach_task_self(), (vm_address_t)pa,
                  (vm_size_t)(pageSize*NumPages), 0,
                  VM_PROT_READ | VM_PROT_WRITE);
  if (KERN_SUCCESS != kr) {
    MakeErrMsg(ErrMsg, "vm_protect RW failed");
    return sys::MemoryBlock();
  }
#endif

  MemoryBlock result;
  result.Address = pa;
  result.Size = NumPages*pageSize;

  return result;
}

bool llvm::sys::Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) {
  if (M.Address == 0 || M.Size == 0) return false;
  if (0 != ::munmap(M.Address, M.Size))
    return MakeErrMsg(ErrMsg, "Can't release RWX Memory");
  return false;
}

bool llvm::sys::Memory::setWritable (MemoryBlock &M, std::string *ErrMsg) {
#if defined(__APPLE__) && defined(__arm__)
  if (M.Address == 0 || M.Size == 0) return false;
  sys::Memory::InvalidateInstructionCache(M.Address, M.Size);
  kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address,
    (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_WRITE);
  return KERN_SUCCESS == kr;
#else
  return true;
#endif
}

bool llvm::sys::Memory::setExecutable (MemoryBlock &M, std::string *ErrMsg) {
#if defined(__APPLE__) && defined(__arm__)
  if (M.Address == 0 || M.Size == 0) return false;
  sys::Memory::InvalidateInstructionCache(M.Address, M.Size);
  kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address,
    (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
  return KERN_SUCCESS == kr;
#else
  return false;
#endif
}

bool llvm::sys::Memory::setRangeWritable(const void *Addr, size_t Size) {
#if defined(__APPLE__) && defined(__arm__)
  kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr,
                                (vm_size_t)Size, 0,
                                VM_PROT_READ | VM_PROT_WRITE);
  return KERN_SUCCESS == kr;
#else
  return true;
#endif
}

bool llvm::sys::Memory::setRangeExecutable(const void *Addr, size_t Size) {
#if defined(__APPLE__) && defined(__arm__)
  kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr,
                                (vm_size_t)Size, 0,
                                VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY);
  return KERN_SUCCESS == kr;
#else
  return true;
#endif
}