blob: 5e1c0a2fdfded7ecd10fc16283b5526375e70386 [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;
47 MapInfo* cur = &maps_[index];
48 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'.
60static bool InternalParseLine(const char* line, MapInfo* map_info) {
61 // 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;
67 map_info->start = strtoul(old_str, &str, 16);
68 if (old_str == str || *str++ != '-') {
Christopher Ferris09385e72017-04-05 13:25:04 -070069 return false;
70 }
Christopher Ferris60521c72017-08-18 15:10:53 -070071
72 old_str = str;
73 map_info->end = strtoul(old_str, &str, 16);
74 if (old_str == str || !std::isspace(*str++)) {
75 return false;
76 }
77
78 while (std::isspace(*str)) {
79 str++;
80 }
81
82 // Parse permissions data.
83 if (*str == '\0') {
84 return false;
85 }
86 map_info->flags = 0;
87 if (*str == 'r') {
Christopher Ferris09385e72017-04-05 13:25:04 -070088 map_info->flags |= PROT_READ;
Christopher Ferris60521c72017-08-18 15:10:53 -070089 } else if (*str != '-') {
90 return false;
Christopher Ferris09385e72017-04-05 13:25:04 -070091 }
Christopher Ferris60521c72017-08-18 15:10:53 -070092 str++;
93 if (*str == 'w') {
Christopher Ferris09385e72017-04-05 13:25:04 -070094 map_info->flags |= PROT_WRITE;
Christopher Ferris60521c72017-08-18 15:10:53 -070095 } else if (*str != '-') {
96 return false;
Christopher Ferris09385e72017-04-05 13:25:04 -070097 }
Christopher Ferris60521c72017-08-18 15:10:53 -070098 str++;
99 if (*str == 'x') {
Christopher Ferris09385e72017-04-05 13:25:04 -0700100 map_info->flags |= PROT_EXEC;
Christopher Ferris60521c72017-08-18 15:10:53 -0700101 } else if (*str != '-') {
102 return false;
103 }
104 str++;
105 if (*str != 'p' && *str != 's') {
106 return false;
107 }
108 str++;
109
110 if (!std::isspace(*str++)) {
111 return false;
Christopher Ferris09385e72017-04-05 13:25:04 -0700112 }
113
Christopher Ferris60521c72017-08-18 15:10:53 -0700114 old_str = str;
115 map_info->offset = strtoul(old_str, &str, 16);
116 if (old_str == str || !std::isspace(*str)) {
117 return false;
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;
122 (void)strtoul(old_str, &str, 16);
123 if (old_str == str || *str++ != ':') {
124 return false;
125 }
126 if (std::isspace(*str)) {
127 return false;
128 }
129
130 // Skip the inode.
131 old_str = str;
132 (void)strtoul(str, &str, 16);
133 if (old_str == str || !std::isspace(*str++)) {
134 return false;
135 }
136
137 // Skip decimal digit.
138 old_str = str;
139 (void)strtoul(old_str, &str, 10);
140 if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
141 return false;
142 }
143
144 while (std::isspace(*str)) {
145 str++;
146 }
147 if (*str == '\0') {
148 map_info->name = str;
149 return true;
150 }
151
152 // Save the name data.
153 map_info->name = str;
154
155 // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
156 if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
157 map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
158 }
Christopher Ferris09385e72017-04-05 13:25:04 -0700159 return true;
160}
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 Ferris60521c72017-08-18 15:10:53 -0700190 MapInfo map_info;
191 if (!InternalParseLine(line, &map_info)) {
192 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
206Maps::~Maps() {
207 for (auto& map : maps_) {
208 delete map.elf;
209 map.elf = nullptr;
210 }
211}
212
213bool BufferMaps::Parse() {
214 const char* start_of_line = buffer_;
215 do {
216 std::string line;
217 const char* end_of_line = strchr(start_of_line, '\n');
218 if (end_of_line == nullptr) {
219 line = start_of_line;
220 } else {
Christopher Ferris09385e72017-04-05 13:25:04 -0700221 line = std::string(start_of_line, end_of_line - start_of_line);
Christopher Ferris60521c72017-08-18 15:10:53 -0700222 end_of_line++;
Christopher Ferris09385e72017-04-05 13:25:04 -0700223 }
224
225 MapInfo map_info;
Christopher Ferris60521c72017-08-18 15:10:53 -0700226 if (!InternalParseLine(line.c_str(), &map_info)) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700227 return false;
228 }
229 maps_.push_back(map_info);
230
231 start_of_line = end_of_line;
232 } while (start_of_line != nullptr && *start_of_line != '\0');
233 return true;
234}
235
236const std::string RemoteMaps::GetMapsFile() const {
237 return "/proc/" + std::to_string(pid_) + "/maps";
238}
239
240bool OfflineMaps::Parse() {
241 // Format of maps information:
242 // <uint64_t> StartOffset
243 // <uint64_t> EndOffset
244 // <uint64_t> offset
245 // <uint16_t> flags
246 // <uint16_t> MapNameLength
247 // <VariableLengthValue> MapName
248 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
249 if (fd == -1) {
250 return false;
251 }
252
253 std::vector<char> name;
254 while (true) {
255 MapInfo map_info;
256 ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
257 if (bytes == 0) {
258 break;
259 }
260 if (bytes == -1 || bytes != sizeof(map_info.start)) {
261 return false;
262 }
263 bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
264 if (bytes == -1 || bytes != sizeof(map_info.end)) {
265 return false;
266 }
267 bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
268 if (bytes == -1 || bytes != sizeof(map_info.offset)) {
269 return false;
270 }
271 bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
272 if (bytes == -1 || bytes != sizeof(map_info.flags)) {
273 return false;
274 }
275 uint16_t len;
276 bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
277 if (bytes == -1 || bytes != sizeof(len)) {
278 return false;
279 }
280 if (len > 0) {
281 name.resize(len);
282 bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
283 if (bytes == -1 || bytes != len) {
284 return false;
285 }
286 map_info.name = std::string(name.data(), len);
287 }
288 maps_.push_back(map_info);
289 }
290 return true;
291}
Christopher Ferrisd226a512017-07-14 10:37:19 -0700292
293} // namespace unwindstack