blob: 56370c19616cdb4ea6811ae5838b543cc6b04504 [file] [log] [blame]
Christopher Ferris09385e72017-04-05 13:25:04 -07001/*
2 * Copyright (C) 2016 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 <errno.h>
18#include <fcntl.h>
19#include <inttypes.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <sys/mman.h>
23#include <sys/types.h>
24#include <unistd.h>
25
26#include <android-base/unique_fd.h>
27
Christopher Ferris60521c72017-08-18 15:10:53 -070028#include <cctype>
Christopher Ferris09385e72017-04-05 13:25:04 -070029#include <memory>
30#include <string>
31#include <vector>
32
Christopher Ferrisd226a512017-07-14 10:37:19 -070033#include <unwindstack/Elf.h>
34#include <unwindstack/Maps.h>
35#include <unwindstack/Memory.h>
36
37namespace unwindstack {
Christopher Ferris09385e72017-04-05 13:25:04 -070038
39MapInfo* Maps::Find(uint64_t pc) {
40 if (maps_.empty()) {
41 return nullptr;
42 }
43 size_t first = 0;
44 size_t last = maps_.size();
45 while (first < last) {
46 size_t index = (first + last) / 2;
Christopher Ferrisbe788d82017-11-27 14:50:38 -080047 MapInfo* cur = maps_[index];
Christopher Ferris09385e72017-04-05 13:25:04 -070048 if (pc >= cur->start && pc < cur->end) {
49 return cur;
50 } else if (pc < cur->start) {
51 last = index;
52 } else {
53 first = index + 1;
54 }
55 }
56 return nullptr;
57}
58
Christopher Ferris60521c72017-08-18 15:10:53 -070059// Assumes that line does not end in '\n'.
Christopher Ferrisbe788d82017-11-27 14:50:38 -080060static MapInfo* InternalParseLine(const char* line) {
Christopher Ferris60521c72017-08-18 15:10:53 -070061 // Do not use a sscanf implementation since it is not performant.
62
63 // Example linux /proc/<pid>/maps lines:
Christopher Ferris09385e72017-04-05 13:25:04 -070064 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
Christopher Ferris60521c72017-08-18 15:10:53 -070065 char* str;
66 const char* old_str = line;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080067 uint64_t start = strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -070068 if (old_str == str || *str++ != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080069 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -070070 }
Christopher Ferris60521c72017-08-18 15:10:53 -070071
72 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080073 uint64_t end = strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -070074 if (old_str == str || !std::isspace(*str++)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080075 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -070076 }
77
78 while (std::isspace(*str)) {
79 str++;
80 }
81
82 // Parse permissions data.
83 if (*str == '\0') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080084 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -070085 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -080086 uint16_t flags = 0;
Christopher Ferris60521c72017-08-18 15:10:53 -070087 if (*str == 'r') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080088 flags |= PROT_READ;
Christopher Ferris60521c72017-08-18 15:10:53 -070089 } else if (*str != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080090 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -070091 }
Christopher Ferris60521c72017-08-18 15:10:53 -070092 str++;
93 if (*str == 'w') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080094 flags |= PROT_WRITE;
Christopher Ferris60521c72017-08-18 15:10:53 -070095 } else if (*str != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080096 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -070097 }
Christopher Ferris60521c72017-08-18 15:10:53 -070098 str++;
99 if (*str == 'x') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800100 flags |= PROT_EXEC;
Christopher Ferris60521c72017-08-18 15:10:53 -0700101 } else if (*str != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800102 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700103 }
104 str++;
105 if (*str != 'p' && *str != 's') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800106 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700107 }
108 str++;
109
110 if (!std::isspace(*str++)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800111 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -0700112 }
113
Christopher Ferris60521c72017-08-18 15:10:53 -0700114 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800115 uint64_t offset = strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -0700116 if (old_str == str || !std::isspace(*str)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800117 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -0700118 }
119
Christopher Ferris60521c72017-08-18 15:10:53 -0700120 // Ignore the 00:00 values.
121 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800122 (void)strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -0700123 if (old_str == str || *str++ != ':') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800124 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700125 }
126 if (std::isspace(*str)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800127 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700128 }
129
130 // Skip the inode.
131 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800132 (void)strtoull(str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -0700133 if (old_str == str || !std::isspace(*str++)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800134 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700135 }
136
137 // Skip decimal digit.
138 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800139 (void)strtoull(old_str, &str, 10);
Christopher Ferris60521c72017-08-18 15:10:53 -0700140 if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800141 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700142 }
143
144 while (std::isspace(*str)) {
145 str++;
146 }
147 if (*str == '\0') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800148 return new MapInfo(start, end, offset, flags, "");
Christopher Ferris60521c72017-08-18 15:10:53 -0700149 }
150
151 // Save the name data.
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800152 std::string name(str);
Christopher Ferris60521c72017-08-18 15:10:53 -0700153
154 // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800155 if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
156 flags |= MAPS_FLAGS_DEVICE_MAP;
Christopher Ferris60521c72017-08-18 15:10:53 -0700157 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800158 return new MapInfo(start, end, offset, flags, name);
Christopher Ferris09385e72017-04-05 13:25:04 -0700159}
160
161bool Maps::Parse() {
Christopher Ferris60521c72017-08-18 15:10:53 -0700162 int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
163 if (fd == -1) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700164 return false;
165 }
166
Christopher Ferris60521c72017-08-18 15:10:53 -0700167 bool return_value = true;
168 char buffer[2048];
169 size_t leftover = 0;
170 while (true) {
171 ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
172 if (bytes == -1) {
173 return_value = false;
Christopher Ferris09385e72017-04-05 13:25:04 -0700174 break;
175 }
Christopher Ferris60521c72017-08-18 15:10:53 -0700176 if (bytes == 0) {
177 break;
178 }
179 bytes += leftover;
180 char* line = buffer;
181 while (bytes > 0) {
182 char* newline = static_cast<char*>(memchr(line, '\n', bytes));
183 if (newline == nullptr) {
184 memmove(buffer, line, bytes);
185 break;
186 }
187 *newline = '\0';
Christopher Ferris09385e72017-04-05 13:25:04 -0700188
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800189 MapInfo* map_info = InternalParseLine(line);
190 if (map_info == nullptr) {
Christopher Ferris60521c72017-08-18 15:10:53 -0700191 return_value = false;
192 break;
193 }
194 maps_.push_back(map_info);
195
196 bytes -= newline - line + 1;
197 line = newline + 1;
198 }
199 leftover = bytes;
Christopher Ferris09385e72017-04-05 13:25:04 -0700200 }
Christopher Ferris60521c72017-08-18 15:10:53 -0700201 close(fd);
202 return return_value;
Christopher Ferris09385e72017-04-05 13:25:04 -0700203}
204
205Maps::~Maps() {
206 for (auto& map : maps_) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800207 delete map;
Christopher Ferris09385e72017-04-05 13:25:04 -0700208 }
209}
210
211bool BufferMaps::Parse() {
212 const char* start_of_line = buffer_;
213 do {
214 std::string line;
215 const char* end_of_line = strchr(start_of_line, '\n');
216 if (end_of_line == nullptr) {
217 line = start_of_line;
218 } else {
Christopher Ferris09385e72017-04-05 13:25:04 -0700219 line = std::string(start_of_line, end_of_line - start_of_line);
Christopher Ferris60521c72017-08-18 15:10:53 -0700220 end_of_line++;
Christopher Ferris09385e72017-04-05 13:25:04 -0700221 }
222
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800223 MapInfo* map_info = InternalParseLine(line.c_str());
224 if (map_info == nullptr) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700225 return false;
226 }
227 maps_.push_back(map_info);
228
229 start_of_line = end_of_line;
230 } while (start_of_line != nullptr && *start_of_line != '\0');
231 return true;
232}
233
234const std::string RemoteMaps::GetMapsFile() const {
235 return "/proc/" + std::to_string(pid_) + "/maps";
236}
237
238bool OfflineMaps::Parse() {
239 // Format of maps information:
240 // <uint64_t> StartOffset
241 // <uint64_t> EndOffset
242 // <uint64_t> offset
243 // <uint16_t> flags
244 // <uint16_t> MapNameLength
245 // <VariableLengthValue> MapName
246 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
247 if (fd == -1) {
248 return false;
249 }
250
251 std::vector<char> name;
252 while (true) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800253 uint64_t start;
254 ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start)));
Christopher Ferris09385e72017-04-05 13:25:04 -0700255 if (bytes == 0) {
256 break;
257 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800258 if (bytes == -1 || bytes != sizeof(start)) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700259 return false;
260 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800261 uint64_t end;
262 bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end)));
263 if (bytes == -1 || bytes != sizeof(end)) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700264 return false;
265 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800266 uint64_t offset;
267 bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset)));
268 if (bytes == -1 || bytes != sizeof(offset)) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700269 return false;
270 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800271 uint16_t flags;
272 bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags)));
273 if (bytes == -1 || bytes != sizeof(flags)) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700274 return false;
275 }
276 uint16_t len;
277 bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
278 if (bytes == -1 || bytes != sizeof(len)) {
279 return false;
280 }
281 if (len > 0) {
282 name.resize(len);
283 bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
284 if (bytes == -1 || bytes != len) {
285 return false;
286 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800287 maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len)));
288 } else {
289 maps_.push_back(new MapInfo(start, end, offset, flags, ""));
Christopher Ferris09385e72017-04-05 13:25:04 -0700290 }
Christopher Ferris09385e72017-04-05 13:25:04 -0700291 }
292 return true;
293}
Christopher Ferrisd226a512017-07-14 10:37:19 -0700294
295} // namespace unwindstack