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