Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
39690 views
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
: 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
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 *
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
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
92
void AppleObjCRuntimeV1::Terminate() {
93
PluginManager::UnregisterPlugin(CreateInstance);
94
}
95
96
BreakpointResolverSP
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>>
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
UNUSED_IF_ASSERT_DISABLED(strformatsize);
165
166
return GetTargetRef().CreateUtilityFunction(buf->contents, std::move(name),
167
eLanguageTypeC, exe_ctx);
168
}
169
170
AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
171
ValueObject &isa_pointer) {
172
Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
173
}
174
175
AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
176
ObjCISA isa, lldb::ProcessSP process_sp) {
177
Initialize(isa, process_sp);
178
}
179
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
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
263
AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
264
return ClassDescriptorSP();
265
}
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
uint64_t)> const &ivar_func) const {
273
return false;
274
}
275
276
lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {
277
return 0;
278
}
279
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
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
429
DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {
430
return nullptr;
431
}
432
433