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
37using namespace lldb;
38using namespace lldb_private;
39
40AppleObjCRuntimeV1::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
49bool
50AppleObjCRuntimeV1::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//------------------------------------------------------------------
72lldb_private::LanguageRuntime *
73AppleObjCRuntimeV1::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
91void
92AppleObjCRuntimeV1::Initialize()
93{
94    PluginManager::RegisterPlugin (GetPluginNameStatic(),
95                                   "Apple Objective C Language Runtime - Version 1",
96                                   CreateInstance);
97}
98
99void
100AppleObjCRuntimeV1::Terminate()
101{
102    PluginManager::UnregisterPlugin (CreateInstance);
103}
104
105lldb_private::ConstString
106AppleObjCRuntimeV1::GetPluginNameStatic()
107{
108    static ConstString g_name("apple-objc-v1");
109    return g_name;
110}
111
112//------------------------------------------------------------------
113// PluginInterface protocol
114//------------------------------------------------------------------
115ConstString
116AppleObjCRuntimeV1::GetPluginName()
117{
118    return GetPluginNameStatic();
119}
120
121uint32_t
122AppleObjCRuntimeV1::GetPluginVersion()
123{
124    return 1;
125}
126
127BreakpointResolverSP
128AppleObjCRuntimeV1::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
142struct BufStruct {
143    char contents[2048];
144};
145
146ClangUtilityFunction *
147AppleObjCRuntimeV1::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
209AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer)
210{
211    Initialize (isa_pointer.GetValueAsUnsigned(0),
212                isa_pointer.GetProcessSP());
213}
214
215AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp)
216{
217    Initialize (isa, process_sp);
218}
219
220void
221AppleObjCRuntimeV1::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
297AppleObjCRuntime::ClassDescriptorSP
298AppleObjCRuntimeV1::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
308bool
309AppleObjCRuntimeV1::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
317lldb::addr_t
318AppleObjCRuntimeV1::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
354void
355AppleObjCRuntimeV1::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
476TypeVendor *
477AppleObjCRuntimeV1::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