Coverage Report

Created: 2023-11-11 10:31

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