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