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