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
Reparse(bool * any_changed)170 bool LocalUpdatableMaps::Reparse(/*out*/ bool* any_changed) {
171 // New maps will be added at the end without deleting the old ones.
172 size_t last_map_idx = maps_.size();
173 if (!Maps::Parse()) {
174 maps_.resize(last_map_idx);
175 return false;
176 }
177
178 size_t search_map_idx = 0;
179 size_t num_deleted_old_entries = 0;
180 size_t num_deleted_new_entries = 0;
181 for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
182 auto& new_map_info = maps_[new_map_idx];
183 uint64_t start = new_map_info->start();
184 uint64_t end = new_map_info->end();
185 uint64_t flags = new_map_info->flags();
186 const SharedString& name = new_map_info->name();
187 for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
188 auto& info = maps_[old_map_idx];
189 if (start == info->start() && end == info->end() && flags == info->flags() &&
190 name == info->name()) {
191 search_map_idx = old_map_idx + 1;
192 // Since we are throwing away a map from the new list, need to
193 // adjust the next/prev pointers in the old map entry.
194 auto prev = new_map_info->prev_map();
195 auto next = new_map_info->next_map();
196 info->set_prev_map(prev);
197 info->set_next_map(next);
198
199 // Fix up the pointers in the prev and next entries.
200 if (prev != nullptr) {
201 prev->set_next_map(info);
202 }
203 if (next != nullptr) {
204 next->set_prev_map(info);
205 }
206
207 maps_[new_map_idx] = nullptr;
208 num_deleted_new_entries++;
209 break;
210 } else if (info->start() > start) {
211 // Stop, there isn't going to be a match.
212 search_map_idx = old_map_idx;
213 break;
214 }
215
216 // Never delete these maps, they may be in use. The assumption is
217 // that there will only every be a handful of these so waiting
218 // to destroy them is not too expensive.
219 // Since these are all shared_ptrs, we can just remove the references.
220 // Any code still holding on to the pointer, will still have a
221 // valid pointer after this.
222 search_map_idx = old_map_idx + 1;
223 maps_[old_map_idx] = nullptr;
224 num_deleted_old_entries++;
225 }
226 if (search_map_idx >= last_map_idx) {
227 break;
228 }
229 }
230
231 for (size_t i = search_map_idx; i < last_map_idx; i++) {
232 maps_[i] = nullptr;
233 num_deleted_old_entries++;
234 }
235
236 // Sort all of the values such that the nullptrs wind up at the end, then
237 // resize them away.
238 std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
239 if (a == nullptr) {
240 return false;
241 } else if (b == nullptr) {
242 return true;
243 }
244 return a->start() < b->start();
245 });
246 maps_.resize(maps_.size() - num_deleted_old_entries - num_deleted_new_entries);
247
248 if (any_changed != nullptr) {
249 *any_changed = num_deleted_old_entries != 0 || maps_.size() != last_map_idx;
250 }
251
252 return true;
253 }
254
255 } // namespace unwindstack
256