blob: 4c16212842db9dbbab62841cdfe6f9223d0800be [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
Christopher Ferrise7b66242017-12-15 11:17:45 -0800205void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
206 const std::string& name, uint64_t load_bias) {
207 MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
208 map_info->load_bias = load_bias;
209 maps_.push_back(map_info);
210}
211
Christopher Ferris09385e72017-04-05 13:25:04 -0700212Maps::~Maps() {
213 for (auto& map : maps_) {
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800214 delete map;
Christopher Ferris09385e72017-04-05 13:25:04 -0700215 }
216}
217
218bool BufferMaps::Parse() {
219 const char* start_of_line = buffer_;
220 do {
221 std::string line;
222 const char* end_of_line = strchr(start_of_line, '\n');
223 if (end_of_line == nullptr) {
224 line = start_of_line;
225 } else {
Christopher Ferris09385e72017-04-05 13:25:04 -0700226 line = std::string(start_of_line, end_of_line - start_of_line);
Christopher Ferris60521c72017-08-18 15:10:53 -0700227 end_of_line++;
Christopher Ferris09385e72017-04-05 13:25:04 -0700228 }
229
Christopher Ferrisbe788d82017-11-27 14:50:38 -0800230 MapInfo* map_info = InternalParseLine(line.c_str());
231 if (map_info == nullptr) {
Christopher Ferris09385e72017-04-05 13:25:04 -0700232 return false;
233 }
234 maps_.push_back(map_info);
235
236 start_of_line = end_of_line;
237 } while (start_of_line != nullptr && *start_of_line != '\0');
238 return true;
239}
240
241const std::string RemoteMaps::GetMapsFile() const {
242 return "/proc/" + std::to_string(pid_) + "/maps";
243}
244
Christopher Ferrisd226a512017-07-14 10:37:19 -0700245} // namespace unwindstack