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