Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 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 <sys/mman.h> |
| 20 | |
Elliott Hughes | 6c9c06d | 2011-11-07 16:43:47 -0800 | [diff] [blame] | 21 | #include "ScopedFd.h" |
| 22 | #include "utils.h" |
| 23 | |
| 24 | #define USE_ASHMEM 1 |
| 25 | |
| 26 | #ifdef USE_ASHMEM |
| 27 | #include <cutils/ashmem.h> |
| 28 | #endif |
| 29 | |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 30 | namespace art { |
| 31 | |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 32 | #if !defined(NDEBUG) |
| 33 | |
| 34 | static size_t ParseHex(const std::string& string) { |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 35 | CHECK_EQ(8U, string.size()); |
| 36 | const char* str = string.c_str(); |
| 37 | char* end; |
| 38 | size_t value = strtoul(str, &end, 16); |
| 39 | CHECK(end != str) << "Failed to parse hexadecimal value from " << string; |
Elliott Hughes | cc60747 | 2011-10-17 15:34:11 -0700 | [diff] [blame] | 40 | CHECK_EQ(*end, '\0') << "Failed to parse hexadecimal value from " << string; |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 41 | return value; |
| 42 | } |
| 43 | |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 44 | static void CheckMapRegion(uint32_t base, uint32_t limit, uint32_t start, uint32_t end, const std::string& maps) { |
| 45 | CHECK(!(base >= start && base < end) // start of new within old |
| 46 | && !(limit > start && limit < end) // end of new within old |
| 47 | && !(base <= start && limit > end)) // start/end of new includes all of old |
| 48 | << StringPrintf("Requested region %08x-%08x overlaps with existing map %08x-%08x\n", |
| 49 | base, limit, start, end) |
| 50 | << maps; |
| 51 | } |
| 52 | |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 53 | void CheckMapRequest(byte* addr, size_t length) { |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 54 | if (addr == NULL) { |
| 55 | return; |
| 56 | } |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 57 | uint32_t base = reinterpret_cast<size_t>(addr); |
| 58 | uint32_t limit = base + length; |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 59 | |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 60 | #if defined(__APPLE__) |
| 61 | // Mac OS vmmap(1) output currently looks something like this: |
| 62 | |
| 63 | // Virtual Memory Map of process 51036 (dex2oatd) |
| 64 | // Output report format: 2.2 -- 32-bit process |
| 65 | // |
| 66 | // ==== regions for process 51036 (non-writable and writable regions are interleaved) |
| 67 | // __PAGEZERO 00000000-00001000 [ 4K 0K 0K] ---/--- SM=NUL out/host/darwin-x86/bin/dex2oatd |
| 68 | // __TEXT 00001000-00015000 [ 80K 80K 0K] r-x/rwx SM=COW out/host/darwin-x86/bin/dex2oatd |
| 69 | // __DATA 00015000-00016000 [ 4K 4K 4K] rw-/rwx SM=PRV out/host/darwin-x86/bin/dex2oatd |
| 70 | // __LINKEDIT 00016000-00044000 [ 184K 184K 0K] r--/rwx SM=COW out/host/darwin-x86/bin/dex2oatd |
| 71 | // __TEXT 00044000-00046000 [ 8K 8K 4K] r-x/rwx SM=COW out/host/darwin-x86/obj/lib/libnativehelper.dylib |
| 72 | // __DATA 00046000-00047000 [ 4K 4K 4K] rw-/rwx SM=ZER out/host/darwin-x86/obj/lib/libnativehelper.dylib |
| 73 | // __LINKEDIT 00047000-0004a000 [ 12K 12K 0K] r--/rwx SM=COW out/host/darwin-x86/obj/lib/libnativehelper.dylib |
| 74 | // ... |
| 75 | |
Elliott Hughes | ba7a3ec | 2012-03-30 12:23:49 -0700 | [diff] [blame] | 76 | // TODO: the -v option replaces "-w -resident -dirty -purge -submap -allSplitLibs -noCoalesce" >= 10.6. |
Elliott Hughes | 8d524a1 | 2012-03-30 17:20:10 -0700 | [diff] [blame] | 77 | std::string command(StringPrintf("vmmap -w -resident -submap -allSplitLibs -interleaved %d", getpid())); |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 78 | FILE* fp = popen(command.c_str(), "r"); |
| 79 | if (fp == NULL) { |
| 80 | PLOG(FATAL) << "popen failed"; |
| 81 | } |
| 82 | std::vector<char> chars(512); |
| 83 | std::string maps; |
| 84 | while (fgets(&chars[0], chars.size(), fp) != NULL) { |
| 85 | std::string line(&chars[0]); |
| 86 | maps += line; |
| 87 | if (line.size() < 40 || line[31] != '-') { |
| 88 | continue; |
| 89 | } |
| 90 | |
Elliott Hughes | 448e93c | 2012-03-28 22:30:06 -0700 | [diff] [blame] | 91 | std::string start_str(line.substr(23, 8)); |
| 92 | std::string end_str(line.substr(32, 8)); |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 93 | uint32_t start = ParseHex(start_str); |
| 94 | uint32_t end = ParseHex(end_str); |
| 95 | CheckMapRegion(base, limit, start, end, maps); |
| 96 | } |
| 97 | if (ferror(fp)) { |
| 98 | PLOG(FATAL) << "fgets failed"; |
| 99 | } |
| 100 | if (pclose(fp) == -1) { |
| 101 | PLOG(FATAL) << "pclose failed"; |
| 102 | } |
| 103 | #else // Linux |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 104 | std::string maps; |
| 105 | bool read = ReadFileToString("/proc/self/maps", &maps); |
| 106 | if (!read) { |
| 107 | PLOG(FATAL) << "Failed to read /proc/self/maps"; |
| 108 | } |
| 109 | // Quick and dirty parse of output like shown below. We only focus |
| 110 | // on grabbing the two 32-bit hex values at the start of each line |
| 111 | // and will fail on wider addresses found on 64-bit systems. |
| 112 | |
| 113 | // 00008000-0001f000 r-xp 00000000 b3:01 273 /system/bin/toolbox |
| 114 | // 0001f000-00021000 rw-p 00017000 b3:01 273 /system/bin/toolbox |
| 115 | // 00021000-00029000 rw-p 00000000 00:00 0 [heap] |
| 116 | // 40011000-40053000 r-xp 00000000 b3:01 1050 /system/lib/libc.so |
| 117 | // 40053000-40056000 rw-p 00042000 b3:01 1050 /system/lib/libc.so |
| 118 | // 40056000-40061000 rw-p 00000000 00:00 0 |
| 119 | // 40061000-40063000 r-xp 00000000 b3:01 1107 /system/lib/libusbhost.so |
| 120 | // 40063000-40064000 rw-p 00002000 b3:01 1107 /system/lib/libusbhost.so |
| 121 | // 4009d000-400a0000 r-xp 00000000 b3:01 1022 /system/lib/liblog.so |
| 122 | // 400a0000-400a1000 rw-p 00003000 b3:01 1022 /system/lib/liblog.so |
| 123 | // 400b7000-400cc000 r-xp 00000000 b3:01 932 /system/lib/libm.so |
| 124 | // 400cc000-400cd000 rw-p 00015000 b3:01 932 /system/lib/libm.so |
| 125 | // 400cf000-400d0000 r--p 00000000 00:00 0 |
| 126 | // 400e4000-400ec000 r--s 00000000 00:0b 388 /dev/__properties__ (deleted) |
| 127 | // 400ec000-400fa000 r-xp 00000000 b3:01 1101 /system/lib/libcutils.so |
| 128 | // 400fa000-400fb000 rw-p 0000e000 b3:01 1101 /system/lib/libcutils.so |
| 129 | // 400fb000-4010a000 rw-p 00000000 00:00 0 |
| 130 | // 4010d000-4010e000 r-xp 00000000 b3:01 929 /system/lib/libstdc++.so |
| 131 | // 4010e000-4010f000 rw-p 00001000 b3:01 929 /system/lib/libstdc++.so |
| 132 | // b0001000-b0009000 r-xp 00001000 b3:01 1098 /system/bin/linker |
| 133 | // b0009000-b000a000 rw-p 00009000 b3:01 1098 /system/bin/linker |
| 134 | // b000a000-b0015000 rw-p 00000000 00:00 0 |
| 135 | // bee35000-bee56000 rw-p 00000000 00:00 0 [stack] |
| 136 | // ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors] |
| 137 | |
| 138 | for (size_t i = 0; i < maps.size(); i++) { |
| 139 | size_t remaining = maps.size() - i; |
| 140 | if (remaining < 8+1+8) { // 00008000-0001f000 |
| 141 | LOG(FATAL) << "Failed to parse at pos " << i << "\n" << maps; |
| 142 | } |
Elliott Hughes | 9557241 | 2011-12-13 18:14:20 -0800 | [diff] [blame] | 143 | std::string start_str(maps.substr(i, 8)); |
| 144 | std::string end_str(maps.substr(i+1+8, 8)); |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 145 | uint32_t start = ParseHex(start_str); |
| 146 | uint32_t end = ParseHex(end_str); |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 147 | CheckMapRegion(base, limit, start, end, maps); |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 148 | i += 8+1+8; |
| 149 | i = maps.find('\n', i); |
| 150 | CHECK(i != std::string::npos) << "Failed to find newline from pos " << i << "\n" << maps; |
| 151 | } |
| 152 | #endif |
| 153 | } |
| 154 | |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 155 | #else |
Elliott Hughes | 1bac54f | 2012-03-16 12:48:31 -0700 | [diff] [blame] | 156 | static void CheckMapRequest(byte*, size_t) { } |
Elliott Hughes | 96970cd | 2012-03-13 15:46:31 -0700 | [diff] [blame] | 157 | #endif |
| 158 | |
Brian Carlstrom | 8952189 | 2011-12-07 22:05:07 -0800 | [diff] [blame] | 159 | MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t length, int prot) { |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 160 | CHECK_NE(0U, length); |
| 161 | CHECK_NE(0, prot); |
| 162 | size_t page_aligned_size = RoundUp(length, kPageSize); |
| 163 | CheckMapRequest(addr, page_aligned_size); |
Elliott Hughes | 6c9c06d | 2011-11-07 16:43:47 -0800 | [diff] [blame] | 164 | |
| 165 | #ifdef USE_ASHMEM |
| 166 | ScopedFd fd(ashmem_create_region(name, page_aligned_size)); |
| 167 | int flags = MAP_PRIVATE; |
| 168 | if (fd.get() == -1) { |
| 169 | PLOG(ERROR) << "ashmem_create_region failed (" << name << ")"; |
| 170 | return NULL; |
| 171 | } |
| 172 | #else |
| 173 | ScopedFd fd(-1); |
| 174 | int flags = MAP_PRIVATE | MAP_ANONYMOUS; |
| 175 | #endif |
| 176 | |
| 177 | byte* actual = reinterpret_cast<byte*>(mmap(addr, page_aligned_size, prot, flags, fd.get(), 0)); |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 178 | if (actual == MAP_FAILED) { |
Elliott Hughes | 5ea8d4b | 2012-05-30 15:21:36 -0700 | [diff] [blame] | 179 | PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_size |
| 180 | << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name; |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 181 | return NULL; |
| 182 | } |
| 183 | return new MemMap(actual, length, actual, page_aligned_size); |
| 184 | } |
| 185 | |
Brian Carlstrom | 8952189 | 2011-12-07 22:05:07 -0800 | [diff] [blame] | 186 | MemMap* MemMap::MapFileAtAddress(byte* addr, size_t length, int prot, int flags, int fd, off_t start) { |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 187 | CHECK_NE(0U, length); |
| 188 | CHECK_NE(0, prot); |
| 189 | CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE)); |
| 190 | // adjust to be page-aligned |
| 191 | int page_offset = start % kPageSize; |
| 192 | off_t page_aligned_offset = start - page_offset; |
| 193 | size_t page_aligned_size = RoundUp(length + page_offset, kPageSize); |
| 194 | CheckMapRequest(addr, page_aligned_size); |
| 195 | byte* actual = reinterpret_cast<byte*>(mmap(addr, |
| 196 | page_aligned_size, |
| 197 | prot, |
| 198 | flags, |
| 199 | fd, |
| 200 | page_aligned_offset)); |
| 201 | if (actual == MAP_FAILED) { |
| 202 | PLOG(ERROR) << "mmap failed"; |
| 203 | return NULL; |
| 204 | } |
| 205 | return new MemMap(actual + page_offset, length, actual, page_aligned_size); |
| 206 | } |
| 207 | |
| 208 | MemMap::~MemMap() { |
Ian Rogers | 30fab40 | 2012-01-23 15:43:46 -0800 | [diff] [blame] | 209 | if (base_begin_ == NULL && base_size_ == 0) { |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 210 | return; |
| 211 | } |
Ian Rogers | 30fab40 | 2012-01-23 15:43:46 -0800 | [diff] [blame] | 212 | int result = munmap(base_begin_, base_size_); |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 213 | if (result == -1) { |
| 214 | PLOG(FATAL) << "munmap failed"; |
| 215 | } |
| 216 | } |
| 217 | |
Ian Rogers | 30fab40 | 2012-01-23 15:43:46 -0800 | [diff] [blame] | 218 | MemMap::MemMap(byte* begin, size_t size, void* base_begin, size_t base_size) |
| 219 | : begin_(begin), size_(size), base_begin_(base_begin), base_size_(base_size) { |
| 220 | CHECK(begin_ != NULL); |
| 221 | CHECK_NE(size_, 0U); |
| 222 | CHECK(base_begin_ != NULL); |
| 223 | CHECK_NE(base_size_, 0U); |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 224 | }; |
| 225 | |
Logan Chien | d88fa26 | 2012-06-06 15:23:32 +0800 | [diff] [blame] | 226 | |
| 227 | bool MemMap::Protect(int prot) { |
| 228 | if (base_begin_ == NULL && base_size_ == 0) { |
| 229 | return true; |
| 230 | } |
| 231 | |
| 232 | if (mprotect(base_begin_, base_size_, prot) == 0) { |
| 233 | return true; |
| 234 | } |
| 235 | |
Shih-wei Liao | a060ed9 | 2012-06-07 09:25:28 -0700 | [diff] [blame^] | 236 | PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(base_begin_) << ", " << base_size_ << ", " |
| 237 | << prot << ") failed"; |
Logan Chien | d88fa26 | 2012-06-06 15:23:32 +0800 | [diff] [blame] | 238 | return false; |
| 239 | } |
| 240 | |
Brian Carlstrom | 27ec961 | 2011-09-19 20:20:38 -0700 | [diff] [blame] | 241 | } // namespace art |