blob: d3ebd4a60dd5e8291549ce112221c8d1b31a1290 [file] [log] [blame]
Brian Carlstrom27ec9612011-09-19 20:20:38 -07001/*
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 Hughes6c9c06d2011-11-07 16:43:47 -080021#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 Carlstrom27ec9612011-09-19 20:20:38 -070030namespace art {
31
Elliott Hughes96970cd2012-03-13 15:46:31 -070032#if !defined(NDEBUG)
33
34static size_t ParseHex(const std::string& string) {
Brian Carlstrom27ec9612011-09-19 20:20:38 -070035 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 Hughescc607472011-10-17 15:34:11 -070040 CHECK_EQ(*end, '\0') << "Failed to parse hexadecimal value from " << string;
Brian Carlstrom27ec9612011-09-19 20:20:38 -070041 return value;
42}
43
Elliott Hughes96970cd2012-03-13 15:46:31 -070044static 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 Carlstrom27ec9612011-09-19 20:20:38 -070053void CheckMapRequest(byte* addr, size_t length) {
Brian Carlstrom27ec9612011-09-19 20:20:38 -070054 if (addr == NULL) {
55 return;
56 }
Elliott Hughes96970cd2012-03-13 15:46:31 -070057 uint32_t base = reinterpret_cast<size_t>(addr);
58 uint32_t limit = base + length;
Brian Carlstrom27ec9612011-09-19 20:20:38 -070059
Elliott Hughes96970cd2012-03-13 15:46:31 -070060#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 Hughesba7a3ec2012-03-30 12:23:49 -070076 // TODO: the -v option replaces "-w -resident -dirty -purge -submap -allSplitLibs -noCoalesce" >= 10.6.
Elliott Hughes8d524a12012-03-30 17:20:10 -070077 std::string command(StringPrintf("vmmap -w -resident -submap -allSplitLibs -interleaved %d", getpid()));
Elliott Hughes96970cd2012-03-13 15:46:31 -070078 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 Hughes448e93c2012-03-28 22:30:06 -070091 std::string start_str(line.substr(23, 8));
92 std::string end_str(line.substr(32, 8));
Elliott Hughes96970cd2012-03-13 15:46:31 -070093 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 Carlstrom27ec9612011-09-19 20:20:38 -0700104 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 Hughes95572412011-12-13 18:14:20 -0800143 std::string start_str(maps.substr(i, 8));
144 std::string end_str(maps.substr(i+1+8, 8));
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700145 uint32_t start = ParseHex(start_str);
146 uint32_t end = ParseHex(end_str);
Elliott Hughes96970cd2012-03-13 15:46:31 -0700147 CheckMapRegion(base, limit, start, end, maps);
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700148 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 Hughes96970cd2012-03-13 15:46:31 -0700155#else
Elliott Hughes1bac54f2012-03-16 12:48:31 -0700156static void CheckMapRequest(byte*, size_t) { }
Elliott Hughes96970cd2012-03-13 15:46:31 -0700157#endif
158
Brian Carlstrom89521892011-12-07 22:05:07 -0800159MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t length, int prot) {
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700160 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 Hughes6c9c06d2011-11-07 16:43:47 -0800164
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 Carlstrom27ec9612011-09-19 20:20:38 -0700178 if (actual == MAP_FAILED) {
Elliott Hughes5ea8d4b2012-05-30 15:21:36 -0700179 PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_size
180 << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name;
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700181 return NULL;
182 }
183 return new MemMap(actual, length, actual, page_aligned_size);
184}
185
Brian Carlstrom89521892011-12-07 22:05:07 -0800186MemMap* MemMap::MapFileAtAddress(byte* addr, size_t length, int prot, int flags, int fd, off_t start) {
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700187 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
208MemMap::~MemMap() {
Ian Rogers30fab402012-01-23 15:43:46 -0800209 if (base_begin_ == NULL && base_size_ == 0) {
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700210 return;
211 }
Ian Rogers30fab402012-01-23 15:43:46 -0800212 int result = munmap(base_begin_, base_size_);
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700213 if (result == -1) {
214 PLOG(FATAL) << "munmap failed";
215 }
216}
217
Ian Rogers30fab402012-01-23 15:43:46 -0800218MemMap::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 Carlstrom27ec9612011-09-19 20:20:38 -0700224};
225
Logan Chiend88fa262012-06-06 15:23:32 +0800226
227bool 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 Liaoa060ed92012-06-07 09:25:28 -0700236 PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(base_begin_) << ", " << base_size_ << ", "
237 << prot << ") failed";
Logan Chiend88fa262012-06-06 15:23:32 +0800238 return false;
239}
240
Brian Carlstrom27ec9612011-09-19 20:20:38 -0700241} // namespace art