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, &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