AppleObjCRuntimeV1.cpp revision 3e11c7ec050648ba865f1d451f8cb46fd39072a8
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////    if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != eLanguageTypeObjC)
182////        return 0;
183//
184//    // if we get an invalid VO (which might still happen when playing around
185//    // with pointers returned by the expression parser, don't consider this
186//    // a valid ObjC object)
187//    if (valobj.GetValue().GetContextType() == Value::eContextTypeInvalid)
188//        return 0;
189//
190//    addr_t isa_pointer = valobj.GetPointerValue();
191//
192//    ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
193//
194//    Process *process = exe_ctx.GetProcessPtr();
195//    if (process)
196//    {
197//        uint8_t pointer_size = process->GetAddressByteSize();
198//
199//        Error error;
200//        return process->ReadUnsignedIntegerFromMemory (isa_pointer,
201//                                                       pointer_size,
202//                                                       0,
203//                                                       error);
204//    }
205//    return 0;
206//}
207
208AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer)
209{
210    Initialize (isa_pointer.GetValueAsUnsigned(0),
211                isa_pointer.GetProcessSP());
212}
213
214AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp)
215{
216    Initialize (isa, process_sp);
217}
218
219void
220AppleObjCRuntimeV1::ClassDescriptorV1::Initialize (ObjCISA isa, lldb::ProcessSP process_sp)
221{
222    if (!isa || !process_sp)
223    {
224        m_valid = false;
225        return;
226    }
227
228    m_valid = true;
229
230    Error error;
231
232    m_isa = process_sp->ReadPointerFromMemory(isa, error);
233
234    if (error.Fail())
235    {
236        m_valid = false;
237        return;
238    }
239
240    uint32_t ptr_size = process_sp->GetAddressByteSize();
241
242    if (!IsPointerValid(m_isa,ptr_size))
243    {
244        m_valid = false;
245        return;
246    }
247
248    m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size,error);
249
250    if (error.Fail())
251    {
252        m_valid = false;
253        return;
254    }
255
256    if (!IsPointerValid(m_parent_isa,ptr_size,true))
257    {
258        m_valid = false;
259        return;
260    }
261
262    lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size,error);
263
264    if (error.Fail())
265    {
266        m_valid = false;
267        return;
268    }
269
270    lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
271
272    size_t count = process_sp->ReadCStringFromMemory(name_ptr, (char*)buffer_sp->GetBytes(), 1024, error);
273
274    if (error.Fail())
275    {
276        m_valid = false;
277        return;
278    }
279
280    if (count)
281        m_name = ConstString((char*)buffer_sp->GetBytes());
282    else
283        m_name = ConstString();
284
285    m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(m_isa + 5 * ptr_size, ptr_size, 0, error);
286
287    if (error.Fail())
288    {
289        m_valid = false;
290        return;
291    }
292
293    m_process_wp = lldb::ProcessWP(process_sp);
294}
295
296AppleObjCRuntime::ClassDescriptorSP
297AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass ()
298{
299    if (!m_valid)
300        return AppleObjCRuntime::ClassDescriptorSP();
301    ProcessSP process_sp = m_process_wp.lock();
302    if (!process_sp)
303        return AppleObjCRuntime::ClassDescriptorSP();
304    return ObjCLanguageRuntime::ClassDescriptorSP(new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa,process_sp));
305}
306
307bool
308AppleObjCRuntimeV1::ClassDescriptorV1::Describe (std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
309                                                 std::function <bool (const char *, const char *)> const &instance_method_func,
310                                                 std::function <bool (const char *, const char *)> const &class_method_func,
311                                                 std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func)
312{
313    return false;
314}
315
316lldb::addr_t
317AppleObjCRuntimeV1::GetISAHashTablePointer ()
318{
319    if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS)
320    {
321        ModuleSP objc_module_sp(GetObjCModule());
322
323        if (!objc_module_sp)
324            return LLDB_INVALID_ADDRESS;
325
326        static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
327
328        const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData);
329        if (symbol)
330        {
331            Process *process = GetProcess();
332            if (process)
333            {
334
335                lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddress().GetLoadAddress(&process->GetTarget());
336
337                if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS)
338                {
339                    Error error;
340                    lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
341                    if (objc_debug_class_hash_ptr != 0 &&
342                        objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS)
343                    {
344                        m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
345                    }
346                }
347            }
348        }
349    }
350    return m_isa_hash_table_ptr;
351}
352
353void
354AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded()
355{
356    // TODO: implement HashTableSignature...
357    Process *process = GetProcess();
358
359    if (process)
360    {
361        // Update the process stop ID that indicates the last time we updated the
362        // map, wether it was successful or not.
363        m_isa_to_descriptor_stop_id = process->GetStopID();
364
365        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
366
367        ProcessSP process_sp = process->shared_from_this();
368
369        ModuleSP objc_module_sp(GetObjCModule());
370
371        if (!objc_module_sp)
372            return;
373
374        uint32_t isa_count = 0;
375
376        lldb::addr_t hash_table_ptr = GetISAHashTablePointer ();
377        if (hash_table_ptr != LLDB_INVALID_ADDRESS)
378        {
379            // Read the NXHashTable struct:
380            //
381            // typedef struct {
382            //     const NXHashTablePrototype *prototype;
383            //     unsigned   count;
384            //     unsigned   nbBuckets;
385            //     void       *buckets;
386            //     const void *info;
387            // } NXHashTable;
388
389            Error error;
390            DataBufferHeap buffer(1024, 0);
391            if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 20)
392            {
393                const uint32_t addr_size = m_process->GetAddressByteSize();
394                const ByteOrder byte_order = m_process->GetByteOrder();
395                DataExtractor data (buffer.GetBytes(), buffer.GetByteSize(), byte_order, addr_size);
396                lldb::offset_t offset = addr_size; // Skip prototype
397                const uint32_t count = data.GetU32(&offset);
398                const uint32_t num_buckets = data.GetU32(&offset);
399                const addr_t buckets_ptr = data.GetPointer(&offset);
400                if (m_hash_signature.NeedsUpdate (count, num_buckets, buckets_ptr))
401                {
402                    m_hash_signature.UpdateSignature (count, num_buckets, buckets_ptr);
403
404                    const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
405                    buffer.SetByteSize(data_size);
406
407                    if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, error) == data_size)
408                    {
409                        data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
410                        offset = 0;
411                        for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx)
412                        {
413                            const uint32_t bucket_isa_count = data.GetU32 (&offset);
414                            const lldb::addr_t bucket_data = data.GetU32 (&offset);
415
416
417                            if (bucket_isa_count == 0)
418                                continue;
419
420                            isa_count += bucket_isa_count;
421
422                            ObjCISA isa;
423                            if (bucket_isa_count == 1)
424                            {
425                                // When we only have one entry in the bucket, the bucket data is the "isa"
426                                isa = bucket_data;
427                                if (isa)
428                                {
429                                    if (!ISAIsCached(isa))
430                                    {
431                                        ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp));
432
433                                        if (log && log->GetVerbose())
434                                            log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa);
435
436                                        AddClass (isa, descriptor_sp);
437                                    }
438                                }
439                            }
440                            else
441                            {
442                                // When we have more than one entry in the bucket, the bucket data is a pointer
443                                // to an array of "isa" values
444                                addr_t isa_addr = bucket_data;
445                                for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count; ++isa_idx, isa_addr += addr_size)
446                                {
447                                    isa = m_process->ReadPointerFromMemory(isa_addr, error);
448
449                                    if (isa && isa != LLDB_INVALID_ADDRESS)
450                                    {
451                                        if (!ISAIsCached(isa))
452                                        {
453                                            ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp));
454
455                                            if (log && log->GetVerbose())
456                                                log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa);
457
458                                            AddClass (isa, descriptor_sp);
459                                        }
460                                    }
461                                }
462                            }
463                        }
464                    }
465                }
466            }
467        }
468    }
469    else
470    {
471        m_isa_to_descriptor_stop_id = UINT32_MAX;
472    }
473}
474
475TypeVendor *
476AppleObjCRuntimeV1::GetTypeVendor()
477{
478    if (!m_type_vendor_ap.get())
479        m_type_vendor_ap.reset(new AppleObjCTypeVendor(*this));
480
481    return m_type_vendor_ap.get();
482}
483