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