• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
28 #include <cctype>
29 #include <memory>
30 #include <string>
31 #include <vector>
32 
33 #include <unwindstack/Elf.h>
34 #include <unwindstack/Maps.h>
35 #include <unwindstack/Memory.h>
36 
37 namespace unwindstack {
38 
Find(uint64_t pc)39 MapInfo* 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 
59 // Assumes that line does not end in '\n'.
InternalParseLine(const char * line)60 static MapInfo* InternalParseLine(const char* line) {
61   // Do not use a sscanf implementation since it is not performant.
62 
63   // Example linux /proc/<pid>/maps lines:
64   // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
65   char* str;
66   const char* old_str = line;
67   uint64_t start = strtoull(old_str, &str, 16);
68   if (old_str == str || *str++ != '-') {
69     return nullptr;
70   }
71 
72   old_str = str;
73   uint64_t end = strtoull(old_str, &str, 16);
74   if (old_str == str || !std::isspace(*str++)) {
75     return nullptr;
76   }
77 
78   while (std::isspace(*str)) {
79     str++;
80   }
81 
82   // Parse permissions data.
83   if (*str == '\0') {
84     return nullptr;
85   }
86   uint16_t flags = 0;
87   if (*str == 'r') {
88     flags |= PROT_READ;
89   } else if (*str != '-') {
90     return nullptr;
91   }
92   str++;
93   if (*str == 'w') {
94     flags |= PROT_WRITE;
95   } else if (*str != '-') {
96     return nullptr;
97   }
98   str++;
99   if (*str == 'x') {
100     flags |= PROT_EXEC;
101   } else if (*str != '-') {
102     return nullptr;
103   }
104   str++;
105   if (*str != 'p' && *str != 's') {
106     return nullptr;
107   }
108   str++;
109 
110   if (!std::isspace(*str++)) {
111     return nullptr;
112   }
113 
114   old_str = str;
115   uint64_t offset = strtoull(old_str, &str, 16);
116   if (old_str == str || !std::isspace(*str)) {
117     return nullptr;
118   }
119 
120   // Ignore the 00:00 values.
121   old_str = str;
122   (void)strtoull(old_str, &str, 16);
123   if (old_str == str || *str++ != ':') {
124     return nullptr;
125   }
126   if (std::isspace(*str)) {
127     return nullptr;
128   }
129 
130   // Skip the inode.
131   old_str = str;
132   (void)strtoull(str, &str, 16);
133   if (old_str == str || !std::isspace(*str++)) {
134     return nullptr;
135   }
136 
137   // Skip decimal digit.
138   old_str = str;
139   (void)strtoull(old_str, &str, 10);
140   if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
141     return nullptr;
142   }
143 
144   while (std::isspace(*str)) {
145     str++;
146   }
147   if (*str == '\0') {
148     return new MapInfo(start, end, offset, flags, "");
149   }
150 
151   // Save the name data.
152   std::string name(str);
153 
154   // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
155   if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
156     flags |= MAPS_FLAGS_DEVICE_MAP;
157   }
158   return new MapInfo(start, end, offset, flags, name);
159 }
160 
Parse()161 bool Maps::Parse() {
162   int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
163   if (fd == -1) {
164     return false;
165   }
166 
167   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;
174       break;
175     }
176     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';
188 
189       MapInfo* map_info = InternalParseLine(line);
190       if (map_info == nullptr) {
191         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;
200   }
201   close(fd);
202   return return_value;
203 }
204 
Add(uint64_t start,uint64_t end,uint64_t offset,uint64_t flags,const std::string & name,uint64_t load_bias)205 void 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 
~Maps()212 Maps::~Maps() {
213   for (auto& map : maps_) {
214     delete map;
215   }
216 }
217 
Parse()218 bool 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 {
226       line = std::string(start_of_line, end_of_line - start_of_line);
227       end_of_line++;
228     }
229 
230     MapInfo* map_info = InternalParseLine(line.c_str());
231     if (map_info == nullptr) {
232       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 
GetMapsFile() const241 const std::string RemoteMaps::GetMapsFile() const {
242   return "/proc/" + std::to_string(pid_) + "/maps";
243 }
244 
245 }  // namespace unwindstack
246