1 //===-- AppleThreadPlanStepThroughObjCTrampoline.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 "AppleThreadPlanStepThroughObjCTrampoline.h"
15 #include "AppleObjCTrampolineHandler.h"
16 #include "lldb/Target/Process.h"
17 #include "lldb/Target/Thread.h"
18 #include "lldb/Expression/ClangExpression.h"
19 #include "lldb/Expression/ClangFunction.h"
20 #include "lldb/Target/ExecutionContext.h"
21 #include "lldb/Target/ObjCLanguageRuntime.h"
22 #include "lldb/Target/ThreadPlanRunToAddress.h"
23 #include "lldb/Target/ThreadPlanStepOut.h"
24 #include "lldb/Core/Log.h"
25 
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 //----------------------------------------------------------------------
31 // ThreadPlanStepThroughObjCTrampoline constructor
32 //----------------------------------------------------------------------
33 AppleThreadPlanStepThroughObjCTrampoline::AppleThreadPlanStepThroughObjCTrampoline
34 (
35     Thread &thread,
36     AppleObjCTrampolineHandler *trampoline_handler,
37     lldb::addr_t args_addr,
38     lldb::addr_t object_addr,
39     lldb::addr_t isa_addr,
40     lldb::addr_t sel_addr,
41     bool stop_others
42 ) :
43     ThreadPlan (ThreadPlan::eKindGeneric,
44                 "MacOSX Step through ObjC Trampoline",
45                 thread,
46                 eVoteNoOpinion,
47                 eVoteNoOpinion),
48     m_trampoline_handler (trampoline_handler),
49     m_args_addr (args_addr),
50     m_object_addr (object_addr),
51     m_isa_addr(isa_addr),
52     m_sel_addr(sel_addr),
53     m_impl_function (trampoline_handler->GetLookupImplementationWrapperFunction()),
54     m_stop_others (stop_others)
55 {
56 
57 }
58 
59 //----------------------------------------------------------------------
60 // Destructor
61 //----------------------------------------------------------------------
62 AppleThreadPlanStepThroughObjCTrampoline::~AppleThreadPlanStepThroughObjCTrampoline()
63 {
64 }
65 
66 void
67 AppleThreadPlanStepThroughObjCTrampoline::DidPush ()
68 {
69     StreamString errors;
70     ExecutionContext exc_ctx;
71     m_thread.CalculateExecutionContext(exc_ctx);
72     m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, m_args_addr, errors, m_stop_others));
73     m_func_sp->SetPrivate(true);
74     m_thread.QueueThreadPlan (m_func_sp, false);
75 }
76 
77 void
78 AppleThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s,
79                                                           lldb::DescriptionLevel level)
80 {
81     if (level == lldb::eDescriptionLevelBrief)
82         s->Printf("Step through ObjC trampoline");
83     else
84     {
85         s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx, isa: 0x%llx, sel: 0x%llx",
86         m_object_addr, m_isa_addr, m_sel_addr);
87     }
88 }
89 
90 bool
91 AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error)
92 {
93     return true;
94 }
95 
96 bool
97 AppleThreadPlanStepThroughObjCTrampoline::PlanExplainsStop ()
98 {
99     // This plan should actually never stop when it is on the top of the plan
100     // stack, since it does all it's running in client plans.
101     return false;
102 }
103 
104 lldb::StateType
105 AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState ()
106 {
107     return eStateRunning;
108 }
109 
110 bool
111 AppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr)
112 {
113     if (m_func_sp.get() == NULL || m_thread.IsThreadPlanDone(m_func_sp.get()))
114     {
115         m_func_sp.reset();
116         if (!m_run_to_sp)
117         {
118             Value target_addr_value;
119             ExecutionContext exc_ctx;
120             m_thread.CalculateExecutionContext(exc_ctx);
121             m_impl_function->FetchFunctionResults (exc_ctx, m_args_addr, target_addr_value);
122             m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr);
123             lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong();
124             Address target_so_addr;
125             target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr());
126             LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
127             if (target_addr == 0)
128             {
129                 if (log)
130                     log->Printf("Got target implementation of 0x0, stopping.");
131                 SetPlanComplete();
132                 return true;
133             }
134             if (m_trampoline_handler->AddrIsMsgForward(target_addr))
135             {
136                 if (log)
137                     log->Printf ("Implementation lookup returned msgForward function: 0x%llx, stopping.", target_addr);
138 
139                 SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(eSymbolContextEverything);
140                 m_run_to_sp.reset(new ThreadPlanStepOut (m_thread,
141                                                          &sc,
142                                                          true,
143                                                          m_stop_others,
144                                                          eVoteNoOpinion,
145                                                          eVoteNoOpinion,
146                                                          0));
147                 m_thread.QueueThreadPlan(m_run_to_sp, false);
148                 m_run_to_sp->SetPrivate(true);
149                 return false;
150             }
151 
152             if (log)
153                 log->Printf("Running to ObjC method implementation: 0x%llx", target_addr);
154 
155             ObjCLanguageRuntime *objc_runtime = GetThread().GetProcess().GetObjCLanguageRuntime();
156             assert (objc_runtime != NULL);
157             objc_runtime->AddToMethodCache (m_isa_addr, m_sel_addr, target_addr);
158             if (log)
159                 log->Printf("Adding {isa-addr=0x%llx, sel-addr=0x%llx} = addr=0x%llx to cache.", m_isa_addr, m_sel_addr, target_addr);
160 
161             // Extract the target address from the value:
162 
163             m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_so_addr, m_stop_others));
164             m_thread.QueueThreadPlan(m_run_to_sp, false);
165             m_run_to_sp->SetPrivate(true);
166             return false;
167         }
168         else if (m_thread.IsThreadPlanDone(m_run_to_sp.get()))
169         {
170             SetPlanComplete();
171             return true;
172         }
173     }
174     return false;
175 }
176 
177 // The base class MischiefManaged does some cleanup - so you have to call it
178 // in your MischiefManaged derived class.
179 bool
180 AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged ()
181 {
182     if (IsPlanComplete())
183         return true;
184     else
185         return false;
186 }
187 
188 bool
189 AppleThreadPlanStepThroughObjCTrampoline::WillStop()
190 {
191     return true;
192 }
193