BacktraceOffline.cpp revision 9e402bb20cb868577e5588d8323363411655291b
1/* 2 * Copyright (C) 2015 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 "BacktraceOffline.h" 18 19extern "C" { 20#define UNW_REMOTE_ONLY 21#include <dwarf.h> 22} 23 24#include <stdint.h> 25#include <string.h> 26#include <sys/types.h> 27#include <ucontext.h> 28#include <unistd.h> 29 30#include <string> 31#include <vector> 32 33#include <backtrace/Backtrace.h> 34#include <backtrace/BacktraceMap.h> 35 36#pragma clang diagnostic push 37#pragma clang diagnostic ignored "-Wunused-parameter" 38 39#include <llvm/ADT/StringRef.h> 40#include <llvm/Object/Binary.h> 41#include <llvm/Object/ELFObjectFile.h> 42#include <llvm/Object/ObjectFile.h> 43 44#pragma clang diagnostic pop 45 46#include "BacktraceLog.h" 47 48void Space::Clear() { 49 start = 0; 50 end = 0; 51 data = nullptr; 52} 53 54size_t Space::Read(uint64_t addr, uint8_t* buffer, size_t size) { 55 if (addr >= start && addr < end) { 56 size_t read_size = std::min(size, static_cast<size_t>(end - addr)); 57 memcpy(buffer, data + (addr - start), read_size); 58 return read_size; 59 } 60 return 0; 61} 62 63static int FindProcInfo(unw_addr_space_t addr_space, unw_word_t ip, unw_proc_info* proc_info, 64 int need_unwind_info, void* arg) { 65 BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg); 66 bool result = backtrace->FindProcInfo(addr_space, ip, proc_info, need_unwind_info); 67 return result ? 0 : -UNW_EINVAL; 68} 69 70static void PutUnwindInfo(unw_addr_space_t, unw_proc_info_t*, void*) { 71} 72 73static int GetDynInfoListAddr(unw_addr_space_t, unw_word_t*, void*) { 74 return -UNW_ENOINFO; 75} 76 77static int AccessMem(unw_addr_space_t, unw_word_t addr, unw_word_t* value, int write, void* arg) { 78 if (write == 1) { 79 return -UNW_EINVAL; 80 } 81 BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg); 82 *value = 0; 83 size_t read_size = backtrace->Read(addr, reinterpret_cast<uint8_t*>(value), sizeof(unw_word_t)); 84 // Strictly we should check if read_size matches sizeof(unw_word_t), but it is possible in 85 // .eh_frame_hdr that the section can end at a position not aligned in sizeof(unw_word_t), and 86 // we should permit the read at the end of the section. 87 return (read_size > 0u ? 0 : -UNW_EINVAL); 88} 89 90static int AccessReg(unw_addr_space_t, unw_regnum_t unwind_reg, unw_word_t* value, int write, 91 void* arg) { 92 if (write == 1) { 93 return -UNW_EINVAL; 94 } 95 BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg); 96 uint64_t reg_value; 97 bool result = backtrace->ReadReg(unwind_reg, ®_value); 98 if (result) { 99 *value = static_cast<unw_word_t>(reg_value); 100 } 101 return result ? 0 : -UNW_EINVAL; 102} 103 104static int AccessFpReg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t*, int, void*) { 105 return -UNW_EINVAL; 106} 107 108static int Resume(unw_addr_space_t, unw_cursor_t*, void*) { 109 return -UNW_EINVAL; 110} 111 112static int GetProcName(unw_addr_space_t, unw_word_t, char*, size_t, unw_word_t*, void*) { 113 return -UNW_EINVAL; 114} 115 116static unw_accessors_t accessors = { 117 .find_proc_info = FindProcInfo, 118 .put_unwind_info = PutUnwindInfo, 119 .get_dyn_info_list_addr = GetDynInfoListAddr, 120 .access_mem = AccessMem, 121 .access_reg = AccessReg, 122 .access_fpreg = AccessFpReg, 123 .resume = Resume, 124 .get_proc_name = GetProcName, 125}; 126 127bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) { 128 if (context == nullptr) { 129 BACK_LOGW("The context is needed for offline backtracing."); 130 return false; 131 } 132 context_ = context; 133 134 unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0); 135 unw_cursor_t cursor; 136 int ret = unw_init_remote(&cursor, addr_space, this); 137 if (ret != 0) { 138 BACK_LOGW("unw_init_remote failed %d", ret); 139 unw_destroy_addr_space(addr_space); 140 return false; 141 } 142 size_t num_frames = 0; 143 do { 144 unw_word_t pc; 145 ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); 146 if (ret < 0) { 147 BACK_LOGW("Failed to read IP %d", ret); 148 break; 149 } 150 unw_word_t sp; 151 ret = unw_get_reg(&cursor, UNW_REG_SP, &sp); 152 if (ret < 0) { 153 BACK_LOGW("Failed to read SP %d", ret); 154 break; 155 } 156 157 if (num_ignore_frames == 0) { 158 frames_.resize(num_frames + 1); 159 backtrace_frame_data_t* frame = &frames_[num_frames]; 160 frame->num = num_frames; 161 frame->pc = static_cast<uintptr_t>(pc); 162 frame->sp = static_cast<uintptr_t>(sp); 163 frame->stack_size = 0; 164 165 if (num_frames > 0) { 166 backtrace_frame_data_t* prev = &frames_[num_frames - 1]; 167 prev->stack_size = frame->sp - prev->sp; 168 } 169 frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); 170 FillInMap(frame->pc, &frame->map); 171 num_frames++; 172 } else { 173 num_ignore_frames--; 174 } 175 ret = unw_step(&cursor); 176 } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES); 177 178 unw_destroy_addr_space(addr_space); 179 context_ = nullptr; 180 return true; 181} 182 183bool BacktraceOffline::ReadWord(uintptr_t ptr, word_t* out_value) { 184 size_t bytes_read = Read(ptr, reinterpret_cast<uint8_t*>(out_value), sizeof(word_t)); 185 return bytes_read == sizeof(word_t); 186} 187 188size_t BacktraceOffline::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { 189 // Normally, libunwind needs stack information and call frame information to do remote unwinding. 190 // If call frame information is stored in .debug_frame, libunwind can read it from file 191 // by itself. If call frame information is stored in .eh_frame, we need to provide data in 192 // .eh_frame/.eh_frame_hdr sections. 193 // The order of readings below doesn't matter, as the spaces don't overlap with each other. 194 size_t read_size = eh_frame_hdr_space_.Read(addr, buffer, bytes); 195 if (read_size != 0) { 196 return read_size; 197 } 198 read_size = eh_frame_space_.Read(addr, buffer, bytes); 199 if (read_size != 0) { 200 return read_size; 201 } 202 read_size = stack_space_.Read(addr, buffer, bytes); 203 return read_size; 204} 205 206static bool FileOffsetToVaddr( 207 const std::vector<DebugFrameInfo::EhFrame::ProgramHeader>& program_headers, 208 uint64_t file_offset, uint64_t* vaddr) { 209 for (auto& header : program_headers) { 210 if (file_offset >= header.file_offset && file_offset < header.file_offset + header.file_size) { 211 // TODO: Consider load_bias? 212 *vaddr = file_offset - header.file_offset + header.vaddr; 213 return true; 214 } 215 } 216 return false; 217} 218 219bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip, 220 unw_proc_info_t* proc_info, int need_unwind_info) { 221 backtrace_map_t map; 222 FillInMap(ip, &map); 223 if (!BacktraceMap::IsValid(map)) { 224 return false; 225 } 226 const std::string& filename = map.name; 227 DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename); 228 if (debug_frame == nullptr) { 229 return false; 230 } 231 if (debug_frame->is_eh_frame) { 232 uint64_t ip_offset = ip - map.start + map.offset; 233 uint64_t ip_vaddr; // vaddr in the elf file. 234 bool result = FileOffsetToVaddr(debug_frame->eh_frame.program_headers, ip_offset, &ip_vaddr); 235 if (!result) { 236 return false; 237 } 238 // Calculate the addresses where .eh_frame_hdr and .eh_frame stay when the process was running. 239 eh_frame_hdr_space_.start = (ip - ip_vaddr) + debug_frame->eh_frame.eh_frame_hdr_vaddr; 240 eh_frame_hdr_space_.end = 241 eh_frame_hdr_space_.start + debug_frame->eh_frame.eh_frame_hdr_data.size(); 242 eh_frame_hdr_space_.data = debug_frame->eh_frame.eh_frame_hdr_data.data(); 243 244 eh_frame_space_.start = (ip - ip_vaddr) + debug_frame->eh_frame.eh_frame_vaddr; 245 eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.eh_frame_data.size(); 246 eh_frame_space_.data = debug_frame->eh_frame.eh_frame_data.data(); 247 248 unw_dyn_info di; 249 memset(&di, '\0', sizeof(di)); 250 di.start_ip = map.start; 251 di.end_ip = map.end; 252 di.format = UNW_INFO_FORMAT_REMOTE_TABLE; 253 di.u.rti.name_ptr = 0; 254 di.u.rti.segbase = eh_frame_hdr_space_.start; 255 di.u.rti.table_data = 256 eh_frame_hdr_space_.start + debug_frame->eh_frame.fde_table_offset_in_eh_frame_hdr; 257 di.u.rti.table_len = (eh_frame_hdr_space_.end - di.u.rti.table_data) / sizeof(unw_word_t); 258 int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this); 259 return ret == 0; 260 } 261 262 eh_frame_hdr_space_.Clear(); 263 eh_frame_space_.Clear(); 264 unw_dyn_info_t di; 265 unw_word_t segbase = map.start - map.offset; 266 int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end); 267 if (found == 1) { 268 int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this); 269 return ret == 0; 270 } 271 return false; 272} 273 274bool BacktraceOffline::ReadReg(size_t reg, uint64_t* value) { 275 bool result = true; 276#if defined(__arm__) 277 switch (reg) { 278 case UNW_ARM_R0: 279 *value = context_->uc_mcontext.arm_r0; 280 break; 281 case UNW_ARM_R1: 282 *value = context_->uc_mcontext.arm_r1; 283 break; 284 case UNW_ARM_R2: 285 *value = context_->uc_mcontext.arm_r2; 286 break; 287 case UNW_ARM_R3: 288 *value = context_->uc_mcontext.arm_r3; 289 break; 290 case UNW_ARM_R4: 291 *value = context_->uc_mcontext.arm_r4; 292 break; 293 case UNW_ARM_R5: 294 *value = context_->uc_mcontext.arm_r5; 295 break; 296 case UNW_ARM_R6: 297 *value = context_->uc_mcontext.arm_r6; 298 break; 299 case UNW_ARM_R7: 300 *value = context_->uc_mcontext.arm_r7; 301 break; 302 case UNW_ARM_R8: 303 *value = context_->uc_mcontext.arm_r8; 304 break; 305 case UNW_ARM_R9: 306 *value = context_->uc_mcontext.arm_r9; 307 break; 308 case UNW_ARM_R10: 309 *value = context_->uc_mcontext.arm_r10; 310 break; 311 case UNW_ARM_R11: 312 *value = context_->uc_mcontext.arm_fp; 313 break; 314 case UNW_ARM_R12: 315 *value = context_->uc_mcontext.arm_ip; 316 break; 317 case UNW_ARM_R13: 318 *value = context_->uc_mcontext.arm_sp; 319 break; 320 case UNW_ARM_R14: 321 *value = context_->uc_mcontext.arm_lr; 322 break; 323 case UNW_ARM_R15: 324 *value = context_->uc_mcontext.arm_pc; 325 break; 326 default: 327 result = false; 328 } 329#elif defined(__aarch64__) 330 if (reg <= UNW_AARCH64_PC) { 331 *value = context_->uc_mcontext.regs[reg]; 332 } else { 333 result = false; 334 } 335#elif defined(__x86_64__) 336 switch (reg) { 337 case UNW_X86_64_R8: 338 *value = context_->uc_mcontext.gregs[REG_R8]; 339 break; 340 case UNW_X86_64_R9: 341 *value = context_->uc_mcontext.gregs[REG_R9]; 342 break; 343 case UNW_X86_64_R10: 344 *value = context_->uc_mcontext.gregs[REG_R10]; 345 break; 346 case UNW_X86_64_R11: 347 *value = context_->uc_mcontext.gregs[REG_R11]; 348 break; 349 case UNW_X86_64_R12: 350 *value = context_->uc_mcontext.gregs[REG_R12]; 351 break; 352 case UNW_X86_64_R13: 353 *value = context_->uc_mcontext.gregs[REG_R13]; 354 break; 355 case UNW_X86_64_R14: 356 *value = context_->uc_mcontext.gregs[REG_R14]; 357 break; 358 case UNW_X86_64_R15: 359 *value = context_->uc_mcontext.gregs[REG_R15]; 360 break; 361 case UNW_X86_64_RDI: 362 *value = context_->uc_mcontext.gregs[REG_RDI]; 363 break; 364 case UNW_X86_64_RSI: 365 *value = context_->uc_mcontext.gregs[REG_RSI]; 366 break; 367 case UNW_X86_64_RBP: 368 *value = context_->uc_mcontext.gregs[REG_RBP]; 369 break; 370 case UNW_X86_64_RBX: 371 *value = context_->uc_mcontext.gregs[REG_RBX]; 372 break; 373 case UNW_X86_64_RDX: 374 *value = context_->uc_mcontext.gregs[REG_RDX]; 375 break; 376 case UNW_X86_64_RAX: 377 *value = context_->uc_mcontext.gregs[REG_RAX]; 378 break; 379 case UNW_X86_64_RCX: 380 *value = context_->uc_mcontext.gregs[REG_RCX]; 381 break; 382 case UNW_X86_64_RSP: 383 *value = context_->uc_mcontext.gregs[REG_RSP]; 384 break; 385 case UNW_X86_64_RIP: 386 *value = context_->uc_mcontext.gregs[REG_RIP]; 387 break; 388 default: 389 result = false; 390 } 391#elif defined(__i386__) 392 switch (reg) { 393 case UNW_X86_GS: 394 *value = context_->uc_mcontext.gregs[REG_GS]; 395 break; 396 case UNW_X86_FS: 397 *value = context_->uc_mcontext.gregs[REG_FS]; 398 break; 399 case UNW_X86_ES: 400 *value = context_->uc_mcontext.gregs[REG_ES]; 401 break; 402 case UNW_X86_DS: 403 *value = context_->uc_mcontext.gregs[REG_DS]; 404 break; 405 case UNW_X86_EAX: 406 *value = context_->uc_mcontext.gregs[REG_EAX]; 407 break; 408 case UNW_X86_EBX: 409 *value = context_->uc_mcontext.gregs[REG_EBX]; 410 break; 411 case UNW_X86_ECX: 412 *value = context_->uc_mcontext.gregs[REG_ECX]; 413 break; 414 case UNW_X86_EDX: 415 *value = context_->uc_mcontext.gregs[REG_EDX]; 416 break; 417 case UNW_X86_ESI: 418 *value = context_->uc_mcontext.gregs[REG_ESI]; 419 break; 420 case UNW_X86_EDI: 421 *value = context_->uc_mcontext.gregs[REG_EDI]; 422 break; 423 case UNW_X86_EBP: 424 *value = context_->uc_mcontext.gregs[REG_EBP]; 425 break; 426 case UNW_X86_EIP: 427 *value = context_->uc_mcontext.gregs[REG_EIP]; 428 break; 429 case UNW_X86_ESP: 430 *value = context_->uc_mcontext.gregs[REG_ESP]; 431 break; 432 case UNW_X86_TRAPNO: 433 *value = context_->uc_mcontext.gregs[REG_TRAPNO]; 434 break; 435 case UNW_X86_CS: 436 *value = context_->uc_mcontext.gregs[REG_CS]; 437 break; 438 case UNW_X86_EFLAGS: 439 *value = context_->uc_mcontext.gregs[REG_EFL]; 440 break; 441 case UNW_X86_SS: 442 *value = context_->uc_mcontext.gregs[REG_SS]; 443 break; 444 default: 445 result = false; 446 } 447#endif 448 return result; 449} 450 451std::string BacktraceOffline::GetFunctionNameRaw(uintptr_t, uintptr_t* offset) { 452 // We don't have enough information to support this. And it is expensive. 453 *offset = 0; 454 return ""; 455} 456 457std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>> BacktraceOffline::debug_frames_; 458std::unordered_set<std::string> BacktraceOffline::debug_frame_missing_files_; 459 460static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename); 461 462DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) { 463 if (cache_file_) { 464 auto it = debug_frames_.find(filename); 465 if (it != debug_frames_.end()) { 466 return it->second.get(); 467 } 468 if (debug_frame_missing_files_.find(filename) != debug_frame_missing_files_.end()) { 469 return nullptr; 470 } 471 } 472 DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename); 473 if (cache_file_) { 474 if (debug_frame != nullptr) { 475 debug_frames_.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame)); 476 } else { 477 debug_frame_missing_files_.insert(filename); 478 } 479 } else { 480 if (last_debug_frame_ != nullptr) { 481 delete last_debug_frame_; 482 } 483 last_debug_frame_ = debug_frame; 484 } 485 return debug_frame; 486} 487 488static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p) { 489 if (encode == DW_EH_PE_omit) { 490 return 0; 491 } 492 uint8_t format = encode & 0x0f; 493 switch (format) { 494 case DW_EH_PE_ptr: 495 p += sizeof(unw_word_t); 496 break; 497 case DW_EH_PE_uleb128: 498 case DW_EH_PE_sleb128: 499 while ((*p & 0x80) != 0) { 500 ++p; 501 } 502 ++p; 503 break; 504 case DW_EH_PE_udata2: 505 case DW_EH_PE_sdata2: 506 p += 2; 507 break; 508 case DW_EH_PE_udata4: 509 case DW_EH_PE_sdata4: 510 p += 4; 511 break; 512 case DW_EH_PE_udata8: 513 case DW_EH_PE_sdata8: 514 p += 8; 515 break; 516 default: 517 return false; 518 } 519 return true; 520} 521 522static bool GetFdeTableOffsetInEhFrameHdr(const std::vector<uint8_t>& data, 523 uint64_t* table_offset_in_eh_frame_hdr) { 524 const uint8_t* p = data.data(); 525 const uint8_t* end = p + data.size(); 526 if (p + 4 > end) { 527 return false; 528 } 529 uint8_t version = *p++; 530 if (version != 1) { 531 return false; 532 } 533 uint8_t eh_frame_ptr_encode = *p++; 534 uint8_t fde_count_encode = *p++; 535 uint8_t fde_table_encode = *p++; 536 537 if (fde_table_encode != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) { 538 return false; 539 } 540 541 if (!OmitEncodedValue(eh_frame_ptr_encode, p) || !OmitEncodedValue(fde_count_encode, p)) { 542 return false; 543 } 544 if (p >= end) { 545 return false; 546 } 547 *table_offset_in_eh_frame_hdr = p - data.data(); 548 return true; 549} 550 551using ProgramHeader = DebugFrameInfo::EhFrame::ProgramHeader; 552 553template <class ELFT> 554DebugFrameInfo* ReadDebugFrameFromELFFile(const llvm::object::ELFFile<ELFT>* elf) { 555 bool has_eh_frame_hdr = false; 556 uint64_t eh_frame_hdr_vaddr = 0; 557 std::vector<uint8_t> eh_frame_hdr_data; 558 bool has_eh_frame = false; 559 uint64_t eh_frame_vaddr = 0; 560 std::vector<uint8_t> eh_frame_data; 561 562 for (auto it = elf->begin_sections(); it != elf->end_sections(); ++it) { 563 llvm::ErrorOr<llvm::StringRef> name = elf->getSectionName(&*it); 564 if (name) { 565 if (name.get() == ".debug_frame") { 566 DebugFrameInfo* debug_frame = new DebugFrameInfo; 567 debug_frame->is_eh_frame = false; 568 return debug_frame; 569 } 570 if (name.get() == ".eh_frame_hdr") { 571 has_eh_frame_hdr = true; 572 eh_frame_hdr_vaddr = it->sh_addr; 573 llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it); 574 if (data) { 575 eh_frame_hdr_data.insert(eh_frame_hdr_data.begin(), data->data(), 576 data->data() + data->size()); 577 } else { 578 return nullptr; 579 } 580 } else if (name.get() == ".eh_frame") { 581 has_eh_frame = true; 582 eh_frame_vaddr = it->sh_addr; 583 llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it); 584 if (data) { 585 eh_frame_data.insert(eh_frame_data.begin(), data->data(), data->data() + data->size()); 586 } else { 587 return nullptr; 588 } 589 } 590 } 591 } 592 if (!(has_eh_frame_hdr && has_eh_frame)) { 593 return nullptr; 594 } 595 uint64_t fde_table_offset; 596 if (!GetFdeTableOffsetInEhFrameHdr(eh_frame_hdr_data, &fde_table_offset)) { 597 return nullptr; 598 } 599 600 std::vector<ProgramHeader> program_headers; 601 for (auto it = elf->begin_program_headers(); it != elf->end_program_headers(); ++it) { 602 ProgramHeader header; 603 header.vaddr = it->p_vaddr; 604 header.file_offset = it->p_offset; 605 header.file_size = it->p_filesz; 606 program_headers.push_back(header); 607 } 608 DebugFrameInfo* debug_frame = new DebugFrameInfo; 609 debug_frame->is_eh_frame = true; 610 debug_frame->eh_frame.eh_frame_hdr_vaddr = eh_frame_hdr_vaddr; 611 debug_frame->eh_frame.eh_frame_vaddr = eh_frame_vaddr; 612 debug_frame->eh_frame.fde_table_offset_in_eh_frame_hdr = fde_table_offset; 613 debug_frame->eh_frame.eh_frame_hdr_data = std::move(eh_frame_hdr_data); 614 debug_frame->eh_frame.eh_frame_data = std::move(eh_frame_data); 615 debug_frame->eh_frame.program_headers = program_headers; 616 return debug_frame; 617} 618 619static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) { 620 auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename)); 621 if (owning_binary.getError()) { 622 return nullptr; 623 } 624 llvm::object::Binary* binary = owning_binary.get().getBinary(); 625 auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary); 626 if (obj == nullptr) { 627 return nullptr; 628 } 629 if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) { 630 return ReadDebugFrameFromELFFile(elf->getELFFile()); 631 } 632 if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) { 633 return ReadDebugFrameFromELFFile(elf->getELFFile()); 634 } 635 return nullptr; 636} 637