blob: e1a1a7144653f3a50e61855e0034c955bd0e9d54 [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
Yabin Cuid5b22c52018-02-22 17:11:31 -080028#include <algorithm>
Christopher Ferris60521c72017-08-18 15:10:53 -070029#include <cctype>
Christopher Ferris09385e72017-04-05 13:25:04 -070030#include <memory>
31#include <string>
32#include <vector>
33
Christopher Ferrisd226a512017-07-14 10:37:19 -070034#include <unwindstack/Elf.h>
35#include <unwindstack/Maps.h>
36#include <unwindstack/Memory.h>
37
38namespace unwindstack {
Christopher Ferris09385e72017-04-05 13:25:04 -070039
40MapInfo* Maps::Find(uint64_t pc) {
41 if (maps_.empty()) {
42 return nullptr;
43 }
44 size_t first = 0;
45 size_t last = maps_.size();
46 while (first < last) {
47 size_t index = (first + last) / 2;
Christopher Ferrisbe788d82017-11-27 14:50:38 -080048 MapInfo* cur = maps_[index];
Christopher Ferris09385e72017-04-05 13:25:04 -070049 if (pc >= cur->start && pc < cur->end) {
50 return cur;
51 } else if (pc < cur->start) {
52 last = index;
53 } else {
54 first = index + 1;
55 }
56 }
57 return nullptr;
58}
59
Christopher Ferris60521c72017-08-18 15:10:53 -070060// Assumes that line does not end in '\n'.
Christopher Ferrisbe788d82017-11-27 14:50:38 -080061static MapInfo* InternalParseLine(const char* line) {
Christopher Ferris60521c72017-08-18 15:10:53 -070062 // Do not use a sscanf implementation since it is not performant.
63
64 // Example linux /proc/<pid>/maps lines:
Christopher Ferris09385e72017-04-05 13:25:04 -070065 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
Christopher Ferris60521c72017-08-18 15:10:53 -070066 char* str;
67 const char* old_str = line;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080068 uint64_t start = strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -070069 if (old_str == str || *str++ != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080070 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -070071 }
Christopher Ferris60521c72017-08-18 15:10:53 -070072
73 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080074 uint64_t end = strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -070075 if (old_str == str || !std::isspace(*str++)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080076 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -070077 }
78
79 while (std::isspace(*str)) {
80 str++;
81 }
82
83 // Parse permissions data.
84 if (*str == '\0') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080085 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -070086 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -080087 uint16_t flags = 0;
Christopher Ferris60521c72017-08-18 15:10:53 -070088 if (*str == 'r') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080089 flags |= PROT_READ;
Christopher Ferris60521c72017-08-18 15:10:53 -070090 } else if (*str != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080091 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -070092 }
Christopher Ferris60521c72017-08-18 15:10:53 -070093 str++;
94 if (*str == 'w') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080095 flags |= PROT_WRITE;
Christopher Ferris60521c72017-08-18 15:10:53 -070096 } else if (*str != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -080097 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -070098 }
Christopher Ferris60521c72017-08-18 15:10:53 -070099 str++;
100 if (*str == 'x') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800101 flags |= PROT_EXEC;
Christopher Ferris60521c72017-08-18 15:10:53 -0700102 } else if (*str != '-') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800103 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700104 }
105 str++;
106 if (*str != 'p' && *str != 's') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800107 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700108 }
109 str++;
110
111 if (!std::isspace(*str++)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800112 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -0700113 }
114
Christopher Ferris60521c72017-08-18 15:10:53 -0700115 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800116 uint64_t offset = strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -0700117 if (old_str == str || !std::isspace(*str)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800118 return nullptr;
Christopher Ferris09385e72017-04-05 13:25:04 -0700119 }
120
Christopher Ferris60521c72017-08-18 15:10:53 -0700121 // Ignore the 00:00 values.
122 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800123 (void)strtoull(old_str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -0700124 if (old_str == str || *str++ != ':') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800125 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700126 }
127 if (std::isspace(*str)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800128 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700129 }
130
131 // Skip the inode.
132 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800133 (void)strtoull(str, &str, 16);
Christopher Ferris60521c72017-08-18 15:10:53 -0700134 if (old_str == str || !std::isspace(*str++)) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800135 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700136 }
137
138 // Skip decimal digit.
139 old_str = str;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800140 (void)strtoull(old_str, &str, 10);
Christopher Ferris60521c72017-08-18 15:10:53 -0700141 if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800142 return nullptr;
Christopher Ferris60521c72017-08-18 15:10:53 -0700143 }
144
145 while (std::isspace(*str)) {
146 str++;
147 }
148 if (*str == '\0') {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800149 return new MapInfo(start, end, offset, flags, "");
Christopher Ferris60521c72017-08-18 15:10:53 -0700150 }
151
152 // Save the name data.
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800153 std::string name(str);
Christopher Ferris60521c72017-08-18 15:10:53 -0700154
155 // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800156 if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
157 flags |= MAPS_FLAGS_DEVICE_MAP;
Christopher Ferris60521c72017-08-18 15:10:53 -0700158 }
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800159 return new MapInfo(start, end, offset, flags, name);
Christopher Ferris09385e72017-04-05 13:25:04 -0700160}
161
162bool Maps::Parse() {
Christopher Ferris60521c72017-08-18 15:10:53 -0700163 int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
164 if (fd == -1) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700165 return false;
166 }
167
Christopher Ferris60521c72017-08-18 15:10:53 -0700168 bool return_value = true;
169 char buffer[2048];
170 size_t leftover = 0;
171 while (true) {
172 ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
173 if (bytes == -1) {
174 return_value = false;
Christopher Ferris09385e72017-04-05 13:25:04 -0700175 break;
176 }
Christopher Ferris60521c72017-08-18 15:10:53 -0700177 if (bytes == 0) {
178 break;
179 }
180 bytes += leftover;
181 char* line = buffer;
182 while (bytes > 0) {
183 char* newline = static_cast<char*>(memchr(line, '\n', bytes));
184 if (newline == nullptr) {
185 memmove(buffer, line, bytes);
186 break;
187 }
188 *newline = '\0';
Christopher Ferris09385e72017-04-05 13:25:04 -0700189
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800190 MapInfo* map_info = InternalParseLine(line);
191 if (map_info == nullptr) {
Christopher Ferris60521c72017-08-18 15:10:53 -0700192 return_value = false;
193 break;
194 }
195 maps_.push_back(map_info);
196
197 bytes -= newline - line + 1;
198 line = newline + 1;
199 }
200 leftover = bytes;
Christopher Ferris09385e72017-04-05 13:25:04 -0700201 }
Christopher Ferris60521c72017-08-18 15:10:53 -0700202 close(fd);
203 return return_value;
Christopher Ferris09385e72017-04-05 13:25:04 -0700204}
205
Christopher Ferrise7b66242017-12-15 11:17:45 -0800206void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
207 const std::string& name, uint64_t load_bias) {
208 MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
209 map_info->load_bias = load_bias;
210 maps_.push_back(map_info);
211}
212
Yabin Cuid5b22c52018-02-22 17:11:31 -0800213void Maps::Sort() {
214 std::sort(maps_.begin(), maps_.end(),
215 [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
216}
217
Christopher Ferris09385e72017-04-05 13:25:04 -0700218Maps::~Maps() {
219 for (auto& map : maps_) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800220 delete map;
Christopher Ferris09385e72017-04-05 13:25:04 -0700221 }
222}
223
224bool BufferMaps::Parse() {
225 const char* start_of_line = buffer_;
226 do {
227 std::string line;
228 const char* end_of_line = strchr(start_of_line, '\n');
229 if (end_of_line == nullptr) {
230 line = start_of_line;
231 } else {
Christopher Ferris09385e72017-04-05 13:25:04 -0700232 line = std::string(start_of_line, end_of_line - start_of_line);
Christopher Ferris60521c72017-08-18 15:10:53 -0700233 end_of_line++;
Christopher Ferris09385e72017-04-05 13:25:04 -0700234 }
235
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800236 MapInfo* map_info = InternalParseLine(line.c_str());
237 if (map_info == nullptr) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700238 return false;
239 }
240 maps_.push_back(map_info);
241
242 start_of_line = end_of_line;
243 } while (start_of_line != nullptr && *start_of_line != '\0');
244 return true;
245}
246
247const std::string RemoteMaps::GetMapsFile() const {
248 return "/proc/" + std::to_string(pid_) + "/maps";
249}
250
Christopher Ferrisd226a512017-07-14 10:37:19 -0700251} // namespace unwindstack