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 <string.h>
23 #include <sys/mman.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <android-base/unique_fd.h>
28 #include <procinfo/process_map.h>
29
30 #include <algorithm>
31 #include <cctype>
32 #include <memory>
33 #include <string>
34 #include <vector>
35
36 #include <unwindstack/Elf.h>
37 #include <unwindstack/Maps.h>
38 #include <unwindstack/Memory.h>
39
40 namespace unwindstack {
41
Find(uint64_t pc)42 std::shared_ptr<MapInfo> Maps::Find(uint64_t pc) {
43 if (maps_.empty()) {
44 return nullptr;
45 }
46 size_t first = 0;
47 size_t last = maps_.size();
48 while (first < last) {
49 size_t index = (first + last) / 2;
50 const auto& cur = maps_[index];
51 if (pc >= cur->start() && pc < cur->end()) {
52 return cur;
53 } else if (pc < cur->start()) {
54 last = index;
55 } else {
56 first = index + 1;
57 }
58 }
59 return nullptr;
60 }
61
Parse()62 bool Maps::Parse() {
63 std::shared_ptr<MapInfo> prev_map;
64 return android::procinfo::ReadMapFile(GetMapsFile(),
65 [&](const android::procinfo::MapInfo& mapinfo) {
66 // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
67 auto flags = mapinfo.flags;
68 if (strncmp(mapinfo.name.c_str(), "/dev/", 5) == 0 &&
69 strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
70 flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
71 }
72 maps_.emplace_back(
73 MapInfo::Create(prev_map, mapinfo.start, mapinfo.end, mapinfo.pgoff, flags, mapinfo.name));
74 prev_map = maps_.back();
75 });
76 }
77
Add(uint64_t start,uint64_t end,uint64_t offset,uint64_t flags,const std::string & name)78 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
79 const std::string& name) {
80 std::shared_ptr<MapInfo> prev_map(maps_.empty() ? nullptr : maps_.back());
81 auto map_info = MapInfo::Create(prev_map, start, end, offset, flags, name);
82 maps_.emplace_back(std::move(map_info));
83 }
84
Add(uint64_t start,uint64_t end,uint64_t offset,uint64_t flags,const std::string & name,uint64_t load_bias)85 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
86 const std::string& name, uint64_t load_bias) {
87 std::shared_ptr<MapInfo> prev_map(maps_.empty() ? nullptr : maps_.back());
88 auto map_info = MapInfo::Create(prev_map, start, end, offset, flags, name);
89 map_info->set_load_bias(load_bias);
90 maps_.emplace_back(std::move(map_info));
91 }
92
Sort()93 void Maps::Sort() {
94 if (maps_.empty()) {
95 return;
96 }
97
98 std::sort(maps_.begin(), maps_.end(),
99 [](const std::shared_ptr<MapInfo>& a, const std::shared_ptr<MapInfo>& b) {
100 return a->start() < b->start();
101 });
102
103 // Set prev_map and next_map on the info objects.
104 std::shared_ptr<MapInfo> prev_map;
105 // Set the last next_map to nullptr.
106 maps_.back()->set_next_map(prev_map);
107 for (auto& map_info : maps_) {
108 map_info->set_prev_map(prev_map);
109 if (prev_map) {
110 prev_map->set_next_map(map_info);
111 }
112 prev_map = map_info;
113 }
114 }
115
Parse()116 bool BufferMaps::Parse() {
117 std::string content(buffer_);
118 std::shared_ptr<MapInfo> prev_map;
119 return android::procinfo::ReadMapFileContent(
120 &content[0], [&](const android::procinfo::MapInfo& mapinfo) {
121 // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
122 auto flags = mapinfo.flags;
123 if (strncmp(mapinfo.name.c_str(), "/dev/", 5) == 0 &&
124 strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
125 flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
126 }
127 maps_.emplace_back(MapInfo::Create(prev_map, mapinfo.start, mapinfo.end, mapinfo.pgoff,
128 flags, mapinfo.name));
129 prev_map = maps_.back();
130 });
131 }
132
GetMapsFile() const133 const std::string RemoteMaps::GetMapsFile() const {
134 return "/proc/" + std::to_string(pid_) + "/maps";
135 }
136
GetMapsFile() const137 const std::string LocalUpdatableMaps::GetMapsFile() const {
138 return "/proc/self/maps";
139 }
140
LocalUpdatableMaps()141 LocalUpdatableMaps::LocalUpdatableMaps() : Maps() {
142 pthread_rwlock_init(&maps_rwlock_, nullptr);
143 }
144
Find(uint64_t pc)145 std::shared_ptr<MapInfo> LocalUpdatableMaps::Find(uint64_t pc) {
146 pthread_rwlock_rdlock(&maps_rwlock_);
147 std::shared_ptr<MapInfo> map_info = Maps::Find(pc);
148 pthread_rwlock_unlock(&maps_rwlock_);
149
150 if (map_info == nullptr) {
151 pthread_rwlock_wrlock(&maps_rwlock_);
152 // This is guaranteed not to invalidate any previous MapInfo objects so
153 // we don't need to worry about any MapInfo* values already in use.
154 if (Reparse()) {
155 map_info = Maps::Find(pc);
156 }
157 pthread_rwlock_unlock(&maps_rwlock_);
158 }
159
160 return map_info;
161 }
162
Parse()163 bool LocalUpdatableMaps::Parse() {
164 pthread_rwlock_wrlock(&maps_rwlock_);
165 bool parsed = Maps::Parse();
166 pthread_rwlock_unlock(&maps_rwlock_);
167 return parsed;
168 }
169
ForEachMapInfo(std::function<bool (MapInfo *)> const & find_var)170 void LocalUpdatableMaps::ForEachMapInfo(std::function<bool(MapInfo*)> const& find_var) {
171 pthread_rwlock_rdlock(&maps_rwlock_);
172 for (const auto& info : maps_) {
173 if (!find_var(info.get())) break;
174 }
175 pthread_rwlock_unlock(&maps_rwlock_);
176 }
177
Reparse(bool * any_changed)178 bool LocalUpdatableMaps::Reparse(/*out*/ bool* any_changed) {
179 // New maps will be added at the end without deleting the old ones.
180 size_t last_map_idx = maps_.size();
181 if (!Maps::Parse()) {
182 maps_.resize(last_map_idx);
183 return false;
184 }
185
186 size_t search_map_idx = 0;
187 size_t num_deleted_old_entries = 0;
188 size_t num_deleted_new_entries = 0;
189 for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
190 auto& new_map_info = maps_[new_map_idx];
191 uint64_t start = new_map_info->start();
192 uint64_t end = new_map_info->end();
193 uint64_t flags = new_map_info->flags();
194 const SharedString& name = new_map_info->name();
195 for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
196 auto& info = maps_[old_map_idx];
197 if (start == info->start() && end == info->end() && flags == info->flags() &&
198 name == info->name()) {
199 search_map_idx = old_map_idx + 1;
200 // Since we are throwing away a map from the new list, need to
201 // adjust the next/prev pointers in the old map entry.
202 auto prev = new_map_info->prev_map();
203 auto next = new_map_info->next_map();
204 info->set_prev_map(prev);
205 info->set_next_map(next);
206
207 // Fix up the pointers in the prev and next entries.
208 if (prev != nullptr) {
209 prev->set_next_map(info);
210 }
211 if (next != nullptr) {
212 next->set_prev_map(info);
213 }
214
215 maps_[new_map_idx] = nullptr;
216 num_deleted_new_entries++;
217 break;
218 } else if (info->start() > start) {
219 // Stop, there isn't going to be a match.
220 search_map_idx = old_map_idx;
221 break;
222 }
223
224 // Never delete these maps, they may be in use. The assumption is
225 // that there will only every be a handful of these so waiting
226 // to destroy them is not too expensive.
227 // Since these are all shared_ptrs, we can just remove the references.
228 // Any code still holding on to the pointer, will still have a
229 // valid pointer after this.
230 search_map_idx = old_map_idx + 1;
231 maps_[old_map_idx] = nullptr;
232 num_deleted_old_entries++;
233 }
234 if (search_map_idx >= last_map_idx) {
235 break;
236 }
237 }
238
239 for (size_t i = search_map_idx; i < last_map_idx; i++) {
240 maps_[i] = nullptr;
241 num_deleted_old_entries++;
242 }
243
244 // Sort all of the values such that the nullptrs wind up at the end, then
245 // resize them away.
246 std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
247 if (a == nullptr) {
248 return false;
249 } else if (b == nullptr) {
250 return true;
251 }
252 return a->start() < b->start();
253 });
254 maps_.resize(maps_.size() - num_deleted_old_entries - num_deleted_new_entries);
255
256 if (any_changed != nullptr) {
257 *any_changed = num_deleted_old_entries != 0 || maps_.size() != last_map_idx;
258 }
259
260 return true;
261 }
262
263 } // namespace unwindstack
264