1 //===-- AppleThreadPlanStepThroughObjCTrampoline.cpp-----------------------===// 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 "AppleThreadPlanStepThroughObjCTrampoline.h" 10 11 #include "AppleObjCTrampolineHandler.h" 12 #include "lldb/Expression/DiagnosticManager.h" 13 #include "lldb/Expression/FunctionCaller.h" 14 #include "lldb/Expression/UtilityFunction.h" 15 #include "lldb/Target/ExecutionContext.h" 16 #include "lldb/Target/Process.h" 17 #include "lldb/Target/Thread.h" 18 #include "lldb/Target/ThreadPlanRunToAddress.h" 19 #include "lldb/Target/ThreadPlanStepOut.h" 20 #include "lldb/Utility/Log.h" 21 22 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" 23 24 #include <memory> 25 26 using namespace lldb; 27 using namespace lldb_private; 28 29 // ThreadPlanStepThroughObjCTrampoline constructor 30 AppleThreadPlanStepThroughObjCTrampoline:: 31 AppleThreadPlanStepThroughObjCTrampoline( 32 Thread &thread, AppleObjCTrampolineHandler &trampoline_handler, 33 ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr) 34 : ThreadPlan(ThreadPlan::eKindGeneric, 35 "MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion, 36 eVoteNoOpinion), 37 m_trampoline_handler(trampoline_handler), 38 m_args_addr(LLDB_INVALID_ADDRESS), m_input_values(input_values), 39 m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(nullptr) {} 40 41 // Destructor 42 AppleThreadPlanStepThroughObjCTrampoline:: 43 ~AppleThreadPlanStepThroughObjCTrampoline() = default; 44 45 void AppleThreadPlanStepThroughObjCTrampoline::DidPush() { 46 // Setting up the memory space for the called function text might require 47 // allocations, i.e. a nested function call. This needs to be done as a 48 // PreResumeAction. 49 m_process.AddPreResumeAction(PreResumeInitializeFunctionCaller, (void *)this); 50 } 51 52 bool AppleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller() { 53 if (!m_func_sp) { 54 DiagnosticManager diagnostics; 55 m_args_addr = 56 m_trampoline_handler.SetupDispatchFunction(GetThread(), m_input_values); 57 58 if (m_args_addr == LLDB_INVALID_ADDRESS) { 59 return false; 60 } 61 m_impl_function = 62 m_trampoline_handler.GetLookupImplementationFunctionCaller(); 63 ExecutionContext exc_ctx; 64 EvaluateExpressionOptions options; 65 options.SetUnwindOnError(true); 66 options.SetIgnoreBreakpoints(true); 67 options.SetStopOthers(false); 68 GetThread().CalculateExecutionContext(exc_ctx); 69 m_func_sp = m_impl_function->GetThreadPlanToCallFunction( 70 exc_ctx, m_args_addr, options, diagnostics); 71 m_func_sp->SetOkayToDiscard(true); 72 PushPlan(m_func_sp); 73 } 74 return true; 75 } 76 77 bool AppleThreadPlanStepThroughObjCTrampoline:: 78 PreResumeInitializeFunctionCaller(void *void_myself) { 79 AppleThreadPlanStepThroughObjCTrampoline *myself = 80 static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself); 81 return myself->InitializeFunctionCaller(); 82 } 83 84 void AppleThreadPlanStepThroughObjCTrampoline::GetDescription( 85 Stream *s, lldb::DescriptionLevel level) { 86 if (level == lldb::eDescriptionLevelBrief) 87 s->Printf("Step through ObjC trampoline"); 88 else { 89 s->Printf("Stepping to implementation of ObjC method - obj: 0x%llx, isa: " 90 "0x%" PRIx64 ", sel: 0x%" PRIx64, 91 m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(), 92 m_isa_addr, m_sel_addr); 93 } 94 } 95 96 bool AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan(Stream *error) { 97 return true; 98 } 99 100 bool AppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop( 101 Event *event_ptr) { 102 // If we get asked to explain the stop it will be because something went 103 // wrong (like the implementation for selector function crashed... We're 104 // going to figure out what to do about that, so we do explain the stop. 105 return true; 106 } 107 108 lldb::StateType AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState() { 109 return eStateRunning; 110 } 111 112 bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) { 113 // First stage: we are still handling the "call a function to get the target 114 // of the dispatch" 115 if (m_func_sp) { 116 if (!m_func_sp->IsPlanComplete()) { 117 return false; 118 } else { 119 if (!m_func_sp->PlanSucceeded()) { 120 SetPlanComplete(false); 121 return true; 122 } 123 m_func_sp.reset(); 124 } 125 } 126 127 // Second stage, if all went well with the function calling, then fetch the 128 // target address, and queue up a "run to that address" plan. 129 if (!m_run_to_sp) { 130 Value target_addr_value; 131 ExecutionContext exc_ctx; 132 GetThread().CalculateExecutionContext(exc_ctx); 133 m_impl_function->FetchFunctionResults(exc_ctx, m_args_addr, 134 target_addr_value); 135 m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr); 136 lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); 137 Address target_so_addr; 138 target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr()); 139 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); 140 if (target_addr == 0) { 141 LLDB_LOGF(log, "Got target implementation of 0x0, stopping."); 142 SetPlanComplete(); 143 return true; 144 } 145 if (m_trampoline_handler.AddrIsMsgForward(target_addr)) { 146 LLDB_LOGF(log, 147 "Implementation lookup returned msgForward function: 0x%" PRIx64 148 ", stopping.", 149 target_addr); 150 151 SymbolContext sc = GetThread().GetStackFrameAtIndex(0)->GetSymbolContext( 152 eSymbolContextEverything); 153 Status status; 154 const bool abort_other_plans = false; 155 const bool first_insn = true; 156 const uint32_t frame_idx = 0; 157 m_run_to_sp = GetThread().QueueThreadPlanForStepOutNoShouldStop( 158 abort_other_plans, &sc, first_insn, false, eVoteNoOpinion, 159 eVoteNoOpinion, frame_idx, status); 160 if (m_run_to_sp && status.Success()) 161 m_run_to_sp->SetPrivate(true); 162 return false; 163 } 164 165 LLDB_LOGF(log, "Running to ObjC method implementation: 0x%" PRIx64, 166 target_addr); 167 168 ObjCLanguageRuntime *objc_runtime = 169 ObjCLanguageRuntime::Get(*GetThread().GetProcess()); 170 assert(objc_runtime != nullptr); 171 objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr); 172 LLDB_LOGF(log, 173 "Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64 174 "} = addr=0x%" PRIx64 " to cache.", 175 m_isa_addr, m_sel_addr, target_addr); 176 177 // Extract the target address from the value: 178 179 m_run_to_sp = std::make_shared<ThreadPlanRunToAddress>( 180 GetThread(), target_so_addr, false); 181 PushPlan(m_run_to_sp); 182 return false; 183 } else if (GetThread().IsThreadPlanDone(m_run_to_sp.get())) { 184 // Third stage, work the run to target plan. 185 SetPlanComplete(); 186 return true; 187 } 188 return false; 189 } 190 191 // The base class MischiefManaged does some cleanup - so you have to call it in 192 // your MischiefManaged derived class. 193 bool AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged() { 194 return IsPlanComplete(); 195 } 196 197 bool AppleThreadPlanStepThroughObjCTrampoline::WillStop() { return true; } 198 199 // Objective-C uses optimized dispatch functions for some common and seldom 200 // overridden methods. For instance 201 // [object respondsToSelector:]; 202 // will get compiled to: 203 // objc_opt_respondsToSelector(object); 204 // This checks whether the selector has been overridden, directly calling the 205 // implementation if it hasn't and calling objc_msgSend if it has. 206 // 207 // We need to get into the overridden implementation. We'll do that by 208 // setting a breakpoint on objc_msgSend, and doing a "step out". If we stop 209 // at objc_msgSend, we can step through to the target of the send, and see if 210 // that's a place we want to stop. 211 // 212 // A couple of complexities. The checking code might call some other method, 213 // so we might see objc_msgSend more than once. Also, these optimized dispatch 214 // functions might dispatch more than one message at a time (e.g. alloc followed 215 // by init.) So we can't give up at the first objc_msgSend. 216 // That means among other things that we have to handle the "ShouldStopHere" - 217 // since we can't just return control to the plan that's controlling us on the 218 // first step. 219 220 AppleThreadPlanStepThroughDirectDispatch :: 221 AppleThreadPlanStepThroughDirectDispatch( 222 Thread &thread, AppleObjCTrampolineHandler &handler, 223 llvm::StringRef dispatch_func_name) 224 : ThreadPlanStepOut(thread, nullptr, true /* first instruction */, false, 225 eVoteNoOpinion, eVoteNoOpinion, 226 0 /* Step out of zeroth frame */, 227 eLazyBoolNo /* Our parent plan will decide this 228 when we are done */ 229 , 230 true /* Run to branch for inline step out */, 231 false /* Don't gather the return value */), 232 m_trampoline_handler(handler), 233 m_dispatch_func_name(std::string(dispatch_func_name)), 234 m_at_msg_send(false) { 235 // Set breakpoints on the dispatch functions: 236 auto bkpt_callback = [&] (lldb::addr_t addr, 237 const AppleObjCTrampolineHandler 238 ::DispatchFunction &dispatch) { 239 m_msgSend_bkpts.push_back(GetTarget().CreateBreakpoint(addr, 240 true /* internal */, 241 false /* hard */)); 242 m_msgSend_bkpts.back()->SetThreadID(GetThread().GetID()); 243 }; 244 handler.ForEachDispatchFunction(bkpt_callback); 245 246 // We'll set the step-out plan in the DidPush so it gets queued in the right 247 // order. 248 249 if (GetThread().GetStepInAvoidsNoDebug()) 250 GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); 251 else 252 GetFlags().Clear(ThreadPlanShouldStopHere::eStepInAvoidNoDebug); 253 // We only care about step in. Our parent plan will figure out what to 254 // do when we've stepped out again. 255 GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug); 256 } 257 258 AppleThreadPlanStepThroughDirectDispatch:: 259 ~AppleThreadPlanStepThroughDirectDispatch() { 260 for (BreakpointSP bkpt_sp : m_msgSend_bkpts) { 261 GetTarget().RemoveBreakpointByID(bkpt_sp->GetID()); 262 } 263 } 264 265 void AppleThreadPlanStepThroughDirectDispatch::GetDescription( 266 Stream *s, lldb::DescriptionLevel level) { 267 switch (level) { 268 case lldb::eDescriptionLevelBrief: 269 s->PutCString("Step through ObjC direct dispatch function."); 270 break; 271 default: 272 s->Printf("Step through ObjC direct dispatch '%s' using breakpoints: ", 273 m_dispatch_func_name.c_str()); 274 bool first = true; 275 for (auto bkpt_sp : m_msgSend_bkpts) { 276 if (!first) { 277 s->PutCString(", "); 278 } 279 first = false; 280 s->Printf("%d", bkpt_sp->GetID()); 281 } 282 (*s) << "."; 283 break; 284 } 285 } 286 287 bool 288 AppleThreadPlanStepThroughDirectDispatch::DoPlanExplainsStop(Event *event_ptr) { 289 if (ThreadPlanStepOut::DoPlanExplainsStop(event_ptr)) 290 return true; 291 292 StopInfoSP stop_info_sp = GetPrivateStopInfo(); 293 294 // Check if the breakpoint is one of ours msgSend dispatch breakpoints. 295 296 StopReason stop_reason = eStopReasonNone; 297 if (stop_info_sp) 298 stop_reason = stop_info_sp->GetStopReason(); 299 300 // See if this is one of our msgSend breakpoints: 301 if (stop_reason == eStopReasonBreakpoint) { 302 ProcessSP process_sp = GetThread().GetProcess(); 303 uint64_t break_site_id = stop_info_sp->GetValue(); 304 BreakpointSiteSP site_sp 305 = process_sp->GetBreakpointSiteList().FindByID(break_site_id); 306 // Some other plan might have deleted the site's last owner before this 307 // got to us. In which case, it wasn't our breakpoint... 308 if (!site_sp) 309 return false; 310 311 for (BreakpointSP break_sp : m_msgSend_bkpts) { 312 if (site_sp->IsBreakpointAtThisSite(break_sp->GetID())) { 313 // If we aren't the only one with a breakpoint on this site, then we 314 // should just stop and return control to the user. 315 if (site_sp->GetNumberOfOwners() > 1) { 316 SetPlanComplete(true); 317 return false; 318 } 319 m_at_msg_send = true; 320 return true; 321 } 322 } 323 } 324 325 // We're done here. If one of our sub-plans explained the stop, they 326 // would have already answered true to PlanExplainsStop, and if they were 327 // done, we'll get called to figure out what to do in ShouldStop... 328 return false; 329 } 330 331 bool AppleThreadPlanStepThroughDirectDispatch 332 ::DoWillResume(lldb::StateType resume_state, bool current_plan) { 333 ThreadPlanStepOut::DoWillResume(resume_state, current_plan); 334 m_at_msg_send = false; 335 return true; 336 } 337 338 bool AppleThreadPlanStepThroughDirectDispatch::ShouldStop(Event *event_ptr) { 339 // If step out plan finished, that means we didn't find our way into a method 340 // implementation. Either we went directly to the default implementation, 341 // of the overridden implementation didn't have debug info. 342 // So we should mark ourselves as done. 343 const bool step_out_should_stop = ThreadPlanStepOut::ShouldStop(event_ptr); 344 if (step_out_should_stop) { 345 SetPlanComplete(true); 346 return true; 347 } 348 349 // If we have a step through plan, then w're in the process of getting 350 // through an ObjC msgSend. If we arrived at the target function, then 351 // check whether we have debug info, and if we do, stop. 352 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); 353 354 if (m_objc_step_through_sp && m_objc_step_through_sp->IsPlanComplete()) { 355 // If the plan failed for some reason, we should probably just let the 356 // step over plan get us out of here... We don't need to do anything about 357 // the step through plan, it is done and will get popped when we continue. 358 if (!m_objc_step_through_sp->PlanSucceeded()) { 359 LLDB_LOGF(log, "ObjC Step through plan failed. Stepping out."); 360 } 361 Status error; 362 if (InvokeShouldStopHereCallback(eFrameCompareYounger, error)) { 363 SetPlanComplete(true); 364 return true; 365 } 366 // If we didn't want to stop at this msgSend, there might be another so 367 // we should just continue on with the step out and see if our breakpoint 368 // triggers again. 369 m_objc_step_through_sp.reset(); 370 for (BreakpointSP bkpt_sp : m_msgSend_bkpts) { 371 bkpt_sp->SetEnabled(true); 372 } 373 return false; 374 } 375 376 // If we hit an msgSend breakpoint, then we should queue the step through 377 // plan: 378 379 if (m_at_msg_send) { 380 LanguageRuntime *objc_runtime 381 = GetThread().GetProcess()->GetLanguageRuntime(eLanguageTypeObjC); 382 // There's no way we could have gotten here without an ObjC language 383 // runtime. 384 assert(objc_runtime); 385 m_objc_step_through_sp = 386 objc_runtime->GetStepThroughTrampolinePlan(GetThread(), false); 387 // If we failed to find the target for this dispatch, just keep going and 388 // let the step out complete. 389 if (!m_objc_step_through_sp) { 390 LLDB_LOG(log, "Couldn't find target for message dispatch, continuing."); 391 return false; 392 } 393 // Otherwise push the step through plan and continue. 394 GetThread().QueueThreadPlan(m_objc_step_through_sp, false); 395 for (BreakpointSP bkpt_sp : m_msgSend_bkpts) { 396 bkpt_sp->SetEnabled(false); 397 } 398 return false; 399 } 400 return true; 401 } 402 403 bool AppleThreadPlanStepThroughDirectDispatch::MischiefManaged() { 404 if (IsPlanComplete()) 405 return true; 406 return ThreadPlanStepOut::MischiefManaged(); 407 } 408