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