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