mem_map.cc revision 943af7dab1454517c5bd11a31ab99f260afb22d1
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "mem_map.h"
18
19#include <backtrace/BacktraceMap.h>
20
21#include "base/stringprintf.h"
22#include "ScopedFd.h"
23#include "utils.h"
24
25#define USE_ASHMEM 1
26
27#ifdef USE_ASHMEM
28#include <cutils/ashmem.h>
29#endif
30
31namespace art {
32
33#if !defined(NDEBUG)
34
35static std::ostream& operator<<(
36    std::ostream& os,
37    std::pair<BacktraceMap::const_iterator, BacktraceMap::const_iterator> iters) {
38  for (BacktraceMap::const_iterator it = iters.first; it != iters.second; ++it) {
39    os << StringPrintf("0x%08x-0x%08x %c%c%c %s\n",
40                       static_cast<uint32_t>(it->start),
41                       static_cast<uint32_t>(it->end),
42                       (it->flags & PROT_READ) ? 'r' : '-',
43                       (it->flags & PROT_WRITE) ? 'w' : '-',
44                       (it->flags & PROT_EXEC) ? 'x' : '-', it->name.c_str());
45  }
46  return os;
47}
48
49static void CheckMapRequest(byte* addr, size_t byte_count) {
50  if (addr == NULL) {
51    return;
52  }
53
54  uintptr_t base = reinterpret_cast<uintptr_t>(addr);
55  uintptr_t limit = base + byte_count;
56
57  BacktraceMap map(getpid());
58  if (!map.Build()) {
59    PLOG(WARNING) << "Failed to build process map";
60    return;
61  }
62  for (BacktraceMap::const_iterator it = map.begin(); it != map.end(); ++it) {
63    CHECK(!(base >= it->start && base < it->end)     // start of new within old
64        && !(limit > it->start && limit < it->end)   // end of new within old
65        && !(base <= it->start && limit > it->end))  // start/end of new includes all of old
66        << StringPrintf("Requested region 0x%08x-0x%08x overlaps with existing map 0x%08x-0x%08x (%s)\n",
67                        base, limit,
68                        static_cast<uint32_t>(it->start), static_cast<uint32_t>(it->end),
69                        it->name.c_str())
70        << std::make_pair(it, map.end());
71  }
72}
73
74#else
75static void CheckMapRequest(byte*, size_t) { }
76#endif
77
78MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot,
79                             std::string* error_msg) {
80  if (byte_count == 0) {
81    return new MemMap(name, NULL, 0, NULL, 0, prot);
82  }
83  size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
84  CheckMapRequest(addr, page_aligned_byte_count);
85
86#ifdef USE_ASHMEM
87  // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
88  // prefixed "dalvik-".
89  std::string debug_friendly_name("dalvik-");
90  debug_friendly_name += name;
91  ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count));
92  int flags = MAP_PRIVATE;
93  if (fd.get() == -1) {
94    *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno));
95    return nullptr;
96  }
97#else
98  ScopedFd fd(-1);
99  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
100#endif
101
102  byte* actual = reinterpret_cast<byte*>(mmap(addr, page_aligned_byte_count, prot, flags, fd.get(), 0));
103  if (actual == MAP_FAILED) {
104    std::string maps;
105    ReadFileToString("/proc/self/maps", &maps);
106    *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s",
107                              addr, page_aligned_byte_count, prot, flags, fd.get(),
108                              maps.c_str());
109    return nullptr;
110  }
111  return new MemMap(name, actual, byte_count, actual, page_aligned_byte_count, prot);
112}
113
114MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd,
115                                 off_t start, bool reuse, const char* filename,
116                                 std::string* error_msg) {
117  CHECK_NE(0, prot);
118  CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
119  if (byte_count == 0) {
120    return new MemMap("file", NULL, 0, NULL, 0, prot);
121  }
122  // Adjust 'offset' to be page-aligned as required by mmap.
123  int page_offset = start % kPageSize;
124  off_t page_aligned_offset = start - page_offset;
125  // Adjust 'byte_count' to be page-aligned as we will map this anyway.
126  size_t page_aligned_byte_count = RoundUp(byte_count + page_offset, kPageSize);
127  // The 'addr' is modified (if specified, ie non-null) to be page aligned to the file but not
128  // necessarily to virtual memory. mmap will page align 'addr' for us.
129  byte* page_aligned_addr = (addr == NULL) ? NULL : (addr - page_offset);
130  if (!reuse) {
131    // reuse means it is okay that it overlaps an existing page mapping.
132    // Only use this if you actually made the page reservation yourself.
133    CheckMapRequest(page_aligned_addr, page_aligned_byte_count);
134  } else {
135    CHECK(addr != NULL);
136  }
137  byte* actual = reinterpret_cast<byte*>(mmap(page_aligned_addr,
138                                              page_aligned_byte_count,
139                                              prot,
140                                              flags,
141                                              fd,
142                                              page_aligned_offset));
143  if (actual == MAP_FAILED) {
144    std::string strerr(strerror(errno));
145    std::string maps;
146    ReadFileToString("/proc/self/maps", &maps);
147    *error_msg = StringPrintf("mmap(%p, %zd, %x, %x, %d, %lld) of file '%s' failed: %s\n%s",
148                              page_aligned_addr, page_aligned_byte_count, prot, flags, fd,
149                              static_cast<int64_t>(page_aligned_offset), filename, strerr.c_str(),
150                              maps.c_str());
151    return NULL;
152  }
153  return new MemMap("file", actual + page_offset, byte_count, actual, page_aligned_byte_count,
154                    prot);
155}
156
157MemMap::~MemMap() {
158  if (base_begin_ == NULL && base_size_ == 0) {
159    return;
160  }
161  int result = munmap(base_begin_, base_size_);
162  if (result == -1) {
163    PLOG(FATAL) << "munmap failed";
164  }
165}
166
167MemMap::MemMap(const std::string& name, byte* begin, size_t size, void* base_begin,
168               size_t base_size, int prot)
169    : name_(name), begin_(begin), size_(size), base_begin_(base_begin), base_size_(base_size),
170      prot_(prot) {
171  if (size_ == 0) {
172    CHECK(begin_ == NULL);
173    CHECK(base_begin_ == NULL);
174    CHECK_EQ(base_size_, 0U);
175  } else {
176    CHECK(begin_ != NULL);
177    CHECK(base_begin_ != NULL);
178    CHECK_NE(base_size_, 0U);
179  }
180};
181
182MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
183                           std::string* error_msg) {
184  DCHECK_GE(new_end, Begin());
185  DCHECK_LE(new_end, End());
186  DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
187  DCHECK(IsAligned<kPageSize>(begin_));
188  DCHECK(IsAligned<kPageSize>(base_begin_));
189  DCHECK(IsAligned<kPageSize>(reinterpret_cast<byte*>(base_begin_) + base_size_));
190  DCHECK(IsAligned<kPageSize>(new_end));
191  byte* old_end = begin_ + size_;
192  byte* old_base_end = reinterpret_cast<byte*>(base_begin_) + base_size_;
193  byte* new_base_end = new_end;
194  DCHECK_LE(new_base_end, old_base_end);
195  if (new_base_end == old_base_end) {
196    return new MemMap(tail_name, NULL, 0, NULL, 0, tail_prot);
197  }
198  size_ = new_end - reinterpret_cast<byte*>(begin_);
199  base_size_ = new_base_end - reinterpret_cast<byte*>(base_begin_);
200  DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
201  size_t tail_size = old_end - new_end;
202  byte* tail_base_begin = new_base_end;
203  size_t tail_base_size = old_base_end - new_base_end;
204  DCHECK_EQ(tail_base_begin + tail_base_size, old_base_end);
205  DCHECK(IsAligned<kPageSize>(tail_base_size));
206
207#ifdef USE_ASHMEM
208  // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
209  // prefixed "dalvik-".
210  std::string debug_friendly_name("dalvik-");
211  debug_friendly_name += tail_name;
212  ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
213  int flags = MAP_PRIVATE;
214  if (fd.get() == -1) {
215    *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
216                              tail_name, strerror(errno));
217    return nullptr;
218  }
219#else
220  ScopedFd fd(-1);
221  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
222#endif
223
224  // Unmap/map the tail region.
225  int result = munmap(tail_base_begin, tail_base_size);
226  if (result == -1) {
227    std::string maps;
228    ReadFileToString("/proc/self/maps", &maps);
229    *error_msg = StringPrintf("munmap(%p, %zd) failed for '%s'\n%s",
230                              tail_base_begin, tail_base_size, name_.c_str(),
231                              maps.c_str());
232    return nullptr;
233  }
234  // Don't cause memory allocation between the munmap and the mmap
235  // calls. Otherwise, libc (or something else) might take this memory
236  // region. Note this isn't perfect as there's no way to prevent
237  // other threads to try to take this memory region here.
238  byte* actual = reinterpret_cast<byte*>(mmap(tail_base_begin, tail_base_size, tail_prot,
239                                              flags, fd.get(), 0));
240  if (actual == MAP_FAILED) {
241    std::string maps;
242    ReadFileToString("/proc/self/maps", &maps);
243    *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s",
244                              tail_base_begin, tail_base_size, tail_prot, flags, fd.get(),
245                              maps.c_str());
246    return nullptr;
247  }
248  return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot);
249}
250
251bool MemMap::Protect(int prot) {
252  if (base_begin_ == NULL && base_size_ == 0) {
253    prot_ = prot;
254    return true;
255  }
256
257  if (mprotect(base_begin_, base_size_, prot) == 0) {
258    prot_ = prot;
259    return true;
260  }
261
262  PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(base_begin_) << ", " << base_size_ << ", "
263              << prot << ") failed";
264  return false;
265}
266
267}  // namespace art
268