UnwindMacOSXFrameBackchain.cpp revision 360784
1//===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Symbol/Function.h"
10#include "lldb/Symbol/ObjectFile.h"
11#include "lldb/Symbol/Symbol.h"
12#include "lldb/Target/ExecutionContext.h"
13#include "lldb/Target/Process.h"
14#include "lldb/Target/Target.h"
15#include "lldb/Target/Thread.h"
16#include "lldb/Utility/ArchSpec.h"
17
18#include "RegisterContextMacOSXFrameBackchain.h"
19
20#include <memory>
21
22using namespace lldb;
23using namespace lldb_private;
24
25UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain(Thread &thread)
26    : Unwind(thread), m_cursors() {}
27
28uint32_t UnwindMacOSXFrameBackchain::DoGetFrameCount() {
29  if (m_cursors.empty()) {
30    ExecutionContext exe_ctx(m_thread.shared_from_this());
31    Target *target = exe_ctx.GetTargetPtr();
32    if (target) {
33      const ArchSpec &target_arch = target->GetArchitecture();
34      // Frame zero should always be supplied by the thread...
35      exe_ctx.SetFrameSP(m_thread.GetStackFrameAtIndex(0));
36
37      if (target_arch.GetAddressByteSize() == 8)
38        GetStackFrameData_x86_64(exe_ctx);
39      else
40        GetStackFrameData_i386(exe_ctx);
41    }
42  }
43  return m_cursors.size();
44}
45
46bool UnwindMacOSXFrameBackchain::DoGetFrameInfoAtIndex(
47    uint32_t idx, addr_t &cfa, addr_t &pc, bool &behaves_like_zeroth_frame) {
48  const uint32_t frame_count = GetFrameCount();
49  if (idx < frame_count) {
50    if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS)
51      return false;
52    if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS)
53      return false;
54
55    pc = m_cursors[idx].pc;
56    cfa = m_cursors[idx].fp;
57    behaves_like_zeroth_frame = (idx == 0);
58
59    return true;
60  }
61  return false;
62}
63
64lldb::RegisterContextSP
65UnwindMacOSXFrameBackchain::DoCreateRegisterContextForFrame(StackFrame *frame) {
66  lldb::RegisterContextSP reg_ctx_sp;
67  uint32_t concrete_idx = frame->GetConcreteFrameIndex();
68  const uint32_t frame_count = GetFrameCount();
69  if (concrete_idx < frame_count)
70    reg_ctx_sp = std::make_shared<RegisterContextMacOSXFrameBackchain>(
71        m_thread, concrete_idx, m_cursors[concrete_idx]);
72  return reg_ctx_sp;
73}
74
75size_t UnwindMacOSXFrameBackchain::GetStackFrameData_i386(
76    const ExecutionContext &exe_ctx) {
77  m_cursors.clear();
78
79  StackFrame *first_frame = exe_ctx.GetFramePtr();
80
81  Process *process = exe_ctx.GetProcessPtr();
82  if (process == nullptr)
83    return 0;
84
85  struct Frame_i386 {
86    uint32_t fp;
87    uint32_t pc;
88  };
89
90  RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
91  assert(reg_ctx);
92
93  Cursor cursor;
94  cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
95  cursor.fp = reg_ctx->GetFP(0);
96
97  Frame_i386 frame = {static_cast<uint32_t>(cursor.fp),
98                      static_cast<uint32_t>(cursor.pc)};
99
100  m_cursors.push_back(cursor);
101
102  const size_t k_frame_size = sizeof(frame);
103  Status error;
104  while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) {
105    // Read both the FP and PC (8 bytes)
106    if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) !=
107        k_frame_size)
108      break;
109    if (frame.pc >= 0x1000) {
110      cursor.pc = frame.pc;
111      cursor.fp = frame.fp;
112      m_cursors.push_back(cursor);
113    }
114  }
115  if (!m_cursors.empty()) {
116    lldb::addr_t first_frame_pc = m_cursors.front().pc;
117    if (first_frame_pc != LLDB_INVALID_ADDRESS) {
118      const SymbolContextItem resolve_scope =
119          eSymbolContextModule | eSymbolContextCompUnit |
120          eSymbolContextFunction | eSymbolContextSymbol;
121
122      SymbolContext first_frame_sc(
123          first_frame->GetSymbolContext(resolve_scope));
124      const AddressRange *addr_range_ptr = nullptr;
125      AddressRange range;
126      if (first_frame_sc.function)
127        addr_range_ptr = &first_frame_sc.function->GetAddressRange();
128      else if (first_frame_sc.symbol) {
129        range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
130        range.SetByteSize(first_frame_sc.symbol->GetByteSize());
131        addr_range_ptr = &range;
132      }
133
134      if (addr_range_ptr) {
135        if (first_frame->GetFrameCodeAddress() ==
136            addr_range_ptr->GetBaseAddress()) {
137          // We are at the first instruction, so we can recover the previous PC
138          // by dereferencing the SP
139          lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
140          // Read the real second frame return address into frame.pc
141          if (first_frame_sp &&
142              process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc),
143                                  error) == sizeof(frame.pc)) {
144            cursor.fp = m_cursors.front().fp;
145            cursor.pc = frame.pc; // Set the new second frame PC
146
147            // Insert the second frame
148            m_cursors.insert(m_cursors.begin() + 1, cursor);
149
150            m_cursors.front().fp = first_frame_sp;
151          }
152        }
153      }
154    }
155  }
156  //    uint32_t i=0;
157  //    printf("      PC                 FP\n");
158  //    printf("      ------------------ ------------------ \n");
159  //    for (i=0; i<m_cursors.size(); ++i)
160  //    {
161  //        printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i,
162  //        m_cursors[i].pc, m_cursors[i].fp);
163  //    }
164  return m_cursors.size();
165}
166
167size_t UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64(
168    const ExecutionContext &exe_ctx) {
169  m_cursors.clear();
170
171  Process *process = exe_ctx.GetProcessPtr();
172  if (process == nullptr)
173    return 0;
174
175  StackFrame *first_frame = exe_ctx.GetFramePtr();
176
177  struct Frame_x86_64 {
178    uint64_t fp;
179    uint64_t pc;
180  };
181
182  RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
183  assert(reg_ctx);
184
185  Cursor cursor;
186  cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
187  cursor.fp = reg_ctx->GetFP(0);
188
189  Frame_x86_64 frame = {cursor.fp, cursor.pc};
190
191  m_cursors.push_back(cursor);
192  Status error;
193  const size_t k_frame_size = sizeof(frame);
194  while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) {
195    // Read both the FP and PC (16 bytes)
196    if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) !=
197        k_frame_size)
198      break;
199
200    if (frame.pc >= 0x1000) {
201      cursor.pc = frame.pc;
202      cursor.fp = frame.fp;
203      m_cursors.push_back(cursor);
204    }
205  }
206  if (!m_cursors.empty()) {
207    lldb::addr_t first_frame_pc = m_cursors.front().pc;
208    if (first_frame_pc != LLDB_INVALID_ADDRESS) {
209      const SymbolContextItem resolve_scope =
210          eSymbolContextModule | eSymbolContextCompUnit |
211          eSymbolContextFunction | eSymbolContextSymbol;
212
213      SymbolContext first_frame_sc(
214          first_frame->GetSymbolContext(resolve_scope));
215      const AddressRange *addr_range_ptr = nullptr;
216      AddressRange range;
217      if (first_frame_sc.function)
218        addr_range_ptr = &first_frame_sc.function->GetAddressRange();
219      else if (first_frame_sc.symbol) {
220        range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
221        range.SetByteSize(first_frame_sc.symbol->GetByteSize());
222        addr_range_ptr = &range;
223      }
224
225      if (addr_range_ptr) {
226        if (first_frame->GetFrameCodeAddress() ==
227            addr_range_ptr->GetBaseAddress()) {
228          // We are at the first instruction, so we can recover the previous PC
229          // by dereferencing the SP
230          lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
231          // Read the real second frame return address into frame.pc
232          if (process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc),
233                                  error) == sizeof(frame.pc)) {
234            cursor.fp = m_cursors.front().fp;
235            cursor.pc = frame.pc; // Set the new second frame PC
236
237            // Insert the second frame
238            m_cursors.insert(m_cursors.begin() + 1, cursor);
239
240            m_cursors.front().fp = first_frame_sp;
241          }
242        }
243      }
244    }
245  }
246  return m_cursors.size();
247}
248