/src/perfetto/buildtools/android-unwinding/libunwindstack/GlobalDebugImpl.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2017 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 | | #pragma once |
18 | | |
19 | | #include <stdint.h> |
20 | | #include <string.h> |
21 | | #include <sys/mman.h> |
22 | | |
23 | | #include <memory> |
24 | | #include <vector> |
25 | | |
26 | | #include <unwindstack/Global.h> |
27 | | #include <unwindstack/Maps.h> |
28 | | |
29 | | #include "Check.h" |
30 | | #include "GlobalDebugInterface.h" |
31 | | #include "MemoryCache.h" |
32 | | #include "MemoryRange.h" |
33 | | |
34 | | // This implements the JIT Compilation Interface. |
35 | | // See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html |
36 | | // |
37 | | // We use it to get in-memory ELF files created by the ART compiler, |
38 | | // but we also use it to get list of DEX files used by the runtime. |
39 | | |
40 | | namespace unwindstack { |
41 | | |
42 | | // Implementation templated for ELF/DEX and for different architectures. |
43 | | template <typename Symfile, typename Uintptr_T, typename Uint64_T> |
44 | | class GlobalDebugImpl : public GlobalDebugInterface<Symfile>, public Global { |
45 | | public: |
46 | | static constexpr int kMaxRaceRetries = 16; |
47 | | static constexpr int kMaxHeadRetries = 16; |
48 | | static constexpr uint8_t kMagic[8] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', '2'}; |
49 | | |
50 | | struct JITCodeEntry { |
51 | | Uintptr_T next; |
52 | | Uintptr_T prev; |
53 | | Uintptr_T symfile_addr; |
54 | | Uint64_T symfile_size; |
55 | | // Android-specific fields: |
56 | | Uint64_T timestamp; |
57 | | uint32_t seqlock; |
58 | | }; |
59 | | |
60 | | static constexpr size_t kSizeOfCodeEntryV1 = offsetof(JITCodeEntry, timestamp); |
61 | | static constexpr size_t kSizeOfCodeEntryV2 = sizeof(JITCodeEntry); |
62 | | |
63 | | struct JITDescriptor { |
64 | | uint32_t version; |
65 | | uint32_t action_flag; |
66 | | Uintptr_T relevant_entry; |
67 | | Uintptr_T first_entry; |
68 | | // Android-specific fields: |
69 | | uint8_t magic[8]; |
70 | | uint32_t flags; |
71 | | uint32_t sizeof_descriptor; |
72 | | uint32_t sizeof_entry; |
73 | | uint32_t seqlock; |
74 | | Uint64_T timestamp; |
75 | | }; |
76 | | |
77 | | static constexpr size_t kSizeOfDescriptorV1 = offsetof(JITDescriptor, magic); |
78 | | static constexpr size_t kSizeOfDescriptorV2 = sizeof(JITDescriptor); |
79 | | |
80 | | // This uniquely identifies entry in presence of concurrent modifications. |
81 | | // Each (address,seqlock) pair is unique for each newly created JIT entry. |
82 | | struct UID { |
83 | | uint64_t address; // Address of JITCodeEntry in memory. |
84 | | uint32_t seqlock; // This servers as "version" for the given address. |
85 | | |
86 | 0 | bool operator<(const UID& other) const { |
87 | 0 | return std::tie(address, seqlock) < std::tie(other.address, other.seqlock); |
88 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID::operator<(unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID const&) const Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID::operator<(unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID const&) const Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID::operator<(unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID const&) const |
89 | | }; |
90 | | |
91 | | GlobalDebugImpl(ArchEnum arch, std::shared_ptr<Memory>& memory, |
92 | | std::vector<std::string>& search_libs, const char* global_variable_name) |
93 | 0 | : Global(memory, search_libs), global_variable_name_(global_variable_name) { |
94 | 0 | SetArch(arch); |
95 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::GlobalDebugImpl(unwindstack::ArchEnum, std::__1::shared_ptr<unwindstack::Memory>&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, char const*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::GlobalDebugImpl(unwindstack::ArchEnum, std::__1::shared_ptr<unwindstack::Memory>&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, char const*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::GlobalDebugImpl(unwindstack::ArchEnum, std::__1::shared_ptr<unwindstack::Memory>&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, char const*) |
96 | | |
97 | 0 | bool ReadDescriptor(uint64_t addr) { |
98 | 0 | JITDescriptor desc{}; |
99 | | // Try to read the full descriptor including Android-specific fields. |
100 | 0 | if (!this->memory_->ReadFully(addr, &desc, kSizeOfDescriptorV2)) { |
101 | | // Fallback to just the minimal descriptor. |
102 | | // This will make the magic check below fail. |
103 | 0 | if (!this->memory_->ReadFully(addr, &desc, kSizeOfDescriptorV1)) { |
104 | 0 | return false; |
105 | 0 | } |
106 | 0 | } |
107 | | |
108 | 0 | if (desc.version != 1 || desc.first_entry == 0) { |
109 | | // Either unknown version, or no jit entries. |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | | // Check if there are extra Android-specific fields. |
114 | 0 | if (memcmp(desc.magic, kMagic, sizeof(kMagic)) == 0) { |
115 | 0 | jit_entry_size_ = kSizeOfCodeEntryV2; |
116 | 0 | seqlock_offset_ = offsetof(JITCodeEntry, seqlock); |
117 | 0 | } else { |
118 | 0 | jit_entry_size_ = kSizeOfCodeEntryV1; |
119 | 0 | seqlock_offset_ = 0; |
120 | 0 | } |
121 | 0 | descriptor_addr_ = addr; |
122 | 0 | return true; |
123 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ReadDescriptor(unsigned long) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ReadDescriptor(unsigned long) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ReadDescriptor(unsigned long) |
124 | | |
125 | 0 | void ProcessArch() {} Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ProcessArch() Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ProcessArch() Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ProcessArch() |
126 | | |
127 | 0 | bool ReadVariableData(uint64_t ptr) { return ReadDescriptor(ptr); } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ReadVariableData(unsigned long) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ReadVariableData(unsigned long) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ReadVariableData(unsigned long) |
128 | | |
129 | | // Invoke callback for all symfiles that contain the given PC. |
130 | | // Returns true if any callback returns true (which also aborts the iteration). |
131 | | template <typename Callback /* (Symfile*) -> bool */> |
132 | 0 | bool ForEachSymfile(Maps* maps, uint64_t pc, Callback callback) { |
133 | | // Use a single lock, this object should be used so infrequently that |
134 | | // a fine grain lock is unnecessary. |
135 | 0 | std::lock_guard<std::mutex> guard(lock_); |
136 | 0 | if (descriptor_addr_ == 0) { |
137 | 0 | FindAndReadVariable(maps, global_variable_name_); |
138 | 0 | if (descriptor_addr_ == 0) { |
139 | 0 | return false; |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | // Try to find the entry in already loaded symbol files. |
144 | 0 | for (auto& it : entries_) { |
145 | 0 | Symfile* symfile = it.second.get(); |
146 | | // Check seqlock to make sure that entry is still valid (it may be very old). |
147 | 0 | if (symfile->IsValidPc(pc) && CheckSeqlock(it.first) && callback(symfile)) { |
148 | 0 | return true; |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | // Update all entries and retry. |
153 | 0 | ReadAllEntries(maps); |
154 | 0 | for (auto& it : entries_) { |
155 | 0 | Symfile* symfile = it.second.get(); |
156 | | // Note that the entry could become invalid since the ReadAllEntries above, |
157 | | // but that is ok. We don't want to fail or refresh the entries yet again. |
158 | | // This is as if we found the entry in time and it became invalid after return. |
159 | | // This is relevant when ART moves/packs JIT entries. That is, the entry is |
160 | | // technically deleted, but only because it was copied into merged uber-entry. |
161 | | // So the JIT method is still alive and the deleted data is still correct. |
162 | 0 | if (symfile->IsValidPc(pc) && callback(symfile)) { |
163 | 0 | return true; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | 0 | return false; |
168 | 0 | } Unexecuted instantiation: bool unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ForEachSymfile<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}>(unwindstack::Maps*, unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}) Unexecuted instantiation: bool unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ForEachSymfile<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}>(unwindstack::Maps*, unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}) Unexecuted instantiation: bool unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ForEachSymfile<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}>(unwindstack::Maps*, unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}) Unexecuted instantiation: bool unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ForEachSymfile<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}>(unwindstack::Maps*, unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}) Unexecuted instantiation: bool unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ForEachSymfile<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}>(unwindstack::Maps*, unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}) Unexecuted instantiation: bool unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ForEachSymfile<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}>(unwindstack::Maps*, unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}) |
169 | | |
170 | 0 | bool GetFunctionName(Maps* maps, uint64_t pc, SharedString* name, uint64_t* offset) { |
171 | | // NB: If symfiles overlap in PC ranges, this will check all of them. |
172 | 0 | return ForEachSymfile(maps, pc, [pc, name, offset](Symfile* file) { |
173 | 0 | return file->GetFunctionName(pc, name, offset); |
174 | 0 | }); Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}::operator()(unwindstack::Elf*) const Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}::operator()(unwindstack::Elf*) const Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*)::{lambda(unwindstack::Elf*)#1}::operator()(unwindstack::Elf*) const |
175 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::GetFunctionName(unwindstack::Maps*, unsigned long, unwindstack::SharedString*, unsigned long*) |
176 | | |
177 | 0 | Symfile* Find(Maps* maps, uint64_t pc) { |
178 | | // NB: If symfiles overlap in PC ranges (which can happen for both ELF and DEX), |
179 | | // this will check all of them and return one that also has a matching function. |
180 | 0 | Symfile* result = nullptr; |
181 | 0 | bool found = ForEachSymfile(maps, pc, [pc, &result](Symfile* file) { |
182 | 0 | result = file; |
183 | 0 | SharedString name; |
184 | 0 | uint64_t offset; |
185 | 0 | return file->GetFunctionName(pc, &name, &offset); |
186 | 0 | }); Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}::operator()(unwindstack::Elf*) const Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}::operator()(unwindstack::Elf*) const Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long)::{lambda(unwindstack::Elf*)#1}::operator()(unwindstack::Elf*) const |
187 | 0 | if (found) { |
188 | 0 | return result; // Found symfile with symbol that also matches the PC. |
189 | 0 | } |
190 | | // There is no matching symbol, so return any symfile for which the PC is valid. |
191 | | // This is a useful fallback for tests, which often have symfiles with no functions. |
192 | 0 | return result; |
193 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::Find(unwindstack::Maps*, unsigned long) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::Find(unwindstack::Maps*, unsigned long) |
194 | | |
195 | | // Read all entries from the process and cache them locally. |
196 | | // The linked list might be concurrently modified. We detect races and retry. |
197 | 0 | bool ReadAllEntries(Maps* maps) { |
198 | 0 | for (int i = 0; i < kMaxRaceRetries; i++) { |
199 | 0 | bool race = false; |
200 | 0 | if (!ReadAllEntries(maps, &race)) { |
201 | 0 | if (race) { |
202 | 0 | continue; // Retry due to concurrent modification of the linked list. |
203 | 0 | } |
204 | 0 | return false; // Failed to read entries. |
205 | 0 | } |
206 | 0 | return true; // Success. |
207 | 0 | } |
208 | 0 | return false; // Too many retries. |
209 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ReadAllEntries(unwindstack::Maps*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ReadAllEntries(unwindstack::Maps*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ReadAllEntries(unwindstack::Maps*) |
210 | | |
211 | | // Read all JIT entries while assuming there might be concurrent modifications. |
212 | | // If there is a race, the method will fail and the caller should retry the call. |
213 | 0 | bool ReadAllEntries(Maps* maps, bool* race) { |
214 | | // New entries might be added while we iterate over the linked list. |
215 | | // In particular, an entry could be effectively moved from end to start due to |
216 | | // the ART repacking algorithm, which groups smaller entries into a big one. |
217 | | // Therefore keep reading the most recent entries until we reach a fixed point. |
218 | 0 | std::map<UID, std::shared_ptr<Symfile>> entries; |
219 | 0 | for (size_t i = 0; i < kMaxHeadRetries; i++) { |
220 | 0 | size_t old_size = entries.size(); |
221 | 0 | if (!ReadNewEntries(maps, &entries, race)) { |
222 | 0 | return false; |
223 | 0 | } |
224 | 0 | if (entries.size() == old_size) { |
225 | 0 | entries_.swap(entries); |
226 | 0 | return true; |
227 | 0 | } |
228 | 0 | } |
229 | 0 | return false; // Too many retries. |
230 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ReadAllEntries(unwindstack::Maps*, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ReadAllEntries(unwindstack::Maps*, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ReadAllEntries(unwindstack::Maps*, bool*) |
231 | | |
232 | | // Read new JIT entries (head of linked list) until we find one that we have seen before. |
233 | | // This method uses seqlocks extensively to ensure safety in case of concurrent modifications. |
234 | 0 | bool ReadNewEntries(Maps* maps, std::map<UID, std::shared_ptr<Symfile>>* entries, bool* race) { |
235 | | // Read the address of the head entry in the linked list. |
236 | 0 | UID uid; |
237 | 0 | if (!ReadNextField(descriptor_addr_ + offsetof(JITDescriptor, first_entry), &uid, race)) { |
238 | 0 | return false; |
239 | 0 | } |
240 | | |
241 | | // Follow the linked list. |
242 | 0 | while (uid.address != 0) { |
243 | | // Check if we have reached an already cached entry (we restart from head repeatedly). |
244 | 0 | if (entries->count(uid) != 0) { |
245 | 0 | return true; |
246 | 0 | } |
247 | | |
248 | | // Read the entry. |
249 | 0 | JITCodeEntry data{}; |
250 | 0 | if (!memory_->ReadFully(uid.address, &data, jit_entry_size_)) { |
251 | 0 | return false; |
252 | 0 | } |
253 | 0 | data.symfile_addr = StripAddressTag(data.symfile_addr); |
254 | | |
255 | | // Check the seqlock to verify the symfile_addr and symfile_size. |
256 | 0 | if (!CheckSeqlock(uid, race)) { |
257 | 0 | return false; |
258 | 0 | } |
259 | | |
260 | | // Copy and load the symfile. |
261 | 0 | auto it = entries_.find(uid); |
262 | 0 | if (it != entries_.end()) { |
263 | | // The symfile was already loaded - just copy the reference. |
264 | 0 | entries->emplace(uid, it->second); |
265 | 0 | } else if (data.symfile_addr != 0) { |
266 | 0 | std::shared_ptr<Symfile> symfile; |
267 | 0 | bool ok = this->Load(maps, memory_, data.symfile_addr, data.symfile_size.value, symfile); |
268 | | // Check seqlock first because load can fail due to race (so we want to trigger retry). |
269 | | // TODO: Extract the memory copy code before the load, so that it is immune to races. |
270 | 0 | if (!CheckSeqlock(uid, race)) { |
271 | 0 | return false; // The ELF/DEX data was removed before we loaded it. |
272 | 0 | } |
273 | | // Exclude symbol files that fail to load (but continue loading other files). |
274 | 0 | if (ok) { |
275 | 0 | entries->emplace(uid, symfile); |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | // Go to next entry. |
280 | 0 | UID next_uid; |
281 | 0 | if (!ReadNextField(uid.address + offsetof(JITCodeEntry, next), &next_uid, race)) { |
282 | 0 | return false; // The next pointer was modified while we were reading it. |
283 | 0 | } |
284 | 0 | if (!CheckSeqlock(uid, race)) { |
285 | 0 | return false; // This entry was deleted before we moved to the next one. |
286 | 0 | } |
287 | 0 | uid = next_uid; |
288 | 0 | } |
289 | | |
290 | 0 | return true; |
291 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ReadNewEntries(unwindstack::Maps*, std::__1::map<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID, std::__1::shared_ptr<unwindstack::Elf>, std::__1::less<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID>, std::__1::allocator<std::__1::pair<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID const, std::__1::shared_ptr<unwindstack::Elf> > > >*, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ReadNewEntries(unwindstack::Maps*, std::__1::map<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID, std::__1::shared_ptr<unwindstack::Elf>, std::__1::less<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID>, std::__1::allocator<std::__1::pair<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID const, std::__1::shared_ptr<unwindstack::Elf> > > >*, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ReadNewEntries(unwindstack::Maps*, std::__1::map<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID, std::__1::shared_ptr<unwindstack::Elf>, std::__1::less<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID>, std::__1::allocator<std::__1::pair<unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID const, std::__1::shared_ptr<unwindstack::Elf> > > >*, bool*) |
292 | | |
293 | | // Read the address and seqlock of entry from the next field of linked list. |
294 | | // This is non-trivial since they need to be consistent (as if we read both atomically). |
295 | | // |
296 | | // We're reading pointers, which can point at heap-allocated structures (the |
297 | | // case for the __dex_debug_descriptor pointers at the time of writing). |
298 | | // On 64 bit systems, the target process might have top-byte heap pointer |
299 | | // tagging enabled, so we need to mask out the tag. We also know that the |
300 | | // address must point to userspace, so the top byte of the address must be |
301 | | // zero on both x64 and aarch64 without tagging. Therefore the masking can be |
302 | | // done unconditionally. |
303 | 0 | bool ReadNextField(uint64_t next_field_addr, UID* uid, bool* race) { |
304 | 0 | Uintptr_T address[2]{0, 0}; |
305 | 0 | uint32_t seqlock[2]{0, 0}; |
306 | | // Read all data twice: address[0], seqlock[0], address[1], seqlock[1]. |
307 | 0 | for (int i = 0; i < 2; i++) { |
308 | 0 | std::atomic_thread_fence(std::memory_order_acquire); |
309 | 0 | if (!(memory_->ReadFully(next_field_addr, &address[i], sizeof(address[i])))) { |
310 | 0 | return false; |
311 | 0 | } |
312 | 0 | address[i] = StripAddressTag(address[i]); |
313 | 0 | if (seqlock_offset_ == 0) { |
314 | | // There is no seqlock field. |
315 | 0 | *uid = UID{.address = address[0], .seqlock = 0}; |
316 | 0 | return true; |
317 | 0 | } |
318 | 0 | if (address[i] != 0) { |
319 | 0 | std::atomic_thread_fence(std::memory_order_acquire); |
320 | 0 | if (!memory_->ReadFully(address[i] + seqlock_offset_, &seqlock[i], sizeof(seqlock[i]))) { |
321 | 0 | return false; |
322 | 0 | } |
323 | 0 | } |
324 | 0 | } |
325 | | // Check that both reads returned identical values, and that the entry is live. |
326 | 0 | if (address[0] != address[1] || seqlock[0] != seqlock[1] || (seqlock[0] & 1) == 1) { |
327 | 0 | *race = true; |
328 | 0 | return false; |
329 | 0 | } |
330 | | // Since address[1] is sandwiched between two seqlock reads, we know that |
331 | | // at the time of address[1] read, the entry had the given seqlock value. |
332 | 0 | *uid = UID{.address = address[1], .seqlock = seqlock[1]}; |
333 | 0 | return true; |
334 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::ReadNextField(unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID*, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::ReadNextField(unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID*, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::ReadNextField(unsigned long, unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID*, bool*) |
335 | | |
336 | | // Check that the given entry has not been deleted (or replaced by new entry at same address). |
337 | 0 | bool CheckSeqlock(UID uid, bool* race = nullptr) { |
338 | 0 | if (seqlock_offset_ == 0) { |
339 | | // There is no seqlock field. |
340 | 0 | return true; |
341 | 0 | } |
342 | | // This is required for memory synchronization if the we are working with local memory. |
343 | | // For other types of memory (e.g. remote) this is no-op and has no significant effect. |
344 | 0 | std::atomic_thread_fence(std::memory_order_acquire); |
345 | 0 | uint32_t seen_seqlock; |
346 | 0 | if (!memory_->Read32(uid.address + seqlock_offset_, &seen_seqlock)) { |
347 | 0 | return false; |
348 | 0 | } |
349 | 0 | if (seen_seqlock != uid.seqlock) { |
350 | 0 | if (race != nullptr) { |
351 | 0 | *race = true; |
352 | 0 | } |
353 | 0 | return false; |
354 | 0 | } |
355 | 0 | return true; |
356 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::CheckSeqlock(unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::UID, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::CheckSeqlock(unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::UID, bool*) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::CheckSeqlock(unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::UID, bool*) |
357 | | |
358 | | // AArch64 has Address tagging (aka Top Byte Ignore) feature, which is used by |
359 | | // HWASAN and MTE to store metadata in the address. We need to remove the tag. |
360 | 0 | Uintptr_T StripAddressTag(Uintptr_T addr) { |
361 | 0 | if (arch() == ARCH_ARM64) { |
362 | | // Make the value signed so it will be sign extended if necessary. |
363 | 0 | return static_cast<Uintptr_T>((static_cast<int64_t>(addr) << 8) >> 8); |
364 | 0 | } |
365 | 0 | return addr; |
366 | 0 | } Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_P>::StripAddressTag(unsigned int) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned int, unwindstack::Uint64_A>::StripAddressTag(unsigned int) Unexecuted instantiation: unwindstack::GlobalDebugImpl<unwindstack::Elf, unsigned long, unwindstack::Uint64_A>::StripAddressTag(unsigned long) |
367 | | |
368 | | private: |
369 | | const char* global_variable_name_ = nullptr; |
370 | | uint64_t descriptor_addr_ = 0; // Non-zero if we have found (non-empty) descriptor. |
371 | | uint32_t jit_entry_size_ = 0; |
372 | | uint32_t seqlock_offset_ = 0; |
373 | | std::map<UID, std::shared_ptr<Symfile>> entries_; // Cached loaded entries. |
374 | | |
375 | | std::mutex lock_; |
376 | | }; |
377 | | |
378 | | // uint64_t values on x86 are not naturally aligned, |
379 | | // but uint64_t values on ARM are naturally aligned. |
380 | | struct Uint64_P { |
381 | | uint64_t value; |
382 | | } __attribute__((packed)); |
383 | | struct Uint64_A { |
384 | | uint64_t value; |
385 | | } __attribute__((aligned(8))); |
386 | | |
387 | | template <typename Symfile> |
388 | | std::unique_ptr<GlobalDebugInterface<Symfile>> CreateGlobalDebugImpl( |
389 | | ArchEnum arch, std::shared_ptr<Memory>& memory, std::vector<std::string> search_libs, |
390 | 0 | const char* global_variable_name) { |
391 | 0 | CHECK(arch != ARCH_UNKNOWN); |
392 | | |
393 | | // The interface needs to see real-time changes in memory for synchronization with the |
394 | | // concurrently running ART JIT compiler. Skip caching and read the memory directly. |
395 | 0 | std::shared_ptr<Memory> jit_memory; |
396 | 0 | MemoryCacheBase* cached_memory = memory->AsMemoryCacheBase(); |
397 | 0 | if (cached_memory != nullptr) { |
398 | 0 | jit_memory = cached_memory->UnderlyingMemory(); |
399 | 0 | } else { |
400 | 0 | jit_memory = memory; |
401 | 0 | } |
402 | |
|
403 | 0 | switch (arch) { |
404 | 0 | case ARCH_X86: { |
405 | 0 | using Impl = GlobalDebugImpl<Symfile, uint32_t, Uint64_P>; |
406 | 0 | static_assert(offsetof(typename Impl::JITCodeEntry, symfile_size) == 12, "layout"); |
407 | 0 | static_assert(offsetof(typename Impl::JITCodeEntry, seqlock) == 28, "layout"); |
408 | 0 | static_assert(sizeof(typename Impl::JITCodeEntry) == 32, "layout"); |
409 | 0 | static_assert(sizeof(typename Impl::JITDescriptor) == 48, "layout"); |
410 | 0 | return std::make_unique<Impl>(arch, jit_memory, search_libs, global_variable_name); |
411 | 0 | } |
412 | 0 | case ARCH_ARM: { |
413 | 0 | using Impl = GlobalDebugImpl<Symfile, uint32_t, Uint64_A>; |
414 | 0 | static_assert(offsetof(typename Impl::JITCodeEntry, symfile_size) == 16, "layout"); |
415 | 0 | static_assert(offsetof(typename Impl::JITCodeEntry, seqlock) == 32, "layout"); |
416 | 0 | static_assert(sizeof(typename Impl::JITCodeEntry) == 40, "layout"); |
417 | 0 | static_assert(sizeof(typename Impl::JITDescriptor) == 48, "layout"); |
418 | 0 | return std::make_unique<Impl>(arch, jit_memory, search_libs, global_variable_name); |
419 | 0 | } |
420 | 0 | case ARCH_ARM64: |
421 | 0 | case ARCH_X86_64: |
422 | 0 | case ARCH_RISCV64: { |
423 | 0 | using Impl = GlobalDebugImpl<Symfile, uint64_t, Uint64_A>; |
424 | 0 | static_assert(offsetof(typename Impl::JITCodeEntry, symfile_size) == 24, "layout"); |
425 | 0 | static_assert(offsetof(typename Impl::JITCodeEntry, seqlock) == 40, "layout"); |
426 | 0 | static_assert(sizeof(typename Impl::JITCodeEntry) == 48, "layout"); |
427 | 0 | static_assert(sizeof(typename Impl::JITDescriptor) == 56, "layout"); |
428 | 0 | return std::make_unique<Impl>(arch, jit_memory, search_libs, global_variable_name); |
429 | 0 | } |
430 | 0 | default: |
431 | 0 | abort(); |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | } // namespace unwindstack |