Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 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 | #include <stdint.h> |
| 18 | #include <sys/mman.h> |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 19 | #include <cstddef> |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 20 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 21 | #include <atomic> |
| 22 | #include <deque> |
| 23 | #include <map> |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 24 | #include <memory> |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 25 | #include <unordered_set> |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 26 | #include <vector> |
| 27 | |
| 28 | #include <unwindstack/Elf.h> |
| 29 | #include <unwindstack/JitDebug.h> |
| 30 | #include <unwindstack/Maps.h> |
| 31 | #include <unwindstack/Memory.h> |
| 32 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 33 | #if !defined(NO_LIBDEXFILE_SUPPORT) |
| 34 | #include <DexFile.h> |
| 35 | #endif |
| 36 | |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 37 | // This implements the JIT Compilation Interface. |
| 38 | // See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html |
| 39 | |
| 40 | namespace unwindstack { |
| 41 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 42 | // 32-bit platforms may differ in alignment of uint64_t. |
| 43 | struct Uint64_P { |
| 44 | uint64_t value; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 45 | } __attribute__((packed)); |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 46 | struct Uint64_A { |
| 47 | uint64_t value; |
| 48 | } __attribute__((aligned(8))); |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 49 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 50 | // Wrapper around other memory object which protects us against data races. |
| 51 | // It will check seqlock after every read, and fail if the seqlock changed. |
| 52 | // This ensues that the read memory has not been partially modified. |
| 53 | struct JitMemory : public Memory { |
| 54 | size_t Read(uint64_t addr, void* dst, size_t size) override; |
| 55 | |
| 56 | Memory* parent_ = nullptr; |
| 57 | uint64_t seqlock_addr_ = 0; |
| 58 | uint32_t expected_seqlock_ = 0; |
| 59 | bool failed_due_to_race_ = false; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 60 | }; |
| 61 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 62 | template <typename Symfile> |
| 63 | struct JitCacheEntry { |
| 64 | // PC memory range described by this entry. |
| 65 | uint64_t addr_ = 0; |
| 66 | uint64_t size_ = 0; |
| 67 | std::unique_ptr<Symfile> symfile_; |
| 68 | |
| 69 | bool Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t size); |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 70 | }; |
| 71 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 72 | template <typename Symfile, typename PointerT, typename Uint64_T> |
| 73 | class JitDebugImpl : public JitDebug<Symfile>, public Global { |
| 74 | public: |
| 75 | static constexpr const char* kDescriptorExtMagic = "Android1"; |
| 76 | static constexpr int kMaxRaceRetries = 16; |
| 77 | |
| 78 | struct JITCodeEntry { |
| 79 | PointerT next; |
| 80 | PointerT prev; |
| 81 | PointerT symfile_addr; |
| 82 | Uint64_T symfile_size; |
| 83 | }; |
| 84 | |
| 85 | struct JITDescriptor { |
| 86 | uint32_t version; |
| 87 | uint32_t action_flag; |
| 88 | PointerT relevant_entry; |
| 89 | PointerT first_entry; |
| 90 | }; |
| 91 | |
| 92 | // Android-specific extensions. |
| 93 | struct JITDescriptorExt { |
| 94 | JITDescriptor desc; |
| 95 | uint8_t magic[8]; |
| 96 | uint32_t flags; |
| 97 | uint32_t sizeof_descriptor; |
| 98 | uint32_t sizeof_entry; |
| 99 | uint32_t action_seqlock; |
| 100 | uint64_t action_timestamp; |
| 101 | }; |
| 102 | |
| 103 | JitDebugImpl(ArchEnum arch, std::shared_ptr<Memory>& memory, |
| 104 | std::vector<std::string>& search_libs) |
| 105 | : Global(memory, search_libs) { |
| 106 | SetArch(arch); |
| 107 | } |
| 108 | |
| 109 | Symfile* Get(Maps* maps, uint64_t pc) override; |
| 110 | virtual bool ReadVariableData(uint64_t offset); |
| 111 | virtual void ProcessArch() {} |
| 112 | bool Update(Maps* maps); |
| 113 | bool Read(Maps* maps, JitMemory* memory); |
| 114 | |
| 115 | bool initialized_ = false; |
| 116 | uint64_t descriptor_addr_ = 0; // Non-zero if we have found (non-empty) descriptor. |
| 117 | uint64_t seqlock_addr_ = 0; // Re-read entries if the value at this address changes. |
| 118 | uint32_t last_seqlock_ = ~0u; // The value of seqlock when we last read the entries. |
| 119 | |
| 120 | std::deque<JitCacheEntry<Symfile>> entries_; |
| 121 | |
| 122 | std::mutex lock_; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 123 | }; |
| 124 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 125 | template <typename Symfile> |
| 126 | std::unique_ptr<JitDebug<Symfile>> JitDebug<Symfile>::Create(ArchEnum arch, |
| 127 | std::shared_ptr<Memory>& memory, |
| 128 | std::vector<std::string> search_libs) { |
| 129 | typedef JitDebugImpl<Symfile, uint32_t, Uint64_P> JitDebugImpl32P; |
| 130 | typedef JitDebugImpl<Symfile, uint32_t, Uint64_A> JitDebugImpl32A; |
| 131 | typedef JitDebugImpl<Symfile, uint64_t, Uint64_A> JitDebugImpl64A; |
| 132 | switch (arch) { |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 133 | case ARCH_X86: |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 134 | static_assert(sizeof(typename JitDebugImpl32P::JITCodeEntry) == 20, "layout"); |
| 135 | static_assert(sizeof(typename JitDebugImpl32P::JITDescriptor) == 16, "layout"); |
| 136 | static_assert(sizeof(typename JitDebugImpl32P::JITDescriptorExt) == 48, "layout"); |
| 137 | return std::unique_ptr<JitDebug>(new JitDebugImpl32P(arch, memory, search_libs)); |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 138 | break; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 139 | case ARCH_ARM: |
| 140 | case ARCH_MIPS: |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 141 | static_assert(sizeof(typename JitDebugImpl32A::JITCodeEntry) == 24, "layout"); |
| 142 | static_assert(sizeof(typename JitDebugImpl32A::JITDescriptor) == 16, "layout"); |
| 143 | static_assert(sizeof(typename JitDebugImpl32A::JITDescriptorExt) == 48, "layout"); |
| 144 | return std::unique_ptr<JitDebug>(new JitDebugImpl32A(arch, memory, search_libs)); |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 145 | break; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 146 | case ARCH_ARM64: |
| 147 | case ARCH_X86_64: |
| 148 | case ARCH_MIPS64: |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 149 | static_assert(sizeof(typename JitDebugImpl64A::JITCodeEntry) == 32, "layout"); |
| 150 | static_assert(sizeof(typename JitDebugImpl64A::JITDescriptor) == 24, "layout"); |
| 151 | static_assert(sizeof(typename JitDebugImpl64A::JITDescriptorExt) == 56, "layout"); |
| 152 | return std::unique_ptr<JitDebug>(new JitDebugImpl64A(arch, memory, search_libs)); |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 153 | break; |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 154 | default: |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 155 | abort(); |
| 156 | } |
| 157 | } |
| 158 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 159 | size_t JitMemory::Read(uint64_t addr, void* dst, size_t size) { |
| 160 | if (!parent_->ReadFully(addr, dst, size)) { |
| 161 | return 0; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 162 | } |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 163 | // This is required for memory synchronization if the we are working with local memory. |
| 164 | // For other types of memory (e.g. remote) this is no-op and has no significant effect. |
| 165 | std::atomic_thread_fence(std::memory_order_acquire); |
| 166 | uint32_t seen_seqlock; |
| 167 | if (!parent_->Read32(seqlock_addr_, &seen_seqlock)) { |
| 168 | return 0; |
| 169 | } |
| 170 | if (seen_seqlock != expected_seqlock_) { |
| 171 | failed_due_to_race_ = true; |
| 172 | return 0; |
| 173 | } |
| 174 | return size; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 175 | } |
| 176 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 177 | template <typename Symfile, typename PointerT, typename Uint64_T> |
| 178 | bool JitDebugImpl<Symfile, PointerT, Uint64_T>::ReadVariableData(uint64_t addr) { |
| 179 | JITDescriptor desc; |
| 180 | if (!this->memory_->ReadFully(addr, &desc, sizeof(desc))) { |
| 181 | return false; |
| 182 | } |
| 183 | if (desc.version != 1) { |
| 184 | return false; |
| 185 | } |
| 186 | if (desc.first_entry == 0) { |
| 187 | return false; // There could be multiple descriptors. Ignore empty ones. |
| 188 | } |
| 189 | descriptor_addr_ = addr; |
| 190 | JITDescriptorExt desc_ext; |
| 191 | if (this->memory_->ReadFully(addr, &desc_ext, sizeof(desc_ext)) && |
| 192 | memcmp(desc_ext.magic, kDescriptorExtMagic, 8) == 0) { |
| 193 | seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptorExt, action_seqlock); |
| 194 | } else { |
| 195 | // In the absence of Android-specific fields, use the head pointer instead. |
| 196 | seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptor, first_entry); |
| 197 | } |
| 198 | return true; |
| 199 | } |
| 200 | |
| 201 | template <typename Symfile> |
| 202 | static const char* GetDescriptorName(); |
| 203 | |
| 204 | template <> |
| 205 | const char* GetDescriptorName<Elf>() { |
| 206 | return "__jit_debug_descriptor"; |
| 207 | } |
| 208 | |
| 209 | template <typename Symfile, typename PointerT, typename Uint64_T> |
| 210 | Symfile* JitDebugImpl<Symfile, PointerT, Uint64_T>::Get(Maps* maps, uint64_t pc) { |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 211 | std::lock_guard<std::mutex> guard(lock_); |
| 212 | if (!initialized_) { |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 213 | FindAndReadVariable(maps, GetDescriptorName<Symfile>()); |
| 214 | initialized_ = true; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 215 | } |
| 216 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 217 | if (descriptor_addr_ == 0) { |
| 218 | return nullptr; |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 219 | } |
| 220 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 221 | if (!Update(maps)) { |
| 222 | return nullptr; |
| 223 | } |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 224 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 225 | Symfile* fallback = nullptr; |
| 226 | for (auto& entry : entries_) { |
| 227 | // Skip entries which are obviously not relevant (if we know the PC range). |
| 228 | if (entry.size_ == 0 || (entry.addr_ <= pc && (pc - entry.addr_) < entry.size_)) { |
| 229 | // Double check the entry contains the PC in case there are overlapping entries. |
| 230 | // This is might happen for native-code due to GC and for DEX due to data sharing. |
| 231 | std::string method_name; |
| 232 | uint64_t method_offset; |
| 233 | if (entry.symfile_->GetFunctionName(pc, &method_name, &method_offset)) { |
| 234 | return entry.symfile_.get(); |
| 235 | } |
| 236 | fallback = entry.symfile_.get(); // Tests don't have any symbols. |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 237 | } |
| 238 | } |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 239 | return fallback; // Not found. |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 240 | } |
| 241 | |
David Srbecky | 85b5fec | 2018-02-23 18:06:13 +0000 | [diff] [blame^] | 242 | // Update JIT entries if needed. It will retry if there are data races. |
| 243 | template <typename Symfile, typename PointerT, typename Uint64_T> |
| 244 | bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Update(Maps* maps) { |
| 245 | // We might need to retry the whole read in the presence of data races. |
| 246 | for (int i = 0; i < kMaxRaceRetries; i++) { |
| 247 | // Read the seqlock (counter which is incremented before and after any modification). |
| 248 | uint32_t seqlock = 0; |
| 249 | if (!this->memory_->Read32(seqlock_addr_, &seqlock)) { |
| 250 | return false; // Failed to read seqlock. |
| 251 | } |
| 252 | |
| 253 | // Check if anything changed since the last time we checked. |
| 254 | if (last_seqlock_ != seqlock) { |
| 255 | // Create memory wrapper to allow us to read the entries safely even in a live process. |
| 256 | JitMemory safe_memory; |
| 257 | safe_memory.parent_ = this->memory_.get(); |
| 258 | safe_memory.seqlock_addr_ = seqlock_addr_; |
| 259 | safe_memory.expected_seqlock_ = seqlock; |
| 260 | std::atomic_thread_fence(std::memory_order_acquire); |
| 261 | |
| 262 | // Add all entries to our cache. |
| 263 | if (!Read(maps, &safe_memory)) { |
| 264 | if (safe_memory.failed_due_to_race_) { |
| 265 | sleep(0); |
| 266 | continue; // Try again (there was a data race). |
| 267 | } else { |
| 268 | return false; // Proper failure (we could not read the data). |
| 269 | } |
| 270 | } |
| 271 | last_seqlock_ = seqlock; |
| 272 | } |
| 273 | return true; |
| 274 | } |
| 275 | return false; // Too many retries. |
| 276 | } |
| 277 | |
| 278 | // Read all JIT entries. It might randomly fail due to data races. |
| 279 | template <typename Symfile, typename PointerT, typename Uint64_T> |
| 280 | bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Read(Maps* maps, JitMemory* memory) { |
| 281 | std::unordered_set<uint64_t> seen_entry_addr; |
| 282 | |
| 283 | // Read and verify the descriptor (must be after we have read the initial seqlock). |
| 284 | JITDescriptor desc; |
| 285 | if (!(memory->ReadFully(descriptor_addr_, &desc, sizeof(desc)))) { |
| 286 | return false; |
| 287 | } |
| 288 | |
| 289 | entries_.clear(); |
| 290 | JITCodeEntry entry; |
| 291 | for (uint64_t entry_addr = desc.first_entry; entry_addr != 0; entry_addr = entry.next) { |
| 292 | // Check for infinite loops in the lined list. |
| 293 | if (!seen_entry_addr.emplace(entry_addr).second) { |
| 294 | return true; // TODO: Fail when seening infinite loop. |
| 295 | } |
| 296 | |
| 297 | // Read the entry (while checking for data races). |
| 298 | if (!memory->ReadFully(entry_addr, &entry, sizeof(entry))) { |
| 299 | return false; |
| 300 | } |
| 301 | |
| 302 | // Copy and load the symfile. |
| 303 | entries_.emplace_back(JitCacheEntry<Symfile>()); |
| 304 | if (!entries_.back().Init(maps, memory, entry.symfile_addr, entry.symfile_size.value)) { |
| 305 | return false; |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | return true; |
| 310 | } |
| 311 | |
| 312 | // Copy and load ELF file. |
| 313 | template <> |
| 314 | bool JitCacheEntry<Elf>::Init(Maps*, JitMemory* memory, uint64_t addr, uint64_t size) { |
| 315 | // Make a copy of the in-memory symbol file (while checking for data races). |
| 316 | std::unique_ptr<MemoryBuffer> buffer(new MemoryBuffer()); |
| 317 | buffer->Resize(size); |
| 318 | if (!memory->ReadFully(addr, buffer->GetPtr(0), buffer->Size())) { |
| 319 | return false; |
| 320 | } |
| 321 | |
| 322 | // Load and validate the ELF file. |
| 323 | symfile_.reset(new Elf(buffer.release())); |
| 324 | symfile_->Init(); |
| 325 | if (!symfile_->valid()) { |
| 326 | return false; |
| 327 | } |
| 328 | |
| 329 | symfile_->GetTextRange(&addr_, &size_); |
| 330 | return true; |
| 331 | } |
| 332 | |
| 333 | template std::unique_ptr<JitDebug<Elf>> JitDebug<Elf>::Create(ArchEnum, std::shared_ptr<Memory>&, |
| 334 | std::vector<std::string>); |
| 335 | |
| 336 | #if !defined(NO_LIBDEXFILE_SUPPORT) |
| 337 | |
| 338 | template <> |
| 339 | const char* GetDescriptorName<DexFile>() { |
| 340 | return "__dex_debug_descriptor"; |
| 341 | } |
| 342 | |
| 343 | // Copy and load DEX file. |
| 344 | template <> |
| 345 | bool JitCacheEntry<DexFile>::Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t) { |
| 346 | MapInfo* info = maps->Find(addr); |
| 347 | if (info == nullptr) { |
| 348 | return false; |
| 349 | } |
| 350 | symfile_ = DexFile::Create(addr, memory, info); |
| 351 | if (symfile_ == nullptr) { |
| 352 | return false; |
| 353 | } |
| 354 | return true; |
| 355 | } |
| 356 | |
| 357 | template std::unique_ptr<JitDebug<DexFile>> JitDebug<DexFile>::Create(ArchEnum, |
| 358 | std::shared_ptr<Memory>&, |
| 359 | std::vector<std::string>); |
| 360 | |
| 361 | #endif |
| 362 | |
Christopher Ferris | 150db12 | 2017-12-20 18:49:01 -0800 | [diff] [blame] | 363 | } // namespace unwindstack |