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 ValueList &input_values, 38 lldb::addr_t isa_addr, 39 lldb::addr_t sel_addr, 40 bool stop_others 41 ) : 42 ThreadPlan (ThreadPlan::eKindGeneric, 43 "MacOSX Step through ObjC Trampoline", 44 thread, 45 eVoteNoOpinion, 46 eVoteNoOpinion), 47 m_trampoline_handler (trampoline_handler), 48 m_args_addr (LLDB_INVALID_ADDRESS), 49 m_input_values (input_values), 50 m_isa_addr(isa_addr), 51 m_sel_addr(sel_addr), 52 m_impl_function (NULL), 53 m_stop_others (stop_others) 54 { 55 56 } 57 58 //---------------------------------------------------------------------- 59 // Destructor 60 //---------------------------------------------------------------------- 61 AppleThreadPlanStepThroughObjCTrampoline::~AppleThreadPlanStepThroughObjCTrampoline() 62 { 63 } 64 65 void 66 AppleThreadPlanStepThroughObjCTrampoline::DidPush () 67 { 68 // Setting up the memory space for the called function text might require allocations, 69 // i.e. a nested function call. This needs to be done as a PreResumeAction. 70 m_thread.GetProcess()->AddPreResumeAction (PreResumeInitializeClangFunction, (void *) this); 71 } 72 73 bool 74 AppleThreadPlanStepThroughObjCTrampoline::InitializeClangFunction () 75 { 76 if (!m_func_sp) 77 { 78 StreamString errors; 79 m_args_addr = m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values); 80 81 if (m_args_addr == LLDB_INVALID_ADDRESS) 82 { 83 return false; 84 } 85 m_impl_function = m_trampoline_handler->GetLookupImplementationWrapperFunction(); 86 ExecutionContext exc_ctx; 87 const bool unwind_on_error = true; 88 const bool ignore_breakpoints = true; 89 m_thread.CalculateExecutionContext(exc_ctx); 90 m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, 91 m_args_addr, 92 errors, 93 m_stop_others, 94 unwind_on_error, 95 ignore_breakpoints)); 96 m_func_sp->SetOkayToDiscard(true); 97 m_thread.QueueThreadPlan (m_func_sp, false); 98 } 99 return true; 100 } 101 102 bool 103 AppleThreadPlanStepThroughObjCTrampoline::PreResumeInitializeClangFunction(void *void_myself) 104 { 105 AppleThreadPlanStepThroughObjCTrampoline *myself = static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself); 106 return myself->InitializeClangFunction(); 107 } 108 109 void 110 AppleThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s, 111 lldb::DescriptionLevel level) 112 { 113 if (level == lldb::eDescriptionLevelBrief) 114 s->Printf("Step through ObjC trampoline"); 115 else 116 { 117 s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx, isa: 0x%" PRIx64 ", sel: 0x%" PRIx64, 118 m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(), m_isa_addr, m_sel_addr); 119 } 120 } 121 122 bool 123 AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error) 124 { 125 return true; 126 } 127 128 bool 129 AppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop (Event *event_ptr) 130 { 131 // If we get asked to explain the stop it will be because something went 132 // wrong (like the implementation for selector function crashed... We're going 133 // to figure out what to do about that, so we do explain the stop. 134 return true; 135 } 136 137 lldb::StateType 138 AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState () 139 { 140 return eStateRunning; 141 } 142 143 bool 144 AppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr) 145 { 146 // First stage: we are still handling the "call a function to get the target of the dispatch" 147 if (m_func_sp) 148 { 149 if (!m_func_sp->IsPlanComplete()) 150 { 151 return false; 152 } 153 else 154 { 155 if (!m_func_sp->PlanSucceeded()) 156 { 157 SetPlanComplete(false); 158 return true; 159 } 160 m_func_sp.reset(); 161 } 162 } 163 164 // Second stage, if all went well with the function calling, then fetch the target address, and 165 // queue up a "run to that address" plan. 166 if (!m_run_to_sp) 167 { 168 Value target_addr_value; 169 ExecutionContext exc_ctx; 170 m_thread.CalculateExecutionContext(exc_ctx); 171 m_impl_function->FetchFunctionResults (exc_ctx, m_args_addr, target_addr_value); 172 m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr); 173 lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); 174 Address target_so_addr; 175 target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr()); 176 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); 177 if (target_addr == 0) 178 { 179 if (log) 180 log->Printf("Got target implementation of 0x0, stopping."); 181 SetPlanComplete(); 182 return true; 183 } 184 if (m_trampoline_handler->AddrIsMsgForward(target_addr)) 185 { 186 if (log) 187 log->Printf ("Implementation lookup returned msgForward function: 0x%" PRIx64 ", stopping.", target_addr); 188 189 SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(eSymbolContextEverything); 190 m_run_to_sp.reset(new ThreadPlanStepOut (m_thread, 191 &sc, 192 true, 193 m_stop_others, 194 eVoteNoOpinion, 195 eVoteNoOpinion, 196 0)); 197 m_thread.QueueThreadPlan(m_run_to_sp, false); 198 m_run_to_sp->SetPrivate(true); 199 return false; 200 } 201 202 if (log) 203 log->Printf("Running to ObjC method implementation: 0x%" PRIx64, target_addr); 204 205 ObjCLanguageRuntime *objc_runtime = GetThread().GetProcess()->GetObjCLanguageRuntime(); 206 assert (objc_runtime != NULL); 207 objc_runtime->AddToMethodCache (m_isa_addr, m_sel_addr, target_addr); 208 if (log) 209 log->Printf("Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64 "} = addr=0x%" PRIx64 " to cache.", m_isa_addr, m_sel_addr, target_addr); 210 211 // Extract the target address from the value: 212 213 m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_so_addr, m_stop_others)); 214 m_thread.QueueThreadPlan(m_run_to_sp, false); 215 m_run_to_sp->SetPrivate(true); 216 return false; 217 } 218 else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) 219 { 220 // Third stage, work the run to target plan. 221 SetPlanComplete(); 222 return true; 223 } 224 return false; 225 } 226 227 // The base class MischiefManaged does some cleanup - so you have to call it 228 // in your MischiefManaged derived class. 229 bool 230 AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged () 231 { 232 if (IsPlanComplete()) 233 return true; 234 else 235 return false; 236 } 237 238 bool 239 AppleThreadPlanStepThroughObjCTrampoline::WillStop() 240 { 241 return true; 242 } 243