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 
AppleObjCRuntimeV1(Process * process)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
GetDynamicTypeAndAddress(ValueObject & in_value,lldb::DynamicValueType use_dynamic,TypeAndOrName & class_type_or_name,Address & address)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 *
CreateInstance(Process * process,lldb::LanguageType language)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
Initialize()92 AppleObjCRuntimeV1::Initialize()
93 {
94     PluginManager::RegisterPlugin (GetPluginNameStatic(),
95                                    "Apple Objective C Language Runtime - Version 1",
96                                    CreateInstance);
97 }
98 
99 void
Terminate()100 AppleObjCRuntimeV1::Terminate()
101 {
102     PluginManager::UnregisterPlugin (CreateInstance);
103 }
104 
105 lldb_private::ConstString
GetPluginNameStatic()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
GetPluginName()116 AppleObjCRuntimeV1::GetPluginName()
117 {
118     return GetPluginNameStatic();
119 }
120 
121 uint32_t
GetPluginVersion()122 AppleObjCRuntimeV1::GetPluginVersion()
123 {
124     return 1;
125 }
126 
127 BreakpointResolverSP
CreateExceptionResolver(Breakpoint * bkpt,bool catch_bp,bool throw_bp)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 *
CreateObjectChecker(const char * name)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 
ClassDescriptorV1(ValueObject & isa_pointer)209 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer)
210 {
211     Initialize (isa_pointer.GetValueAsUnsigned(0),
212                 isa_pointer.GetProcessSP());
213 }
214 
ClassDescriptorV1(ObjCISA isa,lldb::ProcessSP process_sp)215 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp)
216 {
217     Initialize (isa, process_sp);
218 }
219 
220 void
Initialize(ObjCISA isa,lldb::ProcessSP process_sp)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
GetSuperclass()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
Describe(std::function<void (ObjCLanguageRuntime::ObjCISA)> const & superclass_func,std::function<bool (const char *,const char *)> const & instance_method_func,std::function<bool (const char *,const char *)> const & class_method_func,std::function<bool (const char *,const char *,lldb::addr_t,uint64_t)> const & ivar_func)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
GetISAHashTablePointer()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
UpdateISAToDescriptorMapIfNeeded()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 *
GetTypeVendor()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