Home | History | Annotate | Download | only in AppleObjCRuntime
      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