Home | History | Annotate | Download | only in AppleObjCRuntime
      1 //===-- AppleObjCRuntimeV1.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 #include "AppleObjCRuntimeV1.h"
     11 #include "AppleObjCTrampolineHandler.h"
     12 #include "AppleObjCTypeVendor.h"
     13 
     14 #include "llvm/Support/MachO.h"
     15 #include "clang/AST/Type.h"
     16 
     17 #include "lldb/Breakpoint/BreakpointLocation.h"
     18 #include "lldb/Core/ConstString.h"
     19 #include "lldb/Core/Error.h"
     20 #include "lldb/Core/Log.h"
     21 #include "lldb/Core/Module.h"
     22 #include "lldb/Core/PluginManager.h"
     23 #include "lldb/Core/Scalar.h"
     24 #include "lldb/Core/StreamString.h"
     25 #include "lldb/Expression/ClangFunction.h"
     26 #include "lldb/Expression/ClangUtilityFunction.h"
     27 #include "lldb/Symbol/ClangASTContext.h"
     28 #include "lldb/Symbol/Symbol.h"
     29 #include "lldb/Target/ExecutionContext.h"
     30 #include "lldb/Target/Process.h"
     31 #include "lldb/Target/RegisterContext.h"
     32 #include "lldb/Target/Target.h"
     33 #include "lldb/Target/Thread.h"
     34 
     35 #include <vector>
     36 
     37 using namespace lldb;
     38 using namespace lldb_private;
     39 
     40 AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process) :
     41     AppleObjCRuntime (process),
     42     m_hash_signature (),
     43     m_isa_hash_table_ptr (LLDB_INVALID_ADDRESS)
     44 {
     45 }
     46 
     47 // for V1 runtime we just try to return a class name as that is the minimum level of support
     48 // required for the data formatters to work
     49 bool
     50 AppleObjCRuntimeV1::GetDynamicTypeAndAddress (ValueObject &in_value,
     51                                              lldb::DynamicValueType use_dynamic,
     52                                              TypeAndOrName &class_type_or_name,
     53                                              Address &address)
     54 {
     55     class_type_or_name.Clear();
     56     if (CouldHaveDynamicValue(in_value))
     57     {
     58         auto class_descriptor(GetClassDescriptor(in_value));
     59         if (class_descriptor && class_descriptor->IsValid() && class_descriptor->GetClassName())
     60         {
     61             const addr_t object_ptr = in_value.GetPointerValue();
     62             address.SetRawAddress(object_ptr);
     63             class_type_or_name.SetName(class_descriptor->GetClassName());
     64         }
     65     }
     66     return class_type_or_name.IsEmpty() == false;
     67 }
     68 
     69 //------------------------------------------------------------------
     70 // Static Functions
     71 //------------------------------------------------------------------
     72 lldb_private::LanguageRuntime *
     73 AppleObjCRuntimeV1::CreateInstance (Process *process, lldb::LanguageType language)
     74 {
     75     // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make
     76     // sure we aren't using the V1 runtime.
     77     if (language == eLanguageTypeObjC)
     78     {
     79         ModuleSP objc_module_sp;
     80 
     81         if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == eAppleObjC_V1)
     82             return new AppleObjCRuntimeV1 (process);
     83         else
     84             return NULL;
     85     }
     86     else
     87         return NULL;
     88 }
     89 
     90 
     91 void
     92 AppleObjCRuntimeV1::Initialize()
     93 {
     94     PluginManager::RegisterPlugin (GetPluginNameStatic(),
     95                                    "Apple Objective C Language Runtime - Version 1",
     96                                    CreateInstance);
     97 }
     98 
     99 void
    100 AppleObjCRuntimeV1::Terminate()
    101 {
    102     PluginManager::UnregisterPlugin (CreateInstance);
    103 }
    104 
    105 lldb_private::ConstString
    106 AppleObjCRuntimeV1::GetPluginNameStatic()
    107 {
    108     static ConstString g_name("apple-objc-v1");
    109     return g_name;
    110 }
    111 
    112 //------------------------------------------------------------------
    113 // PluginInterface protocol
    114 //------------------------------------------------------------------
    115 ConstString
    116 AppleObjCRuntimeV1::GetPluginName()
    117 {
    118     return GetPluginNameStatic();
    119 }
    120 
    121 uint32_t
    122 AppleObjCRuntimeV1::GetPluginVersion()
    123 {
    124     return 1;
    125 }
    126 
    127 BreakpointResolverSP
    128 AppleObjCRuntimeV1::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp)
    129 {
    130     BreakpointResolverSP resolver_sp;
    131 
    132     if (throw_bp)
    133         resolver_sp.reset (new BreakpointResolverName (bkpt,
    134                                                        "objc_exception_throw",
    135                                                        eFunctionNameTypeBase,
    136                                                        Breakpoint::Exact,
    137                                                        eLazyBoolNo));
    138     // FIXME: don't do catch yet.
    139     return resolver_sp;
    140 }
    141 
    142 struct BufStruct {
    143     char contents[2048];
    144 };
    145 
    146 ClangUtilityFunction *
    147 AppleObjCRuntimeV1::CreateObjectChecker(const char *name)
    148 {
    149     std::unique_ptr<BufStruct> buf(new BufStruct);
    150 
    151     assert(snprintf(&buf->contents[0], sizeof(buf->contents),
    152                     "struct __objc_class                                                    \n"
    153                     "{                                                                      \n"
    154                     "   struct __objc_class *isa;                                           \n"
    155                     "   struct __objc_class *super_class;                                   \n"
    156                     "   const char *name;                                                   \n"
    157                     "   // rest of struct elided because unused                             \n"
    158                     "};                                                                     \n"
    159                     "                                                                       \n"
    160                     "struct __objc_object                                                   \n"
    161                     "{                                                                      \n"
    162                     "   struct __objc_class *isa;                                           \n"
    163                     "};                                                                     \n"
    164                     "                                                                       \n"
    165                     "extern \"C\" void                                                      \n"
    166                     "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector)                  \n"
    167                     "{                                                                      \n"
    168                     "   struct __objc_object *obj = (struct __objc_object*)$__lldb_arg_obj; \n"
    169                     "   (int)strlen(obj->isa->name);                                        \n"
    170                     "}                                                                      \n",
    171                     name) < (int)sizeof(buf->contents));
    172 
    173     return new ClangUtilityFunction(buf->contents, name);
    174 }
    175 
    176 // this code relies on the assumption that an Objective-C object always starts
    177 // with an ISA at offset 0.
    178 //ObjCLanguageRuntime::ObjCISA
    179 //AppleObjCRuntimeV1::GetISA(ValueObject& valobj)
    180 //{
    181 //    ClangASTType valobj_clang_type = valobj.GetClangType();
    182 ////    if (valobj_clang_type.GetMinimumLanguage() != eLanguageTypeObjC)
    183 ////        return 0;
    184 //
    185 //    // if we get an invalid VO (which might still happen when playing around
    186 //    // with pointers returned by the expression parser, don't consider this
    187 //    // a valid ObjC object)
    188 //    if (!valobj.GetClangType().IsValid())
    189 //        return 0;
    190 //
    191 //    addr_t isa_pointer = valobj.GetPointerValue();
    192 //
    193 //    ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
    194 //
    195 //    Process *process = exe_ctx.GetProcessPtr();
    196 //    if (process)
    197 //    {
    198 //        uint8_t pointer_size = process->GetAddressByteSize();
    199 //
    200 //        Error error;
    201 //        return process->ReadUnsignedIntegerFromMemory (isa_pointer,
    202 //                                                       pointer_size,
    203 //                                                       0,
    204 //                                                       error);
    205 //    }
    206 //    return 0;
    207 //}
    208 
    209 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer)
    210 {
    211     Initialize (isa_pointer.GetValueAsUnsigned(0),
    212                 isa_pointer.GetProcessSP());
    213 }
    214 
    215 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp)
    216 {
    217     Initialize (isa, process_sp);
    218 }
    219 
    220 void
    221 AppleObjCRuntimeV1::ClassDescriptorV1::Initialize (ObjCISA isa, lldb::ProcessSP process_sp)
    222 {
    223     if (!isa || !process_sp)
    224     {
    225         m_valid = false;
    226         return;
    227     }
    228 
    229     m_valid = true;
    230 
    231     Error error;
    232 
    233     m_isa = process_sp->ReadPointerFromMemory(isa, error);
    234 
    235     if (error.Fail())
    236     {
    237         m_valid = false;
    238         return;
    239     }
    240 
    241     uint32_t ptr_size = process_sp->GetAddressByteSize();
    242 
    243     if (!IsPointerValid(m_isa,ptr_size))
    244     {
    245         m_valid = false;
    246         return;
    247     }
    248 
    249     m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size,error);
    250 
    251     if (error.Fail())
    252     {
    253         m_valid = false;
    254         return;
    255     }
    256 
    257     if (!IsPointerValid(m_parent_isa,ptr_size,true))
    258     {
    259         m_valid = false;
    260         return;
    261     }
    262 
    263     lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size,error);
    264 
    265     if (error.Fail())
    266     {
    267         m_valid = false;
    268         return;
    269     }
    270 
    271     lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
    272 
    273     size_t count = process_sp->ReadCStringFromMemory(name_ptr, (char*)buffer_sp->GetBytes(), 1024, error);
    274 
    275     if (error.Fail())
    276     {
    277         m_valid = false;
    278         return;
    279     }
    280 
    281     if (count)
    282         m_name = ConstString((char*)buffer_sp->GetBytes());
    283     else
    284         m_name = ConstString();
    285 
    286     m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(m_isa + 5 * ptr_size, ptr_size, 0, error);
    287 
    288     if (error.Fail())
    289     {
    290         m_valid = false;
    291         return;
    292     }
    293 
    294     m_process_wp = lldb::ProcessWP(process_sp);
    295 }
    296 
    297 AppleObjCRuntime::ClassDescriptorSP
    298 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass ()
    299 {
    300     if (!m_valid)
    301         return AppleObjCRuntime::ClassDescriptorSP();
    302     ProcessSP process_sp = m_process_wp.lock();
    303     if (!process_sp)
    304         return AppleObjCRuntime::ClassDescriptorSP();
    305     return ObjCLanguageRuntime::ClassDescriptorSP(new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa,process_sp));
    306 }
    307 
    308 bool
    309 AppleObjCRuntimeV1::ClassDescriptorV1::Describe (std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
    310                                                  std::function <bool (const char *, const char *)> const &instance_method_func,
    311                                                  std::function <bool (const char *, const char *)> const &class_method_func,
    312                                                  std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func)
    313 {
    314     return false;
    315 }
    316 
    317 lldb::addr_t
    318 AppleObjCRuntimeV1::GetISAHashTablePointer ()
    319 {
    320     if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS)
    321     {
    322         ModuleSP objc_module_sp(GetObjCModule());
    323 
    324         if (!objc_module_sp)
    325             return LLDB_INVALID_ADDRESS;
    326 
    327         static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
    328 
    329         const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData);
    330         if (symbol)
    331         {
    332             Process *process = GetProcess();
    333             if (process)
    334             {
    335 
    336                 lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddress().GetLoadAddress(&process->GetTarget());
    337 
    338                 if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS)
    339                 {
    340                     Error error;
    341                     lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
    342                     if (objc_debug_class_hash_ptr != 0 &&
    343                         objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS)
    344                     {
    345                         m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
    346                     }
    347                 }
    348             }
    349         }
    350     }
    351     return m_isa_hash_table_ptr;
    352 }
    353 
    354 void
    355 AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded()
    356 {
    357     // TODO: implement HashTableSignature...
    358     Process *process = GetProcess();
    359 
    360     if (process)
    361     {
    362         // Update the process stop ID that indicates the last time we updated the
    363         // map, wether it was successful or not.
    364         m_isa_to_descriptor_stop_id = process->GetStopID();
    365 
    366         Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
    367 
    368         ProcessSP process_sp = process->shared_from_this();
    369 
    370         ModuleSP objc_module_sp(GetObjCModule());
    371 
    372         if (!objc_module_sp)
    373             return;
    374 
    375         uint32_t isa_count = 0;
    376 
    377         lldb::addr_t hash_table_ptr = GetISAHashTablePointer ();
    378         if (hash_table_ptr != LLDB_INVALID_ADDRESS)
    379         {
    380             // Read the NXHashTable struct:
    381             //
    382             // typedef struct {
    383             //     const NXHashTablePrototype *prototype;
    384             //     unsigned   count;
    385             //     unsigned   nbBuckets;
    386             //     void       *buckets;
    387             //     const void *info;
    388             // } NXHashTable;
    389 
    390             Error error;
    391             DataBufferHeap buffer(1024, 0);
    392             if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 20)
    393             {
    394                 const uint32_t addr_size = m_process->GetAddressByteSize();
    395                 const ByteOrder byte_order = m_process->GetByteOrder();
    396                 DataExtractor data (buffer.GetBytes(), buffer.GetByteSize(), byte_order, addr_size);
    397                 lldb::offset_t offset = addr_size; // Skip prototype
    398                 const uint32_t count = data.GetU32(&offset);
    399                 const uint32_t num_buckets = data.GetU32(&offset);
    400                 const addr_t buckets_ptr = data.GetPointer(&offset);
    401                 if (m_hash_signature.NeedsUpdate (count, num_buckets, buckets_ptr))
    402                 {
    403                     m_hash_signature.UpdateSignature (count, num_buckets, buckets_ptr);
    404 
    405                     const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
    406                     buffer.SetByteSize(data_size);
    407 
    408                     if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, error) == data_size)
    409                     {
    410                         data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
    411                         offset = 0;
    412                         for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx)
    413                         {
    414                             const uint32_t bucket_isa_count = data.GetU32 (&offset);
    415                             const lldb::addr_t bucket_data = data.GetU32 (&offset);
    416 
    417 
    418                             if (bucket_isa_count == 0)
    419                                 continue;
    420 
    421                             isa_count += bucket_isa_count;
    422 
    423                             ObjCISA isa;
    424                             if (bucket_isa_count == 1)
    425                             {
    426                                 // When we only have one entry in the bucket, the bucket data is the "isa"
    427                                 isa = bucket_data;
    428                                 if (isa)
    429                                 {
    430                                     if (!ISAIsCached(isa))
    431                                     {
    432                                         ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp));
    433 
    434                                         if (log && log->GetVerbose())
    435                                             log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa);
    436 
    437                                         AddClass (isa, descriptor_sp);
    438                                     }
    439                                 }
    440                             }
    441                             else
    442                             {
    443                                 // When we have more than one entry in the bucket, the bucket data is a pointer
    444                                 // to an array of "isa" values
    445                                 addr_t isa_addr = bucket_data;
    446                                 for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count; ++isa_idx, isa_addr += addr_size)
    447                                 {
    448                                     isa = m_process->ReadPointerFromMemory(isa_addr, error);
    449 
    450                                     if (isa && isa != LLDB_INVALID_ADDRESS)
    451                                     {
    452                                         if (!ISAIsCached(isa))
    453                                         {
    454                                             ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp));
    455 
    456                                             if (log && log->GetVerbose())
    457                                                 log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa);
    458 
    459                                             AddClass (isa, descriptor_sp);
    460                                         }
    461                                     }
    462                                 }
    463                             }
    464                         }
    465                     }
    466                 }
    467             }
    468         }
    469     }
    470     else
    471     {
    472         m_isa_to_descriptor_stop_id = UINT32_MAX;
    473     }
    474 }
    475 
    476 TypeVendor *
    477 AppleObjCRuntimeV1::GetTypeVendor()
    478 {
    479     if (!m_type_vendor_ap.get())
    480         m_type_vendor_ap.reset(new AppleObjCTypeVendor(*this));
    481 
    482     return m_type_vendor_ap.get();
    483 }
    484