File: | build/llvm-toolchain-snapshot-16~++20220816100716+b5a18de65169/lldb/include/lldb/Target/StackFrameRecognizer.h |
Warning: | line 149, column 5 Potential memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- AppleObjCRuntimeV2.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 "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" | |||
10 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | |||
11 | ||||
12 | #include "lldb/Core/Debugger.h" | |||
13 | #include "lldb/Core/DebuggerEvents.h" | |||
14 | #include "lldb/Core/Module.h" | |||
15 | #include "lldb/Core/PluginManager.h" | |||
16 | #include "lldb/Core/Section.h" | |||
17 | #include "lldb/Core/ValueObjectConstResult.h" | |||
18 | #include "lldb/Core/ValueObjectVariable.h" | |||
19 | #include "lldb/Expression/DiagnosticManager.h" | |||
20 | #include "lldb/Expression/FunctionCaller.h" | |||
21 | #include "lldb/Expression/UtilityFunction.h" | |||
22 | #include "lldb/Host/OptionParser.h" | |||
23 | #include "lldb/Interpreter/CommandObject.h" | |||
24 | #include "lldb/Interpreter/CommandObjectMultiword.h" | |||
25 | #include "lldb/Interpreter/CommandReturnObject.h" | |||
26 | #include "lldb/Interpreter/OptionArgParser.h" | |||
27 | #include "lldb/Interpreter/OptionValueBoolean.h" | |||
28 | #include "lldb/Symbol/CompilerType.h" | |||
29 | #include "lldb/Symbol/ObjectFile.h" | |||
30 | #include "lldb/Symbol/Symbol.h" | |||
31 | #include "lldb/Symbol/TypeList.h" | |||
32 | #include "lldb/Symbol/VariableList.h" | |||
33 | #include "lldb/Target/ABI.h" | |||
34 | #include "lldb/Target/DynamicLoader.h" | |||
35 | #include "lldb/Target/ExecutionContext.h" | |||
36 | #include "lldb/Target/Platform.h" | |||
37 | #include "lldb/Target/Process.h" | |||
38 | #include "lldb/Target/RegisterContext.h" | |||
39 | #include "lldb/Target/StackFrameRecognizer.h" | |||
40 | #include "lldb/Target/Target.h" | |||
41 | #include "lldb/Target/Thread.h" | |||
42 | #include "lldb/Utility/ConstString.h" | |||
43 | #include "lldb/Utility/LLDBLog.h" | |||
44 | #include "lldb/Utility/Log.h" | |||
45 | #include "lldb/Utility/Scalar.h" | |||
46 | #include "lldb/Utility/Status.h" | |||
47 | #include "lldb/Utility/Stream.h" | |||
48 | #include "lldb/Utility/StreamString.h" | |||
49 | #include "lldb/Utility/Timer.h" | |||
50 | #include "lldb/lldb-enumerations.h" | |||
51 | ||||
52 | #include "AppleObjCClassDescriptorV2.h" | |||
53 | #include "AppleObjCDeclVendor.h" | |||
54 | #include "AppleObjCRuntimeV2.h" | |||
55 | #include "AppleObjCTrampolineHandler.h" | |||
56 | #include "AppleObjCTypeEncodingParser.h" | |||
57 | ||||
58 | #include "clang/AST/ASTContext.h" | |||
59 | #include "clang/AST/DeclObjC.h" | |||
60 | #include "clang/Basic/TargetInfo.h" | |||
61 | #include "llvm/ADT/ScopeExit.h" | |||
62 | ||||
63 | #include <cstdint> | |||
64 | #include <memory> | |||
65 | #include <string> | |||
66 | #include <vector> | |||
67 | ||||
68 | using namespace lldb; | |||
69 | using namespace lldb_private; | |||
70 | ||||
71 | char AppleObjCRuntimeV2::ID = 0; | |||
72 | ||||
73 | static const char *g_get_dynamic_class_info_name = | |||
74 | "__lldb_apple_objc_v2_get_dynamic_class_info"; | |||
75 | ||||
76 | static const char *g_get_dynamic_class_info_body = R"( | |||
77 | ||||
78 | extern "C" | |||
79 | { | |||
80 | size_t strlen(const char *); | |||
81 | char *strncpy (char * s1, const char * s2, size_t n); | |||
82 | int printf(const char * format, ...); | |||
83 | } | |||
84 | #define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__) | |||
85 | ||||
86 | typedef struct _NXMapTable { | |||
87 | void *prototype; | |||
88 | unsigned num_classes; | |||
89 | unsigned num_buckets_minus_one; | |||
90 | void *buckets; | |||
91 | } NXMapTable; | |||
92 | ||||
93 | #define NX_MAPNOTAKEY ((void *)(-1)) | |||
94 | ||||
95 | typedef struct BucketInfo | |||
96 | { | |||
97 | const char *name_ptr; | |||
98 | Class isa; | |||
99 | } BucketInfo; | |||
100 | ||||
101 | struct ClassInfo | |||
102 | { | |||
103 | Class isa; | |||
104 | uint32_t hash; | |||
105 | } __attribute__((__packed__)); | |||
106 | ||||
107 | uint32_t | |||
108 | __lldb_apple_objc_v2_get_dynamic_class_info (void *gdb_objc_realized_classes_ptr, | |||
109 | void *class_infos_ptr, | |||
110 | uint32_t class_infos_byte_size, | |||
111 | uint32_t should_log) | |||
112 | { | |||
113 | DEBUG_PRINTF ("gdb_objc_realized_classes_ptr = %p\n", gdb_objc_realized_classes_ptr); | |||
114 | DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); | |||
115 | DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size); | |||
116 | const NXMapTable *grc = (const NXMapTable *)gdb_objc_realized_classes_ptr; | |||
117 | if (grc) | |||
118 | { | |||
119 | const unsigned num_classes = grc->num_classes; | |||
120 | DEBUG_PRINTF ("num_classes = %u\n", grc->num_classes); | |||
121 | if (class_infos_ptr) | |||
122 | { | |||
123 | const unsigned num_buckets_minus_one = grc->num_buckets_minus_one; | |||
124 | DEBUG_PRINTF ("num_buckets_minus_one = %u\n", num_buckets_minus_one); | |||
125 | ||||
126 | const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); | |||
127 | DEBUG_PRINTF ("max_class_infos = %u\n", max_class_infos); | |||
128 | ||||
129 | ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; | |||
130 | BucketInfo *buckets = (BucketInfo *)grc->buckets; | |||
131 | ||||
132 | uint32_t idx = 0; | |||
133 | for (unsigned i=0; i<=num_buckets_minus_one; ++i) | |||
134 | { | |||
135 | if (buckets[i].name_ptr != NX_MAPNOTAKEY) | |||
136 | { | |||
137 | if (idx < max_class_infos) | |||
138 | { | |||
139 | const char *s = buckets[i].name_ptr; | |||
140 | uint32_t h = 5381; | |||
141 | for (unsigned char c = *s; c; c = *++s) | |||
142 | h = ((h << 5) + h) + c; | |||
143 | class_infos[idx].hash = h; | |||
144 | class_infos[idx].isa = buckets[i].isa; | |||
145 | DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, buckets[i].name_ptr); | |||
146 | } | |||
147 | ++idx; | |||
148 | } | |||
149 | } | |||
150 | if (idx < max_class_infos) | |||
151 | { | |||
152 | class_infos[idx].isa = NULL; | |||
153 | class_infos[idx].hash = 0; | |||
154 | } | |||
155 | } | |||
156 | return num_classes; | |||
157 | } | |||
158 | return 0; | |||
159 | } | |||
160 | ||||
161 | )"; | |||
162 | ||||
163 | static const char *g_get_dynamic_class_info2_name = | |||
164 | "__lldb_apple_objc_v2_get_dynamic_class_info2"; | |||
165 | ||||
166 | static const char *g_get_dynamic_class_info2_body = R"( | |||
167 | ||||
168 | extern "C" { | |||
169 | int printf(const char * format, ...); | |||
170 | void free(void *ptr); | |||
171 | Class* objc_copyRealizedClassList_nolock(unsigned int *outCount); | |||
172 | const char* objc_debug_class_getNameRaw(Class cls); | |||
173 | } | |||
174 | ||||
175 | #define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__) | |||
176 | ||||
177 | struct ClassInfo | |||
178 | { | |||
179 | Class isa; | |||
180 | uint32_t hash; | |||
181 | } __attribute__((__packed__)); | |||
182 | ||||
183 | uint32_t | |||
184 | __lldb_apple_objc_v2_get_dynamic_class_info2(void *gdb_objc_realized_classes_ptr, | |||
185 | void *class_infos_ptr, | |||
186 | uint32_t class_infos_byte_size, | |||
187 | uint32_t should_log) | |||
188 | { | |||
189 | DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); | |||
190 | DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size); | |||
191 | ||||
192 | const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); | |||
193 | DEBUG_PRINTF ("max_class_infos = %u\n", max_class_infos); | |||
194 | ||||
195 | ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; | |||
196 | ||||
197 | uint32_t count = 0; | |||
198 | Class* realized_class_list = objc_copyRealizedClassList_nolock(&count); | |||
199 | DEBUG_PRINTF ("count = %u\n", count); | |||
200 | ||||
201 | uint32_t idx = 0; | |||
202 | for (uint32_t i=0; i<count; ++i) | |||
203 | { | |||
204 | if (idx < max_class_infos) | |||
205 | { | |||
206 | Class isa = realized_class_list[i]; | |||
207 | const char *name_ptr = objc_debug_class_getNameRaw(isa); | |||
208 | if (!name_ptr) | |||
209 | continue; | |||
210 | const char *s = name_ptr; | |||
211 | uint32_t h = 5381; | |||
212 | for (unsigned char c = *s; c; c = *++s) | |||
213 | h = ((h << 5) + h) + c; | |||
214 | class_infos[idx].hash = h; | |||
215 | class_infos[idx].isa = isa; | |||
216 | DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name_ptr); | |||
217 | } | |||
218 | idx++; | |||
219 | } | |||
220 | ||||
221 | if (idx < max_class_infos) | |||
222 | { | |||
223 | class_infos[idx].isa = NULL; | |||
224 | class_infos[idx].hash = 0; | |||
225 | } | |||
226 | ||||
227 | free(realized_class_list); | |||
228 | return count; | |||
229 | } | |||
230 | )"; | |||
231 | ||||
232 | static const char *g_get_dynamic_class_info3_name = | |||
233 | "__lldb_apple_objc_v2_get_dynamic_class_info3"; | |||
234 | ||||
235 | static const char *g_get_dynamic_class_info3_body = R"( | |||
236 | ||||
237 | extern "C" { | |||
238 | int printf(const char * format, ...); | |||
239 | void free(void *ptr); | |||
240 | size_t objc_getRealizedClassList_trylock(Class *buffer, size_t len); | |||
241 | const char* objc_debug_class_getNameRaw(Class cls); | |||
242 | const char* class_getName(Class cls); | |||
243 | } | |||
244 | ||||
245 | #define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__) | |||
246 | ||||
247 | struct ClassInfo | |||
248 | { | |||
249 | Class isa; | |||
250 | uint32_t hash; | |||
251 | } __attribute__((__packed__)); | |||
252 | ||||
253 | uint32_t | |||
254 | __lldb_apple_objc_v2_get_dynamic_class_info3(void *gdb_objc_realized_classes_ptr, | |||
255 | void *class_infos_ptr, | |||
256 | uint32_t class_infos_byte_size, | |||
257 | void *class_buffer, | |||
258 | uint32_t class_buffer_len, | |||
259 | uint32_t should_log) | |||
260 | { | |||
261 | DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); | |||
262 | DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size); | |||
263 | ||||
264 | const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); | |||
265 | DEBUG_PRINTF ("max_class_infos = %u\n", max_class_infos); | |||
266 | ||||
267 | ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; | |||
268 | ||||
269 | Class *realized_class_list = (Class*)class_buffer; | |||
270 | ||||
271 | uint32_t count = objc_getRealizedClassList_trylock(realized_class_list, | |||
272 | class_buffer_len); | |||
273 | DEBUG_PRINTF ("count = %u\n", count); | |||
274 | ||||
275 | uint32_t idx = 0; | |||
276 | for (uint32_t i=0; i<count; ++i) | |||
277 | { | |||
278 | if (idx < max_class_infos) | |||
279 | { | |||
280 | Class isa = realized_class_list[i]; | |||
281 | const char *name_ptr = objc_debug_class_getNameRaw(isa); | |||
282 | if (!name_ptr) { | |||
283 | class_getName(isa); // Realize name of lazy classes. | |||
284 | name_ptr = objc_debug_class_getNameRaw(isa); | |||
285 | } | |||
286 | if (!name_ptr) | |||
287 | continue; | |||
288 | const char *s = name_ptr; | |||
289 | uint32_t h = 5381; | |||
290 | for (unsigned char c = *s; c; c = *++s) | |||
291 | h = ((h << 5) + h) + c; | |||
292 | class_infos[idx].hash = h; | |||
293 | class_infos[idx].isa = isa; | |||
294 | DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name_ptr); | |||
295 | } | |||
296 | idx++; | |||
297 | } | |||
298 | ||||
299 | if (idx < max_class_infos) | |||
300 | { | |||
301 | class_infos[idx].isa = NULL; | |||
302 | class_infos[idx].hash = 0; | |||
303 | } | |||
304 | ||||
305 | return count; | |||
306 | } | |||
307 | )"; | |||
308 | ||||
309 | // We'll substitute in class_getName or class_getNameRaw depending | |||
310 | // on which is present. | |||
311 | static const char *g_shared_cache_class_name_funcptr = R"( | |||
312 | extern "C" | |||
313 | { | |||
314 | const char *%s(void *objc_class); | |||
315 | const char *(*class_name_lookup_func)(void *) = %s; | |||
316 | } | |||
317 | )"; | |||
318 | ||||
319 | static const char *g_get_shared_cache_class_info_name = | |||
320 | "__lldb_apple_objc_v2_get_shared_cache_class_info"; | |||
321 | ||||
322 | static const char *g_get_shared_cache_class_info_body = R"( | |||
323 | ||||
324 | extern "C" | |||
325 | { | |||
326 | size_t strlen(const char *); | |||
327 | char *strncpy (char * s1, const char * s2, size_t n); | |||
328 | int printf(const char * format, ...); | |||
329 | } | |||
330 | ||||
331 | #define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__) | |||
332 | ||||
333 | ||||
334 | struct objc_classheader_t { | |||
335 | int32_t clsOffset; | |||
336 | int32_t hiOffset; | |||
337 | }; | |||
338 | ||||
339 | struct objc_classheader_v16_t { | |||
340 | uint64_t isDuplicate : 1, | |||
341 | objectCacheOffset : 47, // Offset from the shared cache base | |||
342 | dylibObjCIndex : 16; | |||
343 | }; | |||
344 | ||||
345 | struct objc_clsopt_t { | |||
346 | uint32_t capacity; | |||
347 | uint32_t occupied; | |||
348 | uint32_t shift; | |||
349 | uint32_t mask; | |||
350 | uint32_t zero; | |||
351 | uint32_t unused; | |||
352 | uint64_t salt; | |||
353 | uint32_t scramble[256]; | |||
354 | uint8_t tab[0]; // tab[mask+1] | |||
355 | // uint8_t checkbytes[capacity]; | |||
356 | // int32_t offset[capacity]; | |||
357 | // objc_classheader_t clsOffsets[capacity]; | |||
358 | // uint32_t duplicateCount; | |||
359 | // objc_classheader_t duplicateOffsets[duplicateCount]; | |||
360 | }; | |||
361 | ||||
362 | struct objc_clsopt_v16_t { | |||
363 | uint32_t version; | |||
364 | uint32_t capacity; | |||
365 | uint32_t occupied; | |||
366 | uint32_t shift; | |||
367 | uint32_t mask; | |||
368 | uint32_t zero; | |||
369 | uint64_t salt; | |||
370 | uint32_t scramble[256]; | |||
371 | uint8_t tab[0]; // tab[mask+1] | |||
372 | // uint8_t checkbytes[capacity]; | |||
373 | // int32_t offset[capacity]; | |||
374 | // objc_classheader_t clsOffsets[capacity]; | |||
375 | // uint32_t duplicateCount; | |||
376 | // objc_classheader_t duplicateOffsets[duplicateCount]; | |||
377 | }; | |||
378 | ||||
379 | struct objc_opt_t { | |||
380 | uint32_t version; | |||
381 | int32_t selopt_offset; | |||
382 | int32_t headeropt_offset; | |||
383 | int32_t clsopt_offset; | |||
384 | }; | |||
385 | ||||
386 | struct objc_opt_v14_t { | |||
387 | uint32_t version; | |||
388 | uint32_t flags; | |||
389 | int32_t selopt_offset; | |||
390 | int32_t headeropt_offset; | |||
391 | int32_t clsopt_offset; | |||
392 | }; | |||
393 | ||||
394 | struct objc_opt_v16_t { | |||
395 | uint32_t version; | |||
396 | uint32_t flags; | |||
397 | int32_t selopt_offset; | |||
398 | int32_t headeropt_ro_offset; | |||
399 | int32_t unused_clsopt_offset; | |||
400 | int32_t unused_protocolopt_offset; | |||
401 | int32_t headeropt_rw_offset; | |||
402 | int32_t unused_protocolopt2_offset; | |||
403 | int32_t largeSharedCachesClassOffset; | |||
404 | int32_t largeSharedCachesProtocolOffset; | |||
405 | uint64_t relativeMethodSelectorBaseAddressCacheOffset; | |||
406 | }; | |||
407 | ||||
408 | struct ClassInfo | |||
409 | { | |||
410 | Class isa; | |||
411 | uint32_t hash; | |||
412 | } __attribute__((__packed__)); | |||
413 | ||||
414 | uint32_t | |||
415 | __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, | |||
416 | void *shared_cache_base_ptr, | |||
417 | void *class_infos_ptr, | |||
418 | uint64_t *relative_selector_offset, | |||
419 | uint32_t class_infos_byte_size, | |||
420 | uint32_t should_log) | |||
421 | { | |||
422 | *relative_selector_offset = 0; | |||
423 | uint32_t idx = 0; | |||
424 | DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr); | |||
425 | DEBUG_PRINTF ("shared_cache_base_ptr = %p\n", shared_cache_base_ptr); | |||
426 | DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); | |||
427 | DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo))); | |||
428 | if (objc_opt_ro_ptr) | |||
429 | { | |||
430 | const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr; | |||
431 | const objc_opt_v14_t* objc_opt_v14 = (objc_opt_v14_t*)objc_opt_ro_ptr; | |||
432 | const objc_opt_v16_t* objc_opt_v16 = (objc_opt_v16_t*)objc_opt_ro_ptr; | |||
433 | if (objc_opt->version >= 16) | |||
434 | { | |||
435 | *relative_selector_offset = objc_opt_v16->relativeMethodSelectorBaseAddressCacheOffset; | |||
436 | DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v16->version); | |||
437 | DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v16->flags); | |||
438 | DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt_v16->selopt_offset); | |||
439 | DEBUG_PRINTF ("objc_opt->headeropt_ro_offset = %d\n", objc_opt_v16->headeropt_ro_offset); | |||
440 | DEBUG_PRINTF ("objc_opt->relativeMethodSelectorBaseAddressCacheOffset = %d\n", *relative_selector_offset); | |||
441 | } | |||
442 | else if (objc_opt->version >= 14) | |||
443 | { | |||
444 | DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v14->version); | |||
445 | DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v14->flags); | |||
446 | DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt_v14->selopt_offset); | |||
447 | DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt_v14->headeropt_offset); | |||
448 | DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt_v14->clsopt_offset); | |||
449 | } | |||
450 | else | |||
451 | { | |||
452 | DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt->version); | |||
453 | DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt->selopt_offset); | |||
454 | DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt->headeropt_offset); | |||
455 | DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt->clsopt_offset); | |||
456 | } | |||
457 | ||||
458 | if (objc_opt->version == 16) | |||
459 | { | |||
460 | const objc_clsopt_v16_t* clsopt = (const objc_clsopt_v16_t*)((uint8_t *)objc_opt + objc_opt_v16->largeSharedCachesClassOffset); | |||
461 | const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); | |||
462 | ||||
463 | DEBUG_PRINTF("max_class_infos = %llu\n", (uint64_t)max_class_infos); | |||
464 | ||||
465 | ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; | |||
466 | ||||
467 | const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1]; | |||
468 | const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity); | |||
469 | const objc_classheader_v16_t *classOffsets = (const objc_classheader_v16_t *)(offsets + clsopt->capacity); | |||
470 | ||||
471 | DEBUG_PRINTF ("clsopt->capacity = %u\n", clsopt->capacity); | |||
472 | DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask); | |||
473 | DEBUG_PRINTF ("classOffsets = %p\n", classOffsets); | |||
474 | ||||
475 | for (uint32_t i=0; i<clsopt->capacity; ++i) | |||
476 | { | |||
477 | const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset; | |||
478 | DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset); | |||
479 | ||||
480 | if (classOffsets[i].isDuplicate) { | |||
481 | DEBUG_PRINTF("isDuplicate = true\n"); | |||
482 | continue; // duplicate | |||
483 | } | |||
484 | ||||
485 | if (objectCacheOffset == 0) { | |||
486 | DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n"); | |||
487 | continue; // invalid offset | |||
488 | } | |||
489 | ||||
490 | if (class_infos && idx < max_class_infos) | |||
491 | { | |||
492 | class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset); | |||
493 | ||||
494 | // Lookup the class name. | |||
495 | const char *name = class_name_lookup_func(class_infos[idx].isa); | |||
496 | DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); | |||
497 | ||||
498 | // Hash the class name so we don't have to read it. | |||
499 | const char *s = name; | |||
500 | uint32_t h = 5381; | |||
501 | for (unsigned char c = *s; c; c = *++s) | |||
502 | { | |||
503 | // class_getName demangles swift names and the hash must | |||
504 | // be calculated on the mangled name. hash==0 means lldb | |||
505 | // will fetch the mangled name and compute the hash in | |||
506 | // ParseClassInfoArray. | |||
507 | if (c == '.') | |||
508 | { | |||
509 | h = 0; | |||
510 | break; | |||
511 | } | |||
512 | h = ((h << 5) + h) + c; | |||
513 | } | |||
514 | class_infos[idx].hash = h; | |||
515 | } | |||
516 | else | |||
517 | { | |||
518 | DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n"); | |||
519 | } | |||
520 | ++idx; | |||
521 | } | |||
522 | ||||
523 | const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity]; | |||
524 | const uint32_t duplicate_count = *duplicate_count_ptr; | |||
525 | const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]); | |||
526 | ||||
527 | DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); | |||
528 | DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); | |||
529 | ||||
530 | for (uint32_t i=0; i<duplicate_count; ++i) | |||
531 | { | |||
532 | const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset; | |||
533 | DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset); | |||
534 | ||||
535 | if (classOffsets[i].isDuplicate) { | |||
536 | DEBUG_PRINTF("isDuplicate = true\n"); | |||
537 | continue; // duplicate | |||
538 | } | |||
539 | ||||
540 | if (objectCacheOffset == 0) { | |||
541 | DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n"); | |||
542 | continue; // invalid offset | |||
543 | } | |||
544 | ||||
545 | if (class_infos && idx < max_class_infos) | |||
546 | { | |||
547 | class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset); | |||
548 | ||||
549 | // Lookup the class name. | |||
550 | const char *name = class_name_lookup_func(class_infos[idx].isa); | |||
551 | DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); | |||
552 | ||||
553 | // Hash the class name so we don't have to read it. | |||
554 | const char *s = name; | |||
555 | uint32_t h = 5381; | |||
556 | for (unsigned char c = *s; c; c = *++s) | |||
557 | { | |||
558 | // class_getName demangles swift names and the hash must | |||
559 | // be calculated on the mangled name. hash==0 means lldb | |||
560 | // will fetch the mangled name and compute the hash in | |||
561 | // ParseClassInfoArray. | |||
562 | if (c == '.') | |||
563 | { | |||
564 | h = 0; | |||
565 | break; | |||
566 | } | |||
567 | h = ((h << 5) + h) + c; | |||
568 | } | |||
569 | class_infos[idx].hash = h; | |||
570 | } | |||
571 | } | |||
572 | } | |||
573 | else if (objc_opt->version >= 12 && objc_opt->version <= 15) | |||
574 | { | |||
575 | const objc_clsopt_t* clsopt = NULL; | |||
576 | if (objc_opt->version >= 14) | |||
577 | clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt_v14 + objc_opt_v14->clsopt_offset); | |||
578 | else | |||
579 | clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt + objc_opt->clsopt_offset); | |||
580 | const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); | |||
581 | DEBUG_PRINTF("max_class_infos = %llu\n", (uint64_t)max_class_infos); | |||
582 | ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; | |||
583 | int32_t invalidEntryOffset = 0; | |||
584 | // this is safe to do because the version field order is invariant | |||
585 | if (objc_opt->version == 12) | |||
586 | invalidEntryOffset = 16; | |||
587 | const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1]; | |||
588 | const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity); | |||
589 | const objc_classheader_t *classOffsets = (const objc_classheader_t *)(offsets + clsopt->capacity); | |||
590 | DEBUG_PRINTF ("clsopt->capacity = %u\n", clsopt->capacity); | |||
591 | DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask); | |||
592 | DEBUG_PRINTF ("classOffsets = %p\n", classOffsets); | |||
593 | DEBUG_PRINTF("invalidEntryOffset = %d\n", invalidEntryOffset); | |||
594 | for (uint32_t i=0; i<clsopt->capacity; ++i) | |||
595 | { | |||
596 | const int32_t clsOffset = classOffsets[i].clsOffset; | |||
597 | DEBUG_PRINTF("clsOffset[%u] = %u\n", i, clsOffset); | |||
598 | if (clsOffset & 1) | |||
599 | { | |||
600 | DEBUG_PRINTF("clsOffset & 1\n"); | |||
601 | continue; // duplicate | |||
602 | } | |||
603 | else if (clsOffset == invalidEntryOffset) | |||
604 | { | |||
605 | DEBUG_PRINTF("clsOffset == invalidEntryOffset\n"); | |||
606 | continue; // invalid offset | |||
607 | } | |||
608 | ||||
609 | if (class_infos && idx < max_class_infos) | |||
610 | { | |||
611 | class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset); | |||
612 | const char *name = class_name_lookup_func (class_infos[idx].isa); | |||
613 | DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); | |||
614 | // Hash the class name so we don't have to read it | |||
615 | const char *s = name; | |||
616 | uint32_t h = 5381; | |||
617 | for (unsigned char c = *s; c; c = *++s) | |||
618 | { | |||
619 | // class_getName demangles swift names and the hash must | |||
620 | // be calculated on the mangled name. hash==0 means lldb | |||
621 | // will fetch the mangled name and compute the hash in | |||
622 | // ParseClassInfoArray. | |||
623 | if (c == '.') | |||
624 | { | |||
625 | h = 0; | |||
626 | break; | |||
627 | } | |||
628 | h = ((h << 5) + h) + c; | |||
629 | } | |||
630 | class_infos[idx].hash = h; | |||
631 | } | |||
632 | else | |||
633 | { | |||
634 | DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n"); | |||
635 | } | |||
636 | ++idx; | |||
637 | } | |||
638 | ||||
639 | const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity]; | |||
640 | const uint32_t duplicate_count = *duplicate_count_ptr; | |||
641 | const objc_classheader_t *duplicateClassOffsets = (const objc_classheader_t *)(&duplicate_count_ptr[1]); | |||
642 | DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); | |||
643 | DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); | |||
644 | for (uint32_t i=0; i<duplicate_count; ++i) | |||
645 | { | |||
646 | const int32_t clsOffset = duplicateClassOffsets[i].clsOffset; | |||
647 | if (clsOffset & 1) | |||
648 | continue; // duplicate | |||
649 | else if (clsOffset == invalidEntryOffset) | |||
650 | continue; // invalid offset | |||
651 | ||||
652 | if (class_infos && idx < max_class_infos) | |||
653 | { | |||
654 | class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset); | |||
655 | const char *name = class_name_lookup_func (class_infos[idx].isa); | |||
656 | DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); | |||
657 | // Hash the class name so we don't have to read it | |||
658 | const char *s = name; | |||
659 | uint32_t h = 5381; | |||
660 | for (unsigned char c = *s; c; c = *++s) | |||
661 | { | |||
662 | // class_getName demangles swift names and the hash must | |||
663 | // be calculated on the mangled name. hash==0 means lldb | |||
664 | // will fetch the mangled name and compute the hash in | |||
665 | // ParseClassInfoArray. | |||
666 | if (c == '.') | |||
667 | { | |||
668 | h = 0; | |||
669 | break; | |||
670 | } | |||
671 | h = ((h << 5) + h) + c; | |||
672 | } | |||
673 | class_infos[idx].hash = h; | |||
674 | } | |||
675 | ++idx; | |||
676 | } | |||
677 | } | |||
678 | DEBUG_PRINTF ("%u class_infos\n", idx); | |||
679 | DEBUG_PRINTF ("done\n"); | |||
680 | } | |||
681 | return idx; | |||
682 | } | |||
683 | ||||
684 | ||||
685 | )"; | |||
686 | ||||
687 | static uint64_t | |||
688 | ExtractRuntimeGlobalSymbol(Process *process, ConstString name, | |||
689 | const ModuleSP &module_sp, Status &error, | |||
690 | bool read_value = true, uint8_t byte_size = 0, | |||
691 | uint64_t default_value = LLDB_INVALID_ADDRESS(18446744073709551615UL), | |||
692 | SymbolType sym_type = lldb::eSymbolTypeData) { | |||
693 | if (!process) { | |||
694 | error.SetErrorString("no process"); | |||
695 | return default_value; | |||
696 | } | |||
697 | ||||
698 | if (!module_sp) { | |||
699 | error.SetErrorString("no module"); | |||
700 | return default_value; | |||
701 | } | |||
702 | ||||
703 | if (!byte_size) | |||
704 | byte_size = process->GetAddressByteSize(); | |||
705 | const Symbol *symbol = | |||
706 | module_sp->FindFirstSymbolWithNameAndType(name, lldb::eSymbolTypeData); | |||
707 | ||||
708 | if (!symbol || !symbol->ValueIsAddress()) { | |||
709 | error.SetErrorString("no symbol"); | |||
710 | return default_value; | |||
711 | } | |||
712 | ||||
713 | lldb::addr_t symbol_load_addr = | |||
714 | symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); | |||
715 | if (symbol_load_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
716 | error.SetErrorString("symbol address invalid"); | |||
717 | return default_value; | |||
718 | } | |||
719 | ||||
720 | if (read_value) | |||
721 | return process->ReadUnsignedIntegerFromMemory(symbol_load_addr, byte_size, | |||
722 | default_value, error); | |||
723 | return symbol_load_addr; | |||
724 | } | |||
725 | ||||
726 | static void RegisterObjCExceptionRecognizer(Process *process); | |||
727 | ||||
728 | AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process, | |||
729 | const ModuleSP &objc_module_sp) | |||
730 | : AppleObjCRuntime(process), m_objc_module_sp(objc_module_sp), | |||
731 | m_dynamic_class_info_extractor(*this), | |||
732 | m_shared_cache_class_info_extractor(*this), m_decl_vendor_up(), | |||
733 | m_tagged_pointer_obfuscator(LLDB_INVALID_ADDRESS(18446744073709551615UL)), | |||
734 | m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS(18446744073709551615UL)), | |||
735 | m_relative_selector_base(LLDB_INVALID_ADDRESS(18446744073709551615UL)), m_hash_signature(), | |||
736 | m_has_object_getClass(false), m_has_objc_copyRealizedClassList(false), | |||
737 | m_has_objc_getRealizedClassList_trylock(false), m_loaded_objc_opt(false), | |||
738 | m_non_pointer_isa_cache_up(), | |||
739 | m_tagged_pointer_vendor_up( | |||
740 | TaggedPointerVendorV2::CreateInstance(*this, objc_module_sp)), | |||
741 | m_encoding_to_type_sp(), m_CFBoolean_values(), | |||
742 | m_realized_class_generation_count(0) { | |||
743 | static const ConstString g_gdb_object_getClass("gdb_object_getClass"); | |||
744 | m_has_object_getClass = HasSymbol(g_gdb_object_getClass); | |||
745 | static const ConstString g_objc_copyRealizedClassList( | |||
746 | "_ZL33objc_copyRealizedClassList_nolockPj"); | |||
747 | static const ConstString g_objc_getRealizedClassList_trylock( | |||
748 | "_objc_getRealizedClassList_trylock"); | |||
749 | m_has_objc_copyRealizedClassList = HasSymbol(g_objc_copyRealizedClassList); | |||
750 | m_has_objc_getRealizedClassList_trylock = | |||
751 | HasSymbol(g_objc_getRealizedClassList_trylock); | |||
752 | WarnIfNoExpandedSharedCache(); | |||
753 | RegisterObjCExceptionRecognizer(process); | |||
754 | } | |||
755 | ||||
756 | bool AppleObjCRuntimeV2::GetDynamicTypeAndAddress( | |||
757 | ValueObject &in_value, lldb::DynamicValueType use_dynamic, | |||
758 | TypeAndOrName &class_type_or_name, Address &address, | |||
759 | Value::ValueType &value_type) { | |||
760 | // We should never get here with a null process... | |||
761 | assert(m_process != nullptr)(static_cast <bool> (m_process != nullptr) ? void (0) : __assert_fail ("m_process != nullptr", "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 761, __extension__ __PRETTY_FUNCTION__)); | |||
762 | ||||
763 | // The Runtime is attached to a particular process, you shouldn't pass in a | |||
764 | // value from another process. Note, however, the process might be NULL (e.g. | |||
765 | // if the value was made with SBTarget::EvaluateExpression...) in which case | |||
766 | // it is sufficient if the target's match: | |||
767 | ||||
768 | Process *process = in_value.GetProcessSP().get(); | |||
769 | if (process) | |||
770 | assert(process == m_process)(static_cast <bool> (process == m_process) ? void (0) : __assert_fail ("process == m_process", "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 770, __extension__ __PRETTY_FUNCTION__)); | |||
771 | else | |||
772 | assert(in_value.GetTargetSP().get() == m_process->CalculateTarget().get())(static_cast <bool> (in_value.GetTargetSP().get() == m_process ->CalculateTarget().get()) ? void (0) : __assert_fail ("in_value.GetTargetSP().get() == m_process->CalculateTarget().get()" , "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 772, __extension__ __PRETTY_FUNCTION__)); | |||
773 | ||||
774 | class_type_or_name.Clear(); | |||
775 | value_type = Value::ValueType::Scalar; | |||
776 | ||||
777 | // Make sure we can have a dynamic value before starting... | |||
778 | if (CouldHaveDynamicValue(in_value)) { | |||
779 | // First job, pull out the address at 0 offset from the object That will | |||
780 | // be the ISA pointer. | |||
781 | ClassDescriptorSP objc_class_sp(GetNonKVOClassDescriptor(in_value)); | |||
782 | if (objc_class_sp) { | |||
783 | const addr_t object_ptr = in_value.GetPointerValue(); | |||
784 | address.SetRawAddress(object_ptr); | |||
785 | ||||
786 | ConstString class_name(objc_class_sp->GetClassName()); | |||
787 | class_type_or_name.SetName(class_name); | |||
788 | TypeSP type_sp(objc_class_sp->GetType()); | |||
789 | if (type_sp) | |||
790 | class_type_or_name.SetTypeSP(type_sp); | |||
791 | else { | |||
792 | type_sp = LookupInCompleteClassCache(class_name); | |||
793 | if (type_sp) { | |||
794 | objc_class_sp->SetType(type_sp); | |||
795 | class_type_or_name.SetTypeSP(type_sp); | |||
796 | } else { | |||
797 | // try to go for a CompilerType at least | |||
798 | if (auto *vendor = GetDeclVendor()) { | |||
799 | auto types = vendor->FindTypes(class_name, /*max_matches*/ 1); | |||
800 | if (!types.empty()) | |||
801 | class_type_or_name.SetCompilerType(types.front()); | |||
802 | } | |||
803 | } | |||
804 | } | |||
805 | } | |||
806 | } | |||
807 | return !class_type_or_name.IsEmpty(); | |||
808 | } | |||
809 | ||||
810 | // Static Functions | |||
811 | LanguageRuntime *AppleObjCRuntimeV2::CreateInstance(Process *process, | |||
812 | LanguageType language) { | |||
813 | // FIXME: This should be a MacOS or iOS process, and we need to look for the | |||
814 | // OBJC section to make | |||
815 | // sure we aren't using the V1 runtime. | |||
816 | if (language == eLanguageTypeObjC) { | |||
817 | ModuleSP objc_module_sp; | |||
818 | ||||
819 | if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) == | |||
820 | ObjCRuntimeVersions::eAppleObjC_V2) | |||
821 | return new AppleObjCRuntimeV2(process, objc_module_sp); | |||
822 | return nullptr; | |||
823 | } | |||
824 | return nullptr; | |||
825 | } | |||
826 | ||||
827 | static constexpr OptionDefinition g_objc_classtable_dump_options[] = { | |||
828 | {LLDB_OPT_SET_ALL0xFFFFFFFFU, | |||
829 | false, | |||
830 | "verbose", | |||
831 | 'v', | |||
832 | OptionParser::eNoArgument, | |||
833 | nullptr, | |||
834 | {}, | |||
835 | 0, | |||
836 | eArgTypeNone, | |||
837 | "Print ivar and method information in detail"}}; | |||
838 | ||||
839 | class CommandObjectObjC_ClassTable_Dump : public CommandObjectParsed { | |||
840 | public: | |||
841 | class CommandOptions : public Options { | |||
842 | public: | |||
843 | CommandOptions() : Options(), m_verbose(false, false) {} | |||
844 | ||||
845 | ~CommandOptions() override = default; | |||
846 | ||||
847 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |||
848 | ExecutionContext *execution_context) override { | |||
849 | Status error; | |||
850 | const int short_option = m_getopt_table[option_idx].val; | |||
851 | switch (short_option) { | |||
852 | case 'v': | |||
853 | m_verbose.SetCurrentValue(true); | |||
854 | m_verbose.SetOptionWasSet(); | |||
855 | break; | |||
856 | ||||
857 | default: | |||
858 | error.SetErrorStringWithFormat("unrecognized short option '%c'", | |||
859 | short_option); | |||
860 | break; | |||
861 | } | |||
862 | ||||
863 | return error; | |||
864 | } | |||
865 | ||||
866 | void OptionParsingStarting(ExecutionContext *execution_context) override { | |||
867 | m_verbose.Clear(); | |||
868 | } | |||
869 | ||||
870 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |||
871 | return llvm::makeArrayRef(g_objc_classtable_dump_options); | |||
872 | } | |||
873 | ||||
874 | OptionValueBoolean m_verbose; | |||
875 | }; | |||
876 | ||||
877 | CommandObjectObjC_ClassTable_Dump(CommandInterpreter &interpreter) | |||
878 | : CommandObjectParsed(interpreter, "dump", | |||
879 | "Dump information on Objective-C classes " | |||
880 | "known to the current process.", | |||
881 | "language objc class-table dump", | |||
882 | eCommandRequiresProcess | | |||
883 | eCommandProcessMustBeLaunched | | |||
884 | eCommandProcessMustBePaused), | |||
885 | m_options() { | |||
886 | CommandArgumentEntry arg; | |||
887 | CommandArgumentData index_arg; | |||
888 | ||||
889 | // Define the first (and only) variant of this arg. | |||
890 | index_arg.arg_type = eArgTypeRegularExpression; | |||
891 | index_arg.arg_repetition = eArgRepeatOptional; | |||
892 | ||||
893 | // There is only one variant this argument could be; put it into the | |||
894 | // argument entry. | |||
895 | arg.push_back(index_arg); | |||
896 | ||||
897 | // Push the data for the first argument into the m_arguments vector. | |||
898 | m_arguments.push_back(arg); | |||
899 | } | |||
900 | ||||
901 | ~CommandObjectObjC_ClassTable_Dump() override = default; | |||
902 | ||||
903 | Options *GetOptions() override { return &m_options; } | |||
904 | ||||
905 | protected: | |||
906 | bool DoExecute(Args &command, CommandReturnObject &result) override { | |||
907 | std::unique_ptr<RegularExpression> regex_up; | |||
908 | switch (command.GetArgumentCount()) { | |||
909 | case 0: | |||
910 | break; | |||
911 | case 1: { | |||
912 | regex_up = | |||
913 | std::make_unique<RegularExpression>(command.GetArgumentAtIndex(0)); | |||
914 | if (!regex_up->IsValid()) { | |||
915 | result.AppendError( | |||
916 | "invalid argument - please provide a valid regular expression"); | |||
917 | result.SetStatus(lldb::eReturnStatusFailed); | |||
918 | return false; | |||
919 | } | |||
920 | break; | |||
921 | } | |||
922 | default: { | |||
923 | result.AppendError("please provide 0 or 1 arguments"); | |||
924 | result.SetStatus(lldb::eReturnStatusFailed); | |||
925 | return false; | |||
926 | } | |||
927 | } | |||
928 | ||||
929 | Process *process = m_exe_ctx.GetProcessPtr(); | |||
930 | ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process); | |||
931 | if (objc_runtime) { | |||
932 | auto iterators_pair = objc_runtime->GetDescriptorIteratorPair(); | |||
933 | auto iterator = iterators_pair.first; | |||
934 | auto &std_out = result.GetOutputStream(); | |||
935 | for (; iterator != iterators_pair.second; iterator++) { | |||
936 | if (iterator->second) { | |||
937 | const char *class_name = | |||
938 | iterator->second->GetClassName().AsCString("<unknown>"); | |||
939 | if (regex_up && class_name && | |||
940 | !regex_up->Execute(llvm::StringRef(class_name))) | |||
941 | continue; | |||
942 | std_out.Printf("isa = 0x%" PRIx64"l" "x", iterator->first); | |||
943 | std_out.Printf(" name = %s", class_name); | |||
944 | std_out.Printf(" instance size = %" PRIu64"l" "u", | |||
945 | iterator->second->GetInstanceSize()); | |||
946 | std_out.Printf(" num ivars = %" PRIuPTR"l" "u", | |||
947 | (uintptr_t)iterator->second->GetNumIVars()); | |||
948 | if (auto superclass = iterator->second->GetSuperclass()) { | |||
949 | std_out.Printf(" superclass = %s", | |||
950 | superclass->GetClassName().AsCString("<unknown>")); | |||
951 | } | |||
952 | std_out.Printf("\n"); | |||
953 | if (m_options.m_verbose) { | |||
954 | for (size_t i = 0; i < iterator->second->GetNumIVars(); i++) { | |||
955 | auto ivar = iterator->second->GetIVarAtIndex(i); | |||
956 | std_out.Printf( | |||
957 | " ivar name = %s type = %s size = %" PRIu64"l" "u" | |||
958 | " offset = %" PRId32"d" "\n", | |||
959 | ivar.m_name.AsCString("<unknown>"), | |||
960 | ivar.m_type.GetDisplayTypeName().AsCString("<unknown>"), | |||
961 | ivar.m_size, ivar.m_offset); | |||
962 | } | |||
963 | ||||
964 | iterator->second->Describe( | |||
965 | nullptr, | |||
966 | [&std_out](const char *name, const char *type) -> bool { | |||
967 | std_out.Printf(" instance method name = %s type = %s\n", | |||
968 | name, type); | |||
969 | return false; | |||
970 | }, | |||
971 | [&std_out](const char *name, const char *type) -> bool { | |||
972 | std_out.Printf(" class method name = %s type = %s\n", name, | |||
973 | type); | |||
974 | return false; | |||
975 | }, | |||
976 | nullptr); | |||
977 | } | |||
978 | } else { | |||
979 | if (regex_up && !regex_up->Execute(llvm::StringRef())) | |||
980 | continue; | |||
981 | std_out.Printf("isa = 0x%" PRIx64"l" "x" " has no associated class.\n", | |||
982 | iterator->first); | |||
983 | } | |||
984 | } | |||
985 | result.SetStatus(lldb::eReturnStatusSuccessFinishResult); | |||
986 | return true; | |||
987 | } | |||
988 | result.AppendError("current process has no Objective-C runtime loaded"); | |||
989 | result.SetStatus(lldb::eReturnStatusFailed); | |||
990 | return false; | |||
991 | } | |||
992 | ||||
993 | CommandOptions m_options; | |||
994 | }; | |||
995 | ||||
996 | class CommandObjectMultiwordObjC_TaggedPointer_Info | |||
997 | : public CommandObjectParsed { | |||
998 | public: | |||
999 | CommandObjectMultiwordObjC_TaggedPointer_Info(CommandInterpreter &interpreter) | |||
1000 | : CommandObjectParsed( | |||
1001 | interpreter, "info", "Dump information on a tagged pointer.", | |||
1002 | "language objc tagged-pointer info", | |||
1003 | eCommandRequiresProcess | eCommandProcessMustBeLaunched | | |||
1004 | eCommandProcessMustBePaused) { | |||
1005 | CommandArgumentEntry arg; | |||
1006 | CommandArgumentData index_arg; | |||
1007 | ||||
1008 | // Define the first (and only) variant of this arg. | |||
1009 | index_arg.arg_type = eArgTypeAddress; | |||
1010 | index_arg.arg_repetition = eArgRepeatPlus; | |||
1011 | ||||
1012 | // There is only one variant this argument could be; put it into the | |||
1013 | // argument entry. | |||
1014 | arg.push_back(index_arg); | |||
1015 | ||||
1016 | // Push the data for the first argument into the m_arguments vector. | |||
1017 | m_arguments.push_back(arg); | |||
1018 | } | |||
1019 | ||||
1020 | ~CommandObjectMultiwordObjC_TaggedPointer_Info() override = default; | |||
1021 | ||||
1022 | protected: | |||
1023 | bool DoExecute(Args &command, CommandReturnObject &result) override { | |||
1024 | if (command.GetArgumentCount() == 0) { | |||
1025 | result.AppendError("this command requires arguments"); | |||
1026 | result.SetStatus(lldb::eReturnStatusFailed); | |||
1027 | return false; | |||
1028 | } | |||
1029 | ||||
1030 | Process *process = m_exe_ctx.GetProcessPtr(); | |||
1031 | ExecutionContext exe_ctx(process); | |||
1032 | ||||
1033 | ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process); | |||
1034 | if (!objc_runtime) { | |||
1035 | result.AppendError("current process has no Objective-C runtime loaded"); | |||
1036 | result.SetStatus(lldb::eReturnStatusFailed); | |||
1037 | return false; | |||
1038 | } | |||
1039 | ||||
1040 | ObjCLanguageRuntime::TaggedPointerVendor *tagged_ptr_vendor = | |||
1041 | objc_runtime->GetTaggedPointerVendor(); | |||
1042 | if (!tagged_ptr_vendor) { | |||
1043 | result.AppendError("current process has no tagged pointer support"); | |||
1044 | result.SetStatus(lldb::eReturnStatusFailed); | |||
1045 | return false; | |||
1046 | } | |||
1047 | ||||
1048 | for (size_t i = 0; i < command.GetArgumentCount(); i++) { | |||
1049 | const char *arg_str = command.GetArgumentAtIndex(i); | |||
1050 | if (!arg_str) | |||
1051 | continue; | |||
1052 | ||||
1053 | Status error; | |||
1054 | lldb::addr_t arg_addr = OptionArgParser::ToAddress( | |||
1055 | &exe_ctx, arg_str, LLDB_INVALID_ADDRESS(18446744073709551615UL), &error); | |||
1056 | if (arg_addr == 0 || arg_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL) || error.Fail()) { | |||
1057 | result.AppendErrorWithFormatv( | |||
1058 | "could not convert '{0}' to a valid address\n", arg_str); | |||
1059 | result.SetStatus(lldb::eReturnStatusFailed); | |||
1060 | return false; | |||
1061 | } | |||
1062 | ||||
1063 | if (!tagged_ptr_vendor->IsPossibleTaggedPointer(arg_addr)) { | |||
1064 | result.GetOutputStream().Format("{0:x16} is not tagged\n", arg_addr); | |||
1065 | continue; | |||
1066 | } | |||
1067 | ||||
1068 | auto descriptor_sp = tagged_ptr_vendor->GetClassDescriptor(arg_addr); | |||
1069 | if (!descriptor_sp) { | |||
1070 | result.AppendErrorWithFormatv( | |||
1071 | "could not get class descriptor for {0:x16}\n", arg_addr); | |||
1072 | result.SetStatus(lldb::eReturnStatusFailed); | |||
1073 | return false; | |||
1074 | } | |||
1075 | ||||
1076 | uint64_t info_bits = 0; | |||
1077 | uint64_t value_bits = 0; | |||
1078 | uint64_t payload = 0; | |||
1079 | if (descriptor_sp->GetTaggedPointerInfo(&info_bits, &value_bits, | |||
1080 | &payload)) { | |||
1081 | result.GetOutputStream().Format( | |||
1082 | "{0:x} is tagged\n" | |||
1083 | "\tpayload = {1:x16}\n" | |||
1084 | "\tvalue = {2:x16}\n" | |||
1085 | "\tinfo bits = {3:x16}\n" | |||
1086 | "\tclass = {4}\n", | |||
1087 | arg_addr, payload, value_bits, info_bits, | |||
1088 | descriptor_sp->GetClassName().AsCString("<unknown>")); | |||
1089 | } else { | |||
1090 | result.GetOutputStream().Format("{0:x16} is not tagged\n", arg_addr); | |||
1091 | } | |||
1092 | } | |||
1093 | ||||
1094 | result.SetStatus(lldb::eReturnStatusSuccessFinishResult); | |||
1095 | return true; | |||
1096 | } | |||
1097 | }; | |||
1098 | ||||
1099 | class CommandObjectMultiwordObjC_ClassTable : public CommandObjectMultiword { | |||
1100 | public: | |||
1101 | CommandObjectMultiwordObjC_ClassTable(CommandInterpreter &interpreter) | |||
1102 | : CommandObjectMultiword( | |||
1103 | interpreter, "class-table", | |||
1104 | "Commands for operating on the Objective-C class table.", | |||
1105 | "class-table <subcommand> [<subcommand-options>]") { | |||
1106 | LoadSubCommand( | |||
1107 | "dump", | |||
1108 | CommandObjectSP(new CommandObjectObjC_ClassTable_Dump(interpreter))); | |||
1109 | } | |||
1110 | ||||
1111 | ~CommandObjectMultiwordObjC_ClassTable() override = default; | |||
1112 | }; | |||
1113 | ||||
1114 | class CommandObjectMultiwordObjC_TaggedPointer : public CommandObjectMultiword { | |||
1115 | public: | |||
1116 | CommandObjectMultiwordObjC_TaggedPointer(CommandInterpreter &interpreter) | |||
1117 | : CommandObjectMultiword( | |||
1118 | interpreter, "tagged-pointer", | |||
1119 | "Commands for operating on Objective-C tagged pointers.", | |||
1120 | "class-table <subcommand> [<subcommand-options>]") { | |||
1121 | LoadSubCommand( | |||
1122 | "info", | |||
1123 | CommandObjectSP( | |||
1124 | new CommandObjectMultiwordObjC_TaggedPointer_Info(interpreter))); | |||
1125 | } | |||
1126 | ||||
1127 | ~CommandObjectMultiwordObjC_TaggedPointer() override = default; | |||
1128 | }; | |||
1129 | ||||
1130 | class CommandObjectMultiwordObjC : public CommandObjectMultiword { | |||
1131 | public: | |||
1132 | CommandObjectMultiwordObjC(CommandInterpreter &interpreter) | |||
1133 | : CommandObjectMultiword( | |||
1134 | interpreter, "objc", | |||
1135 | "Commands for operating on the Objective-C language runtime.", | |||
1136 | "objc <subcommand> [<subcommand-options>]") { | |||
1137 | LoadSubCommand("class-table", | |||
1138 | CommandObjectSP( | |||
1139 | new CommandObjectMultiwordObjC_ClassTable(interpreter))); | |||
1140 | LoadSubCommand("tagged-pointer", | |||
1141 | CommandObjectSP(new CommandObjectMultiwordObjC_TaggedPointer( | |||
1142 | interpreter))); | |||
1143 | } | |||
1144 | ||||
1145 | ~CommandObjectMultiwordObjC() override = default; | |||
1146 | }; | |||
1147 | ||||
1148 | void AppleObjCRuntimeV2::Initialize() { | |||
1149 | PluginManager::RegisterPlugin( | |||
1150 | GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 2", | |||
1151 | CreateInstance, | |||
1152 | [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP { | |||
1153 | return CommandObjectSP(new CommandObjectMultiwordObjC(interpreter)); | |||
1154 | }, | |||
1155 | GetBreakpointExceptionPrecondition); | |||
1156 | } | |||
1157 | ||||
1158 | void AppleObjCRuntimeV2::Terminate() { | |||
1159 | PluginManager::UnregisterPlugin(CreateInstance); | |||
1160 | } | |||
1161 | ||||
1162 | BreakpointResolverSP | |||
1163 | AppleObjCRuntimeV2::CreateExceptionResolver(const BreakpointSP &bkpt, | |||
1164 | bool catch_bp, bool throw_bp) { | |||
1165 | BreakpointResolverSP resolver_sp; | |||
1166 | ||||
1167 | if (throw_bp) | |||
1168 | resolver_sp = std::make_shared<BreakpointResolverName>( | |||
1169 | bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), | |||
1170 | eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, | |||
1171 | eLazyBoolNo); | |||
1172 | // FIXME: We don't do catch breakpoints for ObjC yet. | |||
1173 | // Should there be some way for the runtime to specify what it can do in this | |||
1174 | // regard? | |||
1175 | return resolver_sp; | |||
1176 | } | |||
1177 | ||||
1178 | llvm::Expected<std::unique_ptr<UtilityFunction>> | |||
1179 | AppleObjCRuntimeV2::CreateObjectChecker(std::string name, | |||
1180 | ExecutionContext &exe_ctx) { | |||
1181 | char check_function_code[2048]; | |||
1182 | ||||
1183 | int len = 0; | |||
1184 | if (m_has_object_getClass) { | |||
1185 | len = ::snprintf(check_function_code, sizeof(check_function_code), R"( | |||
1186 | extern "C" void *gdb_object_getClass(void *); | |||
1187 | extern "C" int printf(const char *format, ...); | |||
1188 | extern "C" void | |||
1189 | %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) { | |||
1190 | if ($__lldb_arg_obj == (void *)0) | |||
1191 | return; // nil is ok | |||
1192 | if (!gdb_object_getClass($__lldb_arg_obj)) { | |||
1193 | *((volatile int *)0) = 'ocgc'; | |||
1194 | } else if ($__lldb_arg_selector != (void *)0) { | |||
1195 | signed char $responds = (signed char) | |||
1196 | [(id)$__lldb_arg_obj respondsToSelector: | |||
1197 | (void *) $__lldb_arg_selector]; | |||
1198 | if ($responds == (signed char) 0) | |||
1199 | *((volatile int *)0) = 'ocgc'; | |||
1200 | } | |||
1201 | })", | |||
1202 | name.c_str()); | |||
1203 | } else { | |||
1204 | len = ::snprintf(check_function_code, sizeof(check_function_code), R"( | |||
1205 | extern "C" void *gdb_class_getClass(void *); | |||
1206 | extern "C" int printf(const char *format, ...); | |||
1207 | extern "C" void | |||
1208 | %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) { | |||
1209 | if ($__lldb_arg_obj == (void *)0) | |||
1210 | return; // nil is ok | |||
1211 | void **$isa_ptr = (void **)$__lldb_arg_obj; | |||
1212 | if (*$isa_ptr == (void *)0 || | |||
1213 | !gdb_class_getClass(*$isa_ptr)) | |||
1214 | *((volatile int *)0) = 'ocgc'; | |||
1215 | else if ($__lldb_arg_selector != (void *)0) { | |||
1216 | signed char $responds = (signed char) | |||
1217 | [(id)$__lldb_arg_obj respondsToSelector: | |||
1218 | (void *) $__lldb_arg_selector]; | |||
1219 | if ($responds == (signed char) 0) | |||
1220 | *((volatile int *)0) = 'ocgc'; | |||
1221 | } | |||
1222 | })", | |||
1223 | name.c_str()); | |||
1224 | } | |||
1225 | ||||
1226 | assert(len < (int)sizeof(check_function_code))(static_cast <bool> (len < (int)sizeof(check_function_code )) ? void (0) : __assert_fail ("len < (int)sizeof(check_function_code)" , "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 1226, __extension__ __PRETTY_FUNCTION__)); | |||
1227 | UNUSED_IF_ASSERT_DISABLED(len)((void)(len)); | |||
1228 | ||||
1229 | return GetTargetRef().CreateUtilityFunction(check_function_code, name, | |||
1230 | eLanguageTypeC, exe_ctx); | |||
1231 | } | |||
1232 | ||||
1233 | size_t AppleObjCRuntimeV2::GetByteOffsetForIvar(CompilerType &parent_ast_type, | |||
1234 | const char *ivar_name) { | |||
1235 | uint32_t ivar_offset = LLDB_INVALID_IVAR_OFFSET(4294967295U); | |||
1236 | ||||
1237 | ConstString class_name = parent_ast_type.GetTypeName(); | |||
1238 | if (!class_name.IsEmpty() && ivar_name && ivar_name[0]) { | |||
1239 | // Make the objective C V2 mangled name for the ivar offset from the class | |||
1240 | // name and ivar name | |||
1241 | std::string buffer("OBJC_IVAR_$_"); | |||
1242 | buffer.append(class_name.AsCString()); | |||
1243 | buffer.push_back('.'); | |||
1244 | buffer.append(ivar_name); | |||
1245 | ConstString ivar_const_str(buffer.c_str()); | |||
1246 | ||||
1247 | // Try to get the ivar offset address from the symbol table first using the | |||
1248 | // name we created above | |||
1249 | SymbolContextList sc_list; | |||
1250 | Target &target = m_process->GetTarget(); | |||
1251 | target.GetImages().FindSymbolsWithNameAndType(ivar_const_str, | |||
1252 | eSymbolTypeObjCIVar, sc_list); | |||
1253 | ||||
1254 | addr_t ivar_offset_address = LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1255 | ||||
1256 | Status error; | |||
1257 | SymbolContext ivar_offset_symbol; | |||
1258 | if (sc_list.GetSize() == 1 && | |||
1259 | sc_list.GetContextAtIndex(0, ivar_offset_symbol)) { | |||
1260 | if (ivar_offset_symbol.symbol) | |||
1261 | ivar_offset_address = | |||
1262 | ivar_offset_symbol.symbol->GetLoadAddress(&target); | |||
1263 | } | |||
1264 | ||||
1265 | // If we didn't get the ivar offset address from the symbol table, fall | |||
1266 | // back to getting it from the runtime | |||
1267 | if (ivar_offset_address == LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1268 | ivar_offset_address = LookupRuntimeSymbol(ivar_const_str); | |||
1269 | ||||
1270 | if (ivar_offset_address != LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1271 | ivar_offset = m_process->ReadUnsignedIntegerFromMemory( | |||
1272 | ivar_offset_address, 4, LLDB_INVALID_IVAR_OFFSET(4294967295U), error); | |||
1273 | } | |||
1274 | return ivar_offset; | |||
1275 | } | |||
1276 | ||||
1277 | // tagged pointers are special not-a-real-pointer values that contain both type | |||
1278 | // and value information this routine attempts to check with as little | |||
1279 | // computational effort as possible whether something could possibly be a | |||
1280 | // tagged pointer - false positives are possible but false negatives shouldn't | |||
1281 | bool AppleObjCRuntimeV2::IsTaggedPointer(addr_t ptr) { | |||
1282 | if (!m_tagged_pointer_vendor_up) | |||
1283 | return false; | |||
1284 | return m_tagged_pointer_vendor_up->IsPossibleTaggedPointer(ptr); | |||
1285 | } | |||
1286 | ||||
1287 | class RemoteNXMapTable { | |||
1288 | public: | |||
1289 | RemoteNXMapTable() : m_end_iterator(*this, -1) {} | |||
1290 | ||||
1291 | void Dump() { | |||
1292 | printf("RemoteNXMapTable.m_load_addr = 0x%" PRIx64"l" "x" "\n", m_load_addr); | |||
1293 | printf("RemoteNXMapTable.m_count = %u\n", m_count); | |||
1294 | printf("RemoteNXMapTable.m_num_buckets_minus_one = %u\n", | |||
1295 | m_num_buckets_minus_one); | |||
1296 | printf("RemoteNXMapTable.m_buckets_ptr = 0x%" PRIX64"l" "X" "\n", m_buckets_ptr); | |||
1297 | } | |||
1298 | ||||
1299 | bool ParseHeader(Process *process, lldb::addr_t load_addr) { | |||
1300 | m_process = process; | |||
1301 | m_load_addr = load_addr; | |||
1302 | m_map_pair_size = m_process->GetAddressByteSize() * 2; | |||
1303 | m_invalid_key = | |||
1304 | m_process->GetAddressByteSize() == 8 ? UINT64_MAX(18446744073709551615UL) : UINT32_MAX(4294967295U); | |||
1305 | Status err; | |||
1306 | ||||
1307 | // This currently holds true for all platforms we support, but we might | |||
1308 | // need to change this to use get the actually byte size of "unsigned" from | |||
1309 | // the target AST... | |||
1310 | const uint32_t unsigned_byte_size = sizeof(uint32_t); | |||
1311 | // Skip the prototype as we don't need it (const struct | |||
1312 | // +NXMapTablePrototype *prototype) | |||
1313 | ||||
1314 | bool success = true; | |||
1315 | if (load_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1316 | success = false; | |||
1317 | else { | |||
1318 | lldb::addr_t cursor = load_addr + m_process->GetAddressByteSize(); | |||
1319 | ||||
1320 | // unsigned count; | |||
1321 | m_count = m_process->ReadUnsignedIntegerFromMemory( | |||
1322 | cursor, unsigned_byte_size, 0, err); | |||
1323 | if (m_count) { | |||
1324 | cursor += unsigned_byte_size; | |||
1325 | ||||
1326 | // unsigned nbBucketsMinusOne; | |||
1327 | m_num_buckets_minus_one = m_process->ReadUnsignedIntegerFromMemory( | |||
1328 | cursor, unsigned_byte_size, 0, err); | |||
1329 | cursor += unsigned_byte_size; | |||
1330 | ||||
1331 | // void *buckets; | |||
1332 | m_buckets_ptr = m_process->ReadPointerFromMemory(cursor, err); | |||
1333 | ||||
1334 | success = m_count > 0 && m_buckets_ptr != LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1335 | } | |||
1336 | } | |||
1337 | ||||
1338 | if (!success) { | |||
1339 | m_count = 0; | |||
1340 | m_num_buckets_minus_one = 0; | |||
1341 | m_buckets_ptr = LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1342 | } | |||
1343 | return success; | |||
1344 | } | |||
1345 | ||||
1346 | // const_iterator mimics NXMapState and its code comes from NXInitMapState | |||
1347 | // and NXNextMapState. | |||
1348 | typedef std::pair<ConstString, ObjCLanguageRuntime::ObjCISA> element; | |||
1349 | ||||
1350 | friend class const_iterator; | |||
1351 | class const_iterator { | |||
1352 | public: | |||
1353 | const_iterator(RemoteNXMapTable &parent, int index) | |||
1354 | : m_parent(parent), m_index(index) { | |||
1355 | AdvanceToValidIndex(); | |||
1356 | } | |||
1357 | ||||
1358 | const_iterator(const const_iterator &rhs) | |||
1359 | : m_parent(rhs.m_parent), m_index(rhs.m_index) { | |||
1360 | // AdvanceToValidIndex() has been called by rhs already. | |||
1361 | } | |||
1362 | ||||
1363 | const_iterator &operator=(const const_iterator &rhs) { | |||
1364 | // AdvanceToValidIndex() has been called by rhs already. | |||
1365 | assert(&m_parent == &rhs.m_parent)(static_cast <bool> (&m_parent == &rhs.m_parent ) ? void (0) : __assert_fail ("&m_parent == &rhs.m_parent" , "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 1365, __extension__ __PRETTY_FUNCTION__)); | |||
1366 | m_index = rhs.m_index; | |||
1367 | return *this; | |||
1368 | } | |||
1369 | ||||
1370 | bool operator==(const const_iterator &rhs) const { | |||
1371 | if (&m_parent != &rhs.m_parent) | |||
1372 | return false; | |||
1373 | if (m_index != rhs.m_index) | |||
1374 | return false; | |||
1375 | ||||
1376 | return true; | |||
1377 | } | |||
1378 | ||||
1379 | bool operator!=(const const_iterator &rhs) const { | |||
1380 | return !(operator==(rhs)); | |||
1381 | } | |||
1382 | ||||
1383 | const_iterator &operator++() { | |||
1384 | AdvanceToValidIndex(); | |||
1385 | return *this; | |||
1386 | } | |||
1387 | ||||
1388 | const element operator*() const { | |||
1389 | if (m_index == -1) { | |||
1390 | // TODO find a way to make this an error, but not an assert | |||
1391 | return element(); | |||
1392 | } | |||
1393 | ||||
1394 | lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr; | |||
1395 | size_t map_pair_size = m_parent.m_map_pair_size; | |||
1396 | lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size); | |||
1397 | ||||
1398 | Status err; | |||
1399 | ||||
1400 | lldb::addr_t key = | |||
1401 | m_parent.m_process->ReadPointerFromMemory(pair_ptr, err); | |||
1402 | if (!err.Success()) | |||
1403 | return element(); | |||
1404 | lldb::addr_t value = m_parent.m_process->ReadPointerFromMemory( | |||
1405 | pair_ptr + m_parent.m_process->GetAddressByteSize(), err); | |||
1406 | if (!err.Success()) | |||
1407 | return element(); | |||
1408 | ||||
1409 | std::string key_string; | |||
1410 | ||||
1411 | m_parent.m_process->ReadCStringFromMemory(key, key_string, err); | |||
1412 | if (!err.Success()) | |||
1413 | return element(); | |||
1414 | ||||
1415 | return element(ConstString(key_string.c_str()), | |||
1416 | (ObjCLanguageRuntime::ObjCISA)value); | |||
1417 | } | |||
1418 | ||||
1419 | private: | |||
1420 | void AdvanceToValidIndex() { | |||
1421 | if (m_index == -1) | |||
1422 | return; | |||
1423 | ||||
1424 | const lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr; | |||
1425 | const size_t map_pair_size = m_parent.m_map_pair_size; | |||
1426 | const lldb::addr_t invalid_key = m_parent.m_invalid_key; | |||
1427 | Status err; | |||
1428 | ||||
1429 | while (m_index--) { | |||
1430 | lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size); | |||
1431 | lldb::addr_t key = | |||
1432 | m_parent.m_process->ReadPointerFromMemory(pair_ptr, err); | |||
1433 | ||||
1434 | if (!err.Success()) { | |||
1435 | m_index = -1; | |||
1436 | return; | |||
1437 | } | |||
1438 | ||||
1439 | if (key != invalid_key) | |||
1440 | return; | |||
1441 | } | |||
1442 | } | |||
1443 | RemoteNXMapTable &m_parent; | |||
1444 | int m_index; | |||
1445 | }; | |||
1446 | ||||
1447 | const_iterator begin() { | |||
1448 | return const_iterator(*this, m_num_buckets_minus_one + 1); | |||
1449 | } | |||
1450 | ||||
1451 | const_iterator end() { return m_end_iterator; } | |||
1452 | ||||
1453 | uint32_t GetCount() const { return m_count; } | |||
1454 | ||||
1455 | uint32_t GetBucketCount() const { return m_num_buckets_minus_one; } | |||
1456 | ||||
1457 | lldb::addr_t GetBucketDataPointer() const { return m_buckets_ptr; } | |||
1458 | ||||
1459 | lldb::addr_t GetTableLoadAddress() const { return m_load_addr; } | |||
1460 | ||||
1461 | private: | |||
1462 | // contents of _NXMapTable struct | |||
1463 | uint32_t m_count = 0; | |||
1464 | uint32_t m_num_buckets_minus_one = 0; | |||
1465 | lldb::addr_t m_buckets_ptr = LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1466 | lldb_private::Process *m_process = nullptr; | |||
1467 | const_iterator m_end_iterator; | |||
1468 | lldb::addr_t m_load_addr = LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1469 | size_t m_map_pair_size = 0; | |||
1470 | lldb::addr_t m_invalid_key = 0; | |||
1471 | }; | |||
1472 | ||||
1473 | AppleObjCRuntimeV2::HashTableSignature::HashTableSignature() = default; | |||
1474 | ||||
1475 | void AppleObjCRuntimeV2::HashTableSignature::UpdateSignature( | |||
1476 | const RemoteNXMapTable &hash_table) { | |||
1477 | m_count = hash_table.GetCount(); | |||
1478 | m_num_buckets = hash_table.GetBucketCount(); | |||
1479 | m_buckets_ptr = hash_table.GetBucketDataPointer(); | |||
1480 | } | |||
1481 | ||||
1482 | bool AppleObjCRuntimeV2::HashTableSignature::NeedsUpdate( | |||
1483 | Process *process, AppleObjCRuntimeV2 *runtime, | |||
1484 | RemoteNXMapTable &hash_table) { | |||
1485 | if (!hash_table.ParseHeader(process, runtime->GetISAHashTablePointer())) { | |||
1486 | return false; // Failed to parse the header, no need to update anything | |||
1487 | } | |||
1488 | ||||
1489 | // Check with out current signature and return true if the count, number of | |||
1490 | // buckets or the hash table address changes. | |||
1491 | if (m_count == hash_table.GetCount() && | |||
1492 | m_num_buckets == hash_table.GetBucketCount() && | |||
1493 | m_buckets_ptr == hash_table.GetBucketDataPointer()) { | |||
1494 | // Hash table hasn't changed | |||
1495 | return false; | |||
1496 | } | |||
1497 | // Hash table data has changed, we need to update | |||
1498 | return true; | |||
1499 | } | |||
1500 | ||||
1501 | ObjCLanguageRuntime::ClassDescriptorSP | |||
1502 | AppleObjCRuntimeV2::GetClassDescriptorFromISA(ObjCISA isa) { | |||
1503 | ObjCLanguageRuntime::ClassDescriptorSP class_descriptor_sp; | |||
1504 | if (auto *non_pointer_isa_cache = GetNonPointerIsaCache()) | |||
1505 | class_descriptor_sp = non_pointer_isa_cache->GetClassDescriptor(isa); | |||
1506 | if (!class_descriptor_sp) | |||
1507 | class_descriptor_sp = ObjCLanguageRuntime::GetClassDescriptorFromISA(isa); | |||
1508 | return class_descriptor_sp; | |||
1509 | } | |||
1510 | ||||
1511 | ObjCLanguageRuntime::ClassDescriptorSP | |||
1512 | AppleObjCRuntimeV2::GetClassDescriptor(ValueObject &valobj) { | |||
1513 | ClassDescriptorSP objc_class_sp; | |||
1514 | if (valobj.IsBaseClass()) { | |||
1515 | ValueObject *parent = valobj.GetParent(); | |||
1516 | // if I am my own parent, bail out of here fast.. | |||
1517 | if (parent && parent != &valobj) { | |||
1518 | ClassDescriptorSP parent_descriptor_sp = GetClassDescriptor(*parent); | |||
1519 | if (parent_descriptor_sp) | |||
1520 | return parent_descriptor_sp->GetSuperclass(); | |||
1521 | } | |||
1522 | return nullptr; | |||
1523 | } | |||
1524 | // if we get an invalid VO (which might still happen when playing around with | |||
1525 | // pointers returned by the expression parser, don't consider this a valid | |||
1526 | // ObjC object) | |||
1527 | if (!valobj.GetCompilerType().IsValid()) | |||
1528 | return objc_class_sp; | |||
1529 | addr_t isa_pointer = valobj.GetPointerValue(); | |||
1530 | ||||
1531 | // tagged pointer | |||
1532 | if (IsTaggedPointer(isa_pointer)) | |||
1533 | return m_tagged_pointer_vendor_up->GetClassDescriptor(isa_pointer); | |||
1534 | ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); | |||
1535 | ||||
1536 | Process *process = exe_ctx.GetProcessPtr(); | |||
1537 | if (!process) | |||
1538 | return objc_class_sp; | |||
1539 | ||||
1540 | Status error; | |||
1541 | ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error); | |||
1542 | if (isa == LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1543 | return objc_class_sp; | |||
1544 | ||||
1545 | objc_class_sp = GetClassDescriptorFromISA(isa); | |||
1546 | if (!objc_class_sp) { | |||
1547 | if (ABISP abi_sp = process->GetABI()) | |||
1548 | isa = abi_sp->FixCodeAddress(isa); | |||
1549 | objc_class_sp = GetClassDescriptorFromISA(isa); | |||
1550 | } | |||
1551 | ||||
1552 | if (isa && !objc_class_sp) { | |||
1553 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
1554 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("0x%" "l" "x" ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was " "not in class descriptor cache 0x%" "l" "x", isa_pointer, isa ); } while (0) | |||
1555 | "0x%" PRIx64 ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was "do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("0x%" "l" "x" ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was " "not in class descriptor cache 0x%" "l" "x", isa_pointer, isa ); } while (0) | |||
1556 | "not in class descriptor cache 0x%" PRIx64,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("0x%" "l" "x" ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was " "not in class descriptor cache 0x%" "l" "x", isa_pointer, isa ); } while (0) | |||
1557 | isa_pointer, isa)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("0x%" "l" "x" ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was " "not in class descriptor cache 0x%" "l" "x", isa_pointer, isa ); } while (0); | |||
1558 | } | |||
1559 | return objc_class_sp; | |||
1560 | } | |||
1561 | ||||
1562 | lldb::addr_t AppleObjCRuntimeV2::GetTaggedPointerObfuscator() { | |||
1563 | if (m_tagged_pointer_obfuscator != LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1564 | return m_tagged_pointer_obfuscator; | |||
1565 | ||||
1566 | Process *process = GetProcess(); | |||
1567 | ModuleSP objc_module_sp(GetObjCModule()); | |||
1568 | ||||
1569 | if (!objc_module_sp) | |||
1570 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1571 | ||||
1572 | static ConstString g_gdb_objc_obfuscator( | |||
1573 | "objc_debug_taggedpointer_obfuscator"); | |||
1574 | ||||
1575 | const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType( | |||
1576 | g_gdb_objc_obfuscator, lldb::eSymbolTypeAny); | |||
1577 | if (symbol) { | |||
1578 | lldb::addr_t g_gdb_obj_obfuscator_ptr = | |||
1579 | symbol->GetLoadAddress(&process->GetTarget()); | |||
1580 | ||||
1581 | if (g_gdb_obj_obfuscator_ptr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1582 | Status error; | |||
1583 | m_tagged_pointer_obfuscator = | |||
1584 | process->ReadPointerFromMemory(g_gdb_obj_obfuscator_ptr, error); | |||
1585 | } | |||
1586 | } | |||
1587 | // If we don't have a correct value at this point, there must be no | |||
1588 | // obfuscation. | |||
1589 | if (m_tagged_pointer_obfuscator == LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1590 | m_tagged_pointer_obfuscator = 0; | |||
1591 | ||||
1592 | return m_tagged_pointer_obfuscator; | |||
1593 | } | |||
1594 | ||||
1595 | lldb::addr_t AppleObjCRuntimeV2::GetISAHashTablePointer() { | |||
1596 | if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1597 | Process *process = GetProcess(); | |||
1598 | ||||
1599 | ModuleSP objc_module_sp(GetObjCModule()); | |||
1600 | ||||
1601 | if (!objc_module_sp) | |||
1602 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1603 | ||||
1604 | static ConstString g_gdb_objc_realized_classes("gdb_objc_realized_classes"); | |||
1605 | ||||
1606 | const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType( | |||
1607 | g_gdb_objc_realized_classes, lldb::eSymbolTypeAny); | |||
1608 | if (symbol) { | |||
1609 | lldb::addr_t gdb_objc_realized_classes_ptr = | |||
1610 | symbol->GetLoadAddress(&process->GetTarget()); | |||
1611 | ||||
1612 | if (gdb_objc_realized_classes_ptr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1613 | Status error; | |||
1614 | m_isa_hash_table_ptr = process->ReadPointerFromMemory( | |||
1615 | gdb_objc_realized_classes_ptr, error); | |||
1616 | } | |||
1617 | } | |||
1618 | } | |||
1619 | return m_isa_hash_table_ptr; | |||
1620 | } | |||
1621 | ||||
1622 | std::unique_ptr<UtilityFunction> | |||
1623 | AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunctionImpl( | |||
1624 | ExecutionContext &exe_ctx, Helper helper, std::string code, | |||
1625 | std::string name) { | |||
1626 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
1627 | ||||
1628 | LLDB_LOG(log, "Creating utility function {0}", name)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Creating utility function {0}", name); } while ( 0); | |||
1629 | ||||
1630 | TypeSystemClang *ast = | |||
1631 | ScratchTypeSystemClang::GetForTarget(exe_ctx.GetTargetRef()); | |||
1632 | if (!ast) | |||
1633 | return {}; | |||
1634 | ||||
1635 | auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction( | |||
1636 | std::move(code), std::move(name), eLanguageTypeC, exe_ctx); | |||
1637 | if (!utility_fn_or_error) { | |||
1638 | LLDB_LOG_ERROR(do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (utility_fn_or_error.takeError()); if (log_private && error_private) { log_private->FormatError(::std::move(error_private ), "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to get utility function for dynamic info extractor: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
1639 | log, utility_fn_or_error.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (utility_fn_or_error.takeError()); if (log_private && error_private) { log_private->FormatError(::std::move(error_private ), "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to get utility function for dynamic info extractor: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
1640 | "Failed to get utility function for dynamic info extractor: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (utility_fn_or_error.takeError()); if (log_private && error_private) { log_private->FormatError(::std::move(error_private ), "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to get utility function for dynamic info extractor: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0); | |||
1641 | return {}; | |||
1642 | } | |||
1643 | ||||
1644 | // Make some types for our arguments. | |||
1645 | CompilerType clang_uint32_t_type = | |||
1646 | ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); | |||
1647 | CompilerType clang_void_pointer_type = | |||
1648 | ast->GetBasicType(eBasicTypeVoid).GetPointerType(); | |||
1649 | ||||
1650 | // Make the runner function for our implementation utility function. | |||
1651 | ValueList arguments; | |||
1652 | Value value; | |||
1653 | value.SetValueType(Value::ValueType::Scalar); | |||
1654 | value.SetCompilerType(clang_void_pointer_type); | |||
1655 | arguments.PushValue(value); | |||
1656 | arguments.PushValue(value); | |||
1657 | value.SetValueType(Value::ValueType::Scalar); | |||
1658 | value.SetCompilerType(clang_uint32_t_type); | |||
1659 | arguments.PushValue(value); | |||
1660 | ||||
1661 | // objc_getRealizedClassList_trylock takes an additional buffer and length. | |||
1662 | if (helper == Helper::objc_getRealizedClassList_trylock) { | |||
1663 | value.SetCompilerType(clang_void_pointer_type); | |||
1664 | arguments.PushValue(value); | |||
1665 | value.SetCompilerType(clang_uint32_t_type); | |||
1666 | arguments.PushValue(value); | |||
1667 | } | |||
1668 | ||||
1669 | arguments.PushValue(value); | |||
1670 | ||||
1671 | std::unique_ptr<UtilityFunction> utility_fn = std::move(*utility_fn_or_error); | |||
1672 | ||||
1673 | Status error; | |||
1674 | utility_fn->MakeFunctionCaller(clang_uint32_t_type, arguments, | |||
1675 | exe_ctx.GetThreadSP(), error); | |||
1676 | ||||
1677 | if (error.Fail()) { | |||
1678 | LLDB_LOG(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to make function caller for implementation lookup: {0}." , error.AsCString()); } while (0) | |||
1679 | "Failed to make function caller for implementation lookup: {0}.",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to make function caller for implementation lookup: {0}." , error.AsCString()); } while (0) | |||
1680 | error.AsCString())do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to make function caller for implementation lookup: {0}." , error.AsCString()); } while (0); | |||
1681 | return {}; | |||
1682 | } | |||
1683 | ||||
1684 | return utility_fn; | |||
1685 | } | |||
1686 | ||||
1687 | UtilityFunction * | |||
1688 | AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunction( | |||
1689 | ExecutionContext &exe_ctx, Helper helper) { | |||
1690 | switch (helper) { | |||
1691 | case gdb_objc_realized_classes: { | |||
1692 | if (!m_gdb_objc_realized_classes_helper.utility_function) | |||
1693 | m_gdb_objc_realized_classes_helper.utility_function = | |||
1694 | GetClassInfoUtilityFunctionImpl(exe_ctx, helper, | |||
1695 | g_get_dynamic_class_info_body, | |||
1696 | g_get_dynamic_class_info_name); | |||
1697 | return m_gdb_objc_realized_classes_helper.utility_function.get(); | |||
1698 | } | |||
1699 | case objc_copyRealizedClassList: { | |||
1700 | if (!m_objc_copyRealizedClassList_helper.utility_function) | |||
1701 | m_objc_copyRealizedClassList_helper.utility_function = | |||
1702 | GetClassInfoUtilityFunctionImpl(exe_ctx, helper, | |||
1703 | g_get_dynamic_class_info2_body, | |||
1704 | g_get_dynamic_class_info2_name); | |||
1705 | return m_objc_copyRealizedClassList_helper.utility_function.get(); | |||
1706 | } | |||
1707 | case objc_getRealizedClassList_trylock: { | |||
1708 | if (!m_objc_getRealizedClassList_trylock_helper.utility_function) | |||
1709 | m_objc_getRealizedClassList_trylock_helper.utility_function = | |||
1710 | GetClassInfoUtilityFunctionImpl(exe_ctx, helper, | |||
1711 | g_get_dynamic_class_info3_body, | |||
1712 | g_get_dynamic_class_info3_name); | |||
1713 | return m_objc_getRealizedClassList_trylock_helper.utility_function.get(); | |||
1714 | } | |||
1715 | } | |||
1716 | llvm_unreachable("Unexpected helper")::llvm::llvm_unreachable_internal("Unexpected helper", "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 1716); | |||
1717 | } | |||
1718 | ||||
1719 | lldb::addr_t & | |||
1720 | AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoArgs(Helper helper) { | |||
1721 | switch (helper) { | |||
1722 | case gdb_objc_realized_classes: | |||
1723 | return m_gdb_objc_realized_classes_helper.args; | |||
1724 | case objc_copyRealizedClassList: | |||
1725 | return m_objc_copyRealizedClassList_helper.args; | |||
1726 | case objc_getRealizedClassList_trylock: | |||
1727 | return m_objc_getRealizedClassList_trylock_helper.args; | |||
1728 | } | |||
1729 | llvm_unreachable("Unexpected helper")::llvm::llvm_unreachable_internal("Unexpected helper", "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 1729); | |||
1730 | } | |||
1731 | ||||
1732 | AppleObjCRuntimeV2::DynamicClassInfoExtractor::Helper | |||
1733 | AppleObjCRuntimeV2::DynamicClassInfoExtractor::ComputeHelper( | |||
1734 | ExecutionContext &exe_ctx) const { | |||
1735 | if (!m_runtime.m_has_objc_copyRealizedClassList && | |||
1736 | !m_runtime.m_has_objc_getRealizedClassList_trylock) | |||
1737 | return DynamicClassInfoExtractor::gdb_objc_realized_classes; | |||
1738 | ||||
1739 | if (Process *process = m_runtime.GetProcess()) { | |||
1740 | if (DynamicLoader *loader = process->GetDynamicLoader()) { | |||
1741 | if (loader->IsFullyInitialized()) { | |||
1742 | switch (exe_ctx.GetTargetRef().GetDynamicClassInfoHelper()) { | |||
1743 | case eDynamicClassInfoHelperAuto: | |||
1744 | [[clang::fallthrough]]; | |||
1745 | case eDynamicClassInfoHelperGetRealizedClassList: | |||
1746 | if (m_runtime.m_has_objc_getRealizedClassList_trylock) | |||
1747 | return DynamicClassInfoExtractor::objc_getRealizedClassList_trylock; | |||
1748 | [[clang::fallthrough]]; | |||
1749 | case eDynamicClassInfoHelperCopyRealizedClassList: | |||
1750 | if (m_runtime.m_has_objc_copyRealizedClassList) | |||
1751 | return DynamicClassInfoExtractor::objc_copyRealizedClassList; | |||
1752 | [[clang::fallthrough]]; | |||
1753 | case eDynamicClassInfoHelperRealizedClassesStruct: | |||
1754 | return DynamicClassInfoExtractor::gdb_objc_realized_classes; | |||
1755 | } | |||
1756 | } | |||
1757 | } | |||
1758 | } | |||
1759 | ||||
1760 | return DynamicClassInfoExtractor::gdb_objc_realized_classes; | |||
1761 | } | |||
1762 | ||||
1763 | std::unique_ptr<UtilityFunction> | |||
1764 | AppleObjCRuntimeV2::SharedCacheClassInfoExtractor:: | |||
1765 | GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx) { | |||
1766 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
1767 | ||||
1768 | LLDB_LOG(log, "Creating utility function {0}",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Creating utility function {0}", g_get_shared_cache_class_info_name ); } while (0) | |||
1769 | g_get_shared_cache_class_info_name)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Creating utility function {0}", g_get_shared_cache_class_info_name ); } while (0); | |||
1770 | ||||
1771 | TypeSystemClang *ast = | |||
1772 | ScratchTypeSystemClang::GetForTarget(exe_ctx.GetTargetRef()); | |||
1773 | if (!ast) | |||
1774 | return {}; | |||
1775 | ||||
1776 | // If the inferior objc.dylib has the class_getNameRaw function, use that in | |||
1777 | // our jitted expression. Else fall back to the old class_getName. | |||
1778 | static ConstString g_class_getName_symbol_name("class_getName"); | |||
1779 | static ConstString g_class_getNameRaw_symbol_name( | |||
1780 | "objc_debug_class_getNameRaw"); | |||
1781 | ||||
1782 | ConstString class_name_getter_function_name = | |||
1783 | m_runtime.HasSymbol(g_class_getNameRaw_symbol_name) | |||
1784 | ? g_class_getNameRaw_symbol_name | |||
1785 | : g_class_getName_symbol_name; | |||
1786 | ||||
1787 | // Substitute in the correct class_getName / class_getNameRaw function name, | |||
1788 | // concatenate the two parts of our expression text. The format string has | |||
1789 | // two %s's, so provide the name twice. | |||
1790 | std::string shared_class_expression; | |||
1791 | llvm::raw_string_ostream(shared_class_expression) | |||
1792 | << llvm::format(g_shared_cache_class_name_funcptr, | |||
1793 | class_name_getter_function_name.AsCString(), | |||
1794 | class_name_getter_function_name.AsCString()); | |||
1795 | ||||
1796 | shared_class_expression += g_get_shared_cache_class_info_body; | |||
1797 | ||||
1798 | auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction( | |||
1799 | std::move(shared_class_expression), g_get_shared_cache_class_info_name, | |||
1800 | eLanguageTypeC, exe_ctx); | |||
1801 | ||||
1802 | if (!utility_fn_or_error) { | |||
1803 | LLDB_LOG_ERROR(do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (utility_fn_or_error.takeError()); if (log_private && error_private) { log_private->FormatError(::std::move(error_private ), "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to get utility function for shared class info extractor: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
1804 | log, utility_fn_or_error.takeError(),do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (utility_fn_or_error.takeError()); if (log_private && error_private) { log_private->FormatError(::std::move(error_private ), "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to get utility function for shared class info extractor: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0) | |||
1805 | "Failed to get utility function for shared class info extractor: {0}")do { ::lldb_private::Log *log_private = (log); ::llvm::Error error_private = (utility_fn_or_error.takeError()); if (log_private && error_private) { log_private->FormatError(::std::move(error_private ), "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to get utility function for shared class info extractor: {0}" ); } else ::llvm::consumeError(::std::move(error_private)); } while (0); | |||
1806 | return nullptr; | |||
1807 | } | |||
1808 | ||||
1809 | // Make some types for our arguments. | |||
1810 | CompilerType clang_uint32_t_type = | |||
1811 | ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); | |||
1812 | CompilerType clang_void_pointer_type = | |||
1813 | ast->GetBasicType(eBasicTypeVoid).GetPointerType(); | |||
1814 | CompilerType clang_uint64_t_pointer_type = | |||
1815 | ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 64) | |||
1816 | .GetPointerType(); | |||
1817 | ||||
1818 | // Next make the function caller for our implementation utility function. | |||
1819 | ValueList arguments; | |||
1820 | Value value; | |||
1821 | value.SetValueType(Value::ValueType::Scalar); | |||
1822 | value.SetCompilerType(clang_void_pointer_type); | |||
1823 | arguments.PushValue(value); | |||
1824 | arguments.PushValue(value); | |||
1825 | arguments.PushValue(value); | |||
1826 | ||||
1827 | value.SetValueType(Value::ValueType::Scalar); | |||
1828 | value.SetCompilerType(clang_uint64_t_pointer_type); | |||
1829 | arguments.PushValue(value); | |||
1830 | ||||
1831 | value.SetValueType(Value::ValueType::Scalar); | |||
1832 | value.SetCompilerType(clang_uint32_t_type); | |||
1833 | arguments.PushValue(value); | |||
1834 | arguments.PushValue(value); | |||
1835 | ||||
1836 | std::unique_ptr<UtilityFunction> utility_fn = std::move(*utility_fn_or_error); | |||
1837 | ||||
1838 | Status error; | |||
1839 | utility_fn->MakeFunctionCaller(clang_uint32_t_type, arguments, | |||
1840 | exe_ctx.GetThreadSP(), error); | |||
1841 | ||||
1842 | if (error.Fail()) { | |||
1843 | LLDB_LOG(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to make function caller for implementation lookup: {0}." , error.AsCString()); } while (0) | |||
1844 | "Failed to make function caller for implementation lookup: {0}.",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to make function caller for implementation lookup: {0}." , error.AsCString()); } while (0) | |||
1845 | error.AsCString())do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Failed to make function caller for implementation lookup: {0}." , error.AsCString()); } while (0); | |||
1846 | return {}; | |||
1847 | } | |||
1848 | ||||
1849 | return utility_fn; | |||
1850 | } | |||
1851 | ||||
1852 | UtilityFunction * | |||
1853 | AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::GetClassInfoUtilityFunction( | |||
1854 | ExecutionContext &exe_ctx) { | |||
1855 | if (!m_utility_function) | |||
1856 | m_utility_function = GetClassInfoUtilityFunctionImpl(exe_ctx); | |||
1857 | return m_utility_function.get(); | |||
1858 | } | |||
1859 | ||||
1860 | AppleObjCRuntimeV2::DescriptorMapUpdateResult | |||
1861 | AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap( | |||
1862 | RemoteNXMapTable &hash_table) { | |||
1863 | Process *process = m_runtime.GetProcess(); | |||
1864 | if (process == nullptr) | |||
1865 | return DescriptorMapUpdateResult::Fail(); | |||
1866 | ||||
1867 | uint32_t num_class_infos = 0; | |||
1868 | ||||
1869 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
1870 | ||||
1871 | ExecutionContext exe_ctx; | |||
1872 | ||||
1873 | ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread(); | |||
1874 | ||||
1875 | if (!thread_sp) | |||
1876 | return DescriptorMapUpdateResult::Fail(); | |||
1877 | ||||
1878 | thread_sp->CalculateExecutionContext(exe_ctx); | |||
1879 | TypeSystemClang *ast = | |||
1880 | ScratchTypeSystemClang::GetForTarget(process->GetTarget()); | |||
1881 | ||||
1882 | if (!ast) | |||
1883 | return DescriptorMapUpdateResult::Fail(); | |||
1884 | ||||
1885 | Address function_address; | |||
1886 | ||||
1887 | const uint32_t addr_size = process->GetAddressByteSize(); | |||
1888 | ||||
1889 | Status err; | |||
1890 | ||||
1891 | // Compute which helper we're going to use for this update. | |||
1892 | const DynamicClassInfoExtractor::Helper helper = ComputeHelper(exe_ctx); | |||
1893 | ||||
1894 | // Read the total number of classes from the hash table | |||
1895 | const uint32_t num_classes = | |||
1896 | helper == DynamicClassInfoExtractor::gdb_objc_realized_classes | |||
1897 | ? hash_table.GetCount() | |||
1898 | : m_runtime.m_realized_class_generation_count; | |||
1899 | if (num_classes == 0) { | |||
1900 | LLDB_LOGF(log, "No dynamic classes found.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("No dynamic classes found."); } while (0); | |||
1901 | return DescriptorMapUpdateResult::Success(0); | |||
1902 | } | |||
1903 | ||||
1904 | UtilityFunction *get_class_info_code = | |||
1905 | GetClassInfoUtilityFunction(exe_ctx, helper); | |||
1906 | if (!get_class_info_code) { | |||
1907 | // The callee will have already logged a useful error message. | |||
1908 | return DescriptorMapUpdateResult::Fail(); | |||
1909 | } | |||
1910 | ||||
1911 | FunctionCaller *get_class_info_function = | |||
1912 | get_class_info_code->GetFunctionCaller(); | |||
1913 | ||||
1914 | if (!get_class_info_function) { | |||
1915 | LLDB_LOGF(log, "Failed to get implementation lookup function caller.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("Failed to get implementation lookup function caller." ); } while (0); | |||
1916 | return DescriptorMapUpdateResult::Fail(); | |||
1917 | } | |||
1918 | ||||
1919 | ValueList arguments = get_class_info_function->GetArgumentValues(); | |||
1920 | ||||
1921 | DiagnosticManager diagnostics; | |||
1922 | ||||
1923 | const uint32_t class_info_byte_size = addr_size + 4; | |||
1924 | const uint32_t class_infos_byte_size = num_classes * class_info_byte_size; | |||
1925 | lldb::addr_t class_infos_addr = process->AllocateMemory( | |||
1926 | class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err); | |||
1927 | ||||
1928 | if (class_infos_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1929 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0) | |||
1930 | "unable to allocate %" PRIu32do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0) | |||
1931 | " bytes in process for shared cache read",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0) | |||
1932 | class_infos_byte_size)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0); | |||
1933 | return DescriptorMapUpdateResult::Fail(); | |||
1934 | } | |||
1935 | ||||
1936 | auto deallocate_class_infos = llvm::make_scope_exit([&] { | |||
1937 | // Deallocate the memory we allocated for the ClassInfo array | |||
1938 | if (class_infos_addr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1939 | process->DeallocateMemory(class_infos_addr); | |||
1940 | }); | |||
1941 | ||||
1942 | lldb::addr_t class_buffer_addr = LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
1943 | const uint32_t class_byte_size = addr_size; | |||
1944 | const uint32_t class_buffer_len = num_classes; | |||
1945 | const uint32_t class_buffer_byte_size = class_buffer_len * class_byte_size; | |||
1946 | if (helper == Helper::objc_getRealizedClassList_trylock) { | |||
1947 | class_buffer_addr = process->AllocateMemory( | |||
1948 | class_buffer_byte_size, ePermissionsReadable | ePermissionsWritable, | |||
1949 | err); | |||
1950 | if (class_buffer_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1951 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_buffer_byte_size); } while (0) | |||
1952 | "unable to allocate %" PRIu32do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_buffer_byte_size); } while (0) | |||
1953 | " bytes in process for shared cache read",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_buffer_byte_size); } while (0) | |||
1954 | class_buffer_byte_size)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_buffer_byte_size); } while (0); | |||
1955 | return DescriptorMapUpdateResult::Fail(); | |||
1956 | } | |||
1957 | } | |||
1958 | ||||
1959 | auto deallocate_class_buffer = llvm::make_scope_exit([&] { | |||
1960 | // Deallocate the memory we allocated for the Class array | |||
1961 | if (class_buffer_addr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
1962 | process->DeallocateMemory(class_buffer_addr); | |||
1963 | }); | |||
1964 | ||||
1965 | std::lock_guard<std::mutex> guard(m_mutex); | |||
1966 | ||||
1967 | // Fill in our function argument values | |||
1968 | uint32_t index = 0; | |||
1969 | arguments.GetValueAtIndex(index++)->GetScalar() = | |||
1970 | hash_table.GetTableLoadAddress(); | |||
1971 | arguments.GetValueAtIndex(index++)->GetScalar() = class_infos_addr; | |||
1972 | arguments.GetValueAtIndex(index++)->GetScalar() = class_infos_byte_size; | |||
1973 | ||||
1974 | if (class_buffer_addr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1975 | arguments.GetValueAtIndex(index++)->GetScalar() = class_buffer_addr; | |||
1976 | arguments.GetValueAtIndex(index++)->GetScalar() = class_buffer_byte_size; | |||
1977 | } | |||
1978 | ||||
1979 | // Only dump the runtime classes from the expression evaluation if the log is | |||
1980 | // verbose: | |||
1981 | Log *type_log = GetLog(LLDBLog::Types); | |||
1982 | bool dump_log = type_log && type_log->GetVerbose(); | |||
1983 | ||||
1984 | arguments.GetValueAtIndex(index++)->GetScalar() = dump_log ? 1 : 0; | |||
1985 | ||||
1986 | bool success = false; | |||
1987 | ||||
1988 | diagnostics.Clear(); | |||
1989 | ||||
1990 | // Write our function arguments into the process so we can run our function | |||
1991 | if (get_class_info_function->WriteFunctionArguments( | |||
1992 | exe_ctx, GetClassInfoArgs(helper), arguments, diagnostics)) { | |||
1993 | EvaluateExpressionOptions options; | |||
1994 | options.SetUnwindOnError(true); | |||
1995 | options.SetTryAllThreads(false); | |||
1996 | options.SetStopOthers(true); | |||
1997 | options.SetIgnoreBreakpoints(true); | |||
1998 | options.SetTimeout(process->GetUtilityExpressionTimeout()); | |||
1999 | options.SetIsForUtilityExpr(true); | |||
2000 | ||||
2001 | CompilerType clang_uint32_t_type = | |||
2002 | ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); | |||
2003 | ||||
2004 | Value return_value; | |||
2005 | return_value.SetValueType(Value::ValueType::Scalar); | |||
2006 | return_value.SetCompilerType(clang_uint32_t_type); | |||
2007 | return_value.GetScalar() = 0; | |||
2008 | ||||
2009 | diagnostics.Clear(); | |||
2010 | ||||
2011 | // Run the function | |||
2012 | ExpressionResults results = get_class_info_function->ExecuteFunction( | |||
2013 | exe_ctx, &GetClassInfoArgs(helper), options, diagnostics, return_value); | |||
2014 | ||||
2015 | if (results == eExpressionCompleted) { | |||
2016 | // The result is the number of ClassInfo structures that were filled in | |||
2017 | num_class_infos = return_value.GetScalar().ULong(); | |||
2018 | LLDB_LOG(log, "Discovered {0} Objective-C classes", num_class_infos)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Discovered {0} Objective-C classes", num_class_infos ); } while (0); | |||
2019 | if (num_class_infos > 0) { | |||
2020 | // Read the ClassInfo structures | |||
2021 | DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0); | |||
2022 | if (process->ReadMemory(class_infos_addr, buffer.GetBytes(), | |||
2023 | buffer.GetByteSize(), | |||
2024 | err) == buffer.GetByteSize()) { | |||
2025 | DataExtractor class_infos_data(buffer.GetBytes(), | |||
2026 | buffer.GetByteSize(), | |||
2027 | process->GetByteOrder(), addr_size); | |||
2028 | m_runtime.ParseClassInfoArray(class_infos_data, num_class_infos); | |||
2029 | } | |||
2030 | } | |||
2031 | success = true; | |||
2032 | } else { | |||
2033 | if (log) { | |||
2034 | LLDB_LOGF(log, "Error evaluating our find class name function.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("Error evaluating our find class name function." ); } while (0); | |||
2035 | diagnostics.Dump(log); | |||
2036 | } | |||
2037 | } | |||
2038 | } else { | |||
2039 | if (log) { | |||
2040 | LLDB_LOGF(log, "Error writing function arguments.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("Error writing function arguments.") ; } while (0); | |||
2041 | diagnostics.Dump(log); | |||
2042 | } | |||
2043 | } | |||
2044 | ||||
2045 | return DescriptorMapUpdateResult(success, num_class_infos); | |||
2046 | } | |||
2047 | ||||
2048 | uint32_t AppleObjCRuntimeV2::ParseClassInfoArray(const DataExtractor &data, | |||
2049 | uint32_t num_class_infos) { | |||
2050 | // Parses an array of "num_class_infos" packed ClassInfo structures: | |||
2051 | // | |||
2052 | // struct ClassInfo | |||
2053 | // { | |||
2054 | // Class isa; | |||
2055 | // uint32_t hash; | |||
2056 | // } __attribute__((__packed__)); | |||
2057 | ||||
2058 | Log *log = GetLog(LLDBLog::Types); | |||
2059 | bool should_log = log && log->GetVerbose(); | |||
2060 | ||||
2061 | uint32_t num_parsed = 0; | |||
2062 | ||||
2063 | // Iterate through all ClassInfo structures | |||
2064 | lldb::offset_t offset = 0; | |||
2065 | for (uint32_t i = 0; i < num_class_infos; ++i) { | |||
2066 | ObjCISA isa = data.GetAddress(&offset); | |||
2067 | ||||
2068 | if (isa == 0) { | |||
2069 | if (should_log) | |||
2070 | LLDB_LOGF(do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 found NULL isa, ignoring this class info" ); } while (0) | |||
2071 | log, "AppleObjCRuntimeV2 found NULL isa, ignoring this class info")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 found NULL isa, ignoring this class info" ); } while (0); | |||
2072 | continue; | |||
2073 | } | |||
2074 | // Check if we already know about this ISA, if we do, the info will never | |||
2075 | // change, so we can just skip it. | |||
2076 | if (ISAIsCached(isa)) { | |||
2077 | if (should_log) | |||
2078 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 found cached isa=0x%" "l" "x" ", ignoring this class info", isa); } while (0) | |||
2079 | "AppleObjCRuntimeV2 found cached isa=0x%" PRIx64do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 found cached isa=0x%" "l" "x" ", ignoring this class info", isa); } while (0) | |||
2080 | ", ignoring this class info",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 found cached isa=0x%" "l" "x" ", ignoring this class info", isa); } while (0) | |||
2081 | isa)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 found cached isa=0x%" "l" "x" ", ignoring this class info", isa); } while (0); | |||
2082 | offset += 4; | |||
2083 | } else { | |||
2084 | // Read the 32 bit hash for the class name | |||
2085 | const uint32_t name_hash = data.GetU32(&offset); | |||
2086 | ClassDescriptorSP descriptor_sp( | |||
2087 | new ClassDescriptorV2(*this, isa, nullptr)); | |||
2088 | ||||
2089 | // The code in g_get_shared_cache_class_info_body sets the value of the | |||
2090 | // hash to 0 to signal a demangled symbol. We use class_getName() in that | |||
2091 | // code to find the class name, but this returns a demangled name for | |||
2092 | // Swift symbols. For those symbols, recompute the hash here by reading | |||
2093 | // their name from the runtime. | |||
2094 | if (name_hash) | |||
2095 | AddClass(isa, descriptor_sp, name_hash); | |||
2096 | else | |||
2097 | AddClass(isa, descriptor_sp, | |||
2098 | descriptor_sp->GetClassName().AsCString(nullptr)); | |||
2099 | num_parsed++; | |||
2100 | if (should_log) | |||
2101 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 added isa=0x%" "l" "x" ", hash=0x%8.8x, name=%s", isa, name_hash, descriptor_sp ->GetClassName().AsCString("<unknown>")); } while (0 ) | |||
2102 | "AppleObjCRuntimeV2 added isa=0x%" PRIx64do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 added isa=0x%" "l" "x" ", hash=0x%8.8x, name=%s", isa, name_hash, descriptor_sp ->GetClassName().AsCString("<unknown>")); } while (0 ) | |||
2103 | ", hash=0x%8.8x, name=%s",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 added isa=0x%" "l" "x" ", hash=0x%8.8x, name=%s", isa, name_hash, descriptor_sp ->GetClassName().AsCString("<unknown>")); } while (0 ) | |||
2104 | isa, name_hash,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 added isa=0x%" "l" "x" ", hash=0x%8.8x, name=%s", isa, name_hash, descriptor_sp ->GetClassName().AsCString("<unknown>")); } while (0 ) | |||
2105 | descriptor_sp->GetClassName().AsCString("<unknown>"))do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 added isa=0x%" "l" "x" ", hash=0x%8.8x, name=%s", isa, name_hash, descriptor_sp ->GetClassName().AsCString("<unknown>")); } while (0 ); | |||
2106 | } | |||
2107 | } | |||
2108 | if (should_log) | |||
2109 | LLDB_LOGF(log, "AppleObjCRuntimeV2 parsed %" PRIu32 " class infos",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 parsed %" "u" " class infos" , num_parsed); } while (0) | |||
2110 | num_parsed)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AppleObjCRuntimeV2 parsed %" "u" " class infos" , num_parsed); } while (0); | |||
2111 | return num_parsed; | |||
2112 | } | |||
2113 | ||||
2114 | bool AppleObjCRuntimeV2::HasSymbol(ConstString Name) { | |||
2115 | if (!m_objc_module_sp) | |||
2116 | return false; | |||
2117 | if (const Symbol *symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType( | |||
2118 | Name, lldb::eSymbolTypeCode)) { | |||
2119 | if (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid()) | |||
2120 | return true; | |||
2121 | } | |||
2122 | return false; | |||
2123 | } | |||
2124 | ||||
2125 | AppleObjCRuntimeV2::DescriptorMapUpdateResult | |||
2126 | AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() { | |||
2127 | Process *process = m_runtime.GetProcess(); | |||
2128 | if (process == nullptr) | |||
2129 | return DescriptorMapUpdateResult::Fail(); | |||
2130 | ||||
2131 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
2132 | ||||
2133 | ExecutionContext exe_ctx; | |||
2134 | ||||
2135 | ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread(); | |||
2136 | ||||
2137 | if (!thread_sp) | |||
2138 | return DescriptorMapUpdateResult::Fail(); | |||
2139 | ||||
2140 | thread_sp->CalculateExecutionContext(exe_ctx); | |||
2141 | TypeSystemClang *ast = | |||
2142 | ScratchTypeSystemClang::GetForTarget(process->GetTarget()); | |||
2143 | ||||
2144 | if (!ast) | |||
2145 | return DescriptorMapUpdateResult::Fail(); | |||
2146 | ||||
2147 | Address function_address; | |||
2148 | ||||
2149 | const uint32_t addr_size = process->GetAddressByteSize(); | |||
2150 | ||||
2151 | Status err; | |||
2152 | ||||
2153 | uint32_t num_class_infos = 0; | |||
2154 | ||||
2155 | const lldb::addr_t objc_opt_ptr = m_runtime.GetSharedCacheReadOnlyAddress(); | |||
2156 | const lldb::addr_t shared_cache_base_addr = | |||
2157 | m_runtime.GetSharedCacheBaseAddress(); | |||
2158 | ||||
2159 | if (objc_opt_ptr == LLDB_INVALID_ADDRESS(18446744073709551615UL) || | |||
2160 | shared_cache_base_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
2161 | return DescriptorMapUpdateResult::Fail(); | |||
2162 | ||||
2163 | // The number of entries to pre-allocate room for. | |||
2164 | // Each entry is (addrsize + 4) bytes | |||
2165 | const uint32_t max_num_classes = 163840; | |||
2166 | ||||
2167 | UtilityFunction *get_class_info_code = GetClassInfoUtilityFunction(exe_ctx); | |||
2168 | if (!get_class_info_code) { | |||
2169 | // The callee will have already logged a useful error message. | |||
2170 | return DescriptorMapUpdateResult::Fail(); | |||
2171 | } | |||
2172 | ||||
2173 | FunctionCaller *get_shared_cache_class_info_function = | |||
2174 | get_class_info_code->GetFunctionCaller(); | |||
2175 | ||||
2176 | if (!get_shared_cache_class_info_function) { | |||
2177 | LLDB_LOGF(log, "Failed to get implementation lookup function caller.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("Failed to get implementation lookup function caller." ); } while (0); | |||
2178 | return DescriptorMapUpdateResult::Fail(); | |||
2179 | } | |||
2180 | ||||
2181 | ValueList arguments = | |||
2182 | get_shared_cache_class_info_function->GetArgumentValues(); | |||
2183 | ||||
2184 | DiagnosticManager diagnostics; | |||
2185 | ||||
2186 | const uint32_t class_info_byte_size = addr_size + 4; | |||
2187 | const uint32_t class_infos_byte_size = max_num_classes * class_info_byte_size; | |||
2188 | lldb::addr_t class_infos_addr = process->AllocateMemory( | |||
2189 | class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err); | |||
2190 | const uint32_t relative_selector_offset_addr_size = 64; | |||
2191 | lldb::addr_t relative_selector_offset_addr = | |||
2192 | process->AllocateMemory(relative_selector_offset_addr_size, | |||
2193 | ePermissionsReadable | ePermissionsWritable, err); | |||
2194 | ||||
2195 | if (class_infos_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
2196 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0) | |||
2197 | "unable to allocate %" PRIu32do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0) | |||
2198 | " bytes in process for shared cache read",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0) | |||
2199 | class_infos_byte_size)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("unable to allocate %" "u" " bytes in process for shared cache read" , class_infos_byte_size); } while (0); | |||
2200 | return DescriptorMapUpdateResult::Fail(); | |||
2201 | } | |||
2202 | ||||
2203 | std::lock_guard<std::mutex> guard(m_mutex); | |||
2204 | ||||
2205 | // Fill in our function argument values | |||
2206 | arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr; | |||
2207 | arguments.GetValueAtIndex(1)->GetScalar() = shared_cache_base_addr; | |||
2208 | arguments.GetValueAtIndex(2)->GetScalar() = class_infos_addr; | |||
2209 | arguments.GetValueAtIndex(3)->GetScalar() = relative_selector_offset_addr; | |||
2210 | arguments.GetValueAtIndex(4)->GetScalar() = class_infos_byte_size; | |||
2211 | // Only dump the runtime classes from the expression evaluation if the log is | |||
2212 | // verbose: | |||
2213 | Log *type_log = GetLog(LLDBLog::Types); | |||
2214 | bool dump_log = type_log && type_log->GetVerbose(); | |||
2215 | ||||
2216 | arguments.GetValueAtIndex(5)->GetScalar() = dump_log ? 1 : 0; | |||
2217 | ||||
2218 | bool success = false; | |||
2219 | ||||
2220 | diagnostics.Clear(); | |||
2221 | ||||
2222 | // Write our function arguments into the process so we can run our function | |||
2223 | if (get_shared_cache_class_info_function->WriteFunctionArguments( | |||
2224 | exe_ctx, m_args, arguments, diagnostics)) { | |||
2225 | EvaluateExpressionOptions options; | |||
2226 | options.SetUnwindOnError(true); | |||
2227 | options.SetTryAllThreads(false); | |||
2228 | options.SetStopOthers(true); | |||
2229 | options.SetIgnoreBreakpoints(true); | |||
2230 | options.SetTimeout(process->GetUtilityExpressionTimeout()); | |||
2231 | options.SetIsForUtilityExpr(true); | |||
2232 | ||||
2233 | CompilerType clang_uint32_t_type = | |||
2234 | ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); | |||
2235 | ||||
2236 | Value return_value; | |||
2237 | return_value.SetValueType(Value::ValueType::Scalar); | |||
2238 | return_value.SetCompilerType(clang_uint32_t_type); | |||
2239 | return_value.GetScalar() = 0; | |||
2240 | ||||
2241 | diagnostics.Clear(); | |||
2242 | ||||
2243 | // Run the function | |||
2244 | ExpressionResults results = | |||
2245 | get_shared_cache_class_info_function->ExecuteFunction( | |||
2246 | exe_ctx, &m_args, options, diagnostics, return_value); | |||
2247 | ||||
2248 | if (results == eExpressionCompleted) { | |||
2249 | // The result is the number of ClassInfo structures that were filled in | |||
2250 | num_class_infos = return_value.GetScalar().ULong(); | |||
2251 | LLDB_LOG(log, "Discovered {0} Objective-C classes in the shared cache",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Discovered {0} Objective-C classes in the shared cache" , num_class_infos); } while (0) | |||
2252 | num_class_infos)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "Discovered {0} Objective-C classes in the shared cache" , num_class_infos); } while (0); | |||
2253 | // Assert if there were more classes than we pre-allocated | |||
2254 | // room for. | |||
2255 | assert(num_class_infos <= max_num_classes)(static_cast <bool> (num_class_infos <= max_num_classes ) ? void (0) : __assert_fail ("num_class_infos <= max_num_classes" , "lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , 2255, __extension__ __PRETTY_FUNCTION__)); | |||
2256 | if (num_class_infos > 0) { | |||
2257 | if (num_class_infos > max_num_classes) { | |||
2258 | num_class_infos = max_num_classes; | |||
2259 | ||||
2260 | success = false; | |||
2261 | } else { | |||
2262 | success = true; | |||
2263 | } | |||
2264 | ||||
2265 | // Read the relative selector offset. | |||
2266 | DataBufferHeap relative_selector_offset_buffer(64, 0); | |||
2267 | if (process->ReadMemory(relative_selector_offset_addr, | |||
2268 | relative_selector_offset_buffer.GetBytes(), | |||
2269 | relative_selector_offset_buffer.GetByteSize(), | |||
2270 | err) == | |||
2271 | relative_selector_offset_buffer.GetByteSize()) { | |||
2272 | DataExtractor relative_selector_offset_data( | |||
2273 | relative_selector_offset_buffer.GetBytes(), | |||
2274 | relative_selector_offset_buffer.GetByteSize(), | |||
2275 | process->GetByteOrder(), addr_size); | |||
2276 | lldb::offset_t offset = 0; | |||
2277 | uint64_t relative_selector_offset = | |||
2278 | relative_selector_offset_data.GetU64(&offset); | |||
2279 | if (relative_selector_offset > 0) { | |||
2280 | // The offset is relative to the objc_opt struct. | |||
2281 | m_runtime.SetRelativeSelectorBaseAddr(objc_opt_ptr + | |||
2282 | relative_selector_offset); | |||
2283 | } | |||
2284 | } | |||
2285 | ||||
2286 | // Read the ClassInfo structures | |||
2287 | DataBufferHeap class_infos_buffer( | |||
2288 | num_class_infos * class_info_byte_size, 0); | |||
2289 | if (process->ReadMemory(class_infos_addr, class_infos_buffer.GetBytes(), | |||
2290 | class_infos_buffer.GetByteSize(), | |||
2291 | err) == class_infos_buffer.GetByteSize()) { | |||
2292 | DataExtractor class_infos_data(class_infos_buffer.GetBytes(), | |||
2293 | class_infos_buffer.GetByteSize(), | |||
2294 | process->GetByteOrder(), addr_size); | |||
2295 | ||||
2296 | m_runtime.ParseClassInfoArray(class_infos_data, num_class_infos); | |||
2297 | } | |||
2298 | } else { | |||
2299 | success = true; | |||
2300 | } | |||
2301 | } else { | |||
2302 | if (log) { | |||
2303 | LLDB_LOGF(log, "Error evaluating our find class name function.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("Error evaluating our find class name function." ); } while (0); | |||
2304 | diagnostics.Dump(log); | |||
2305 | } | |||
2306 | } | |||
2307 | } else { | |||
2308 | if (log) { | |||
2309 | LLDB_LOGF(log, "Error writing function arguments.")do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("Error writing function arguments.") ; } while (0); | |||
2310 | diagnostics.Dump(log); | |||
2311 | } | |||
2312 | } | |||
2313 | ||||
2314 | // Deallocate the memory we allocated for the ClassInfo array | |||
2315 | process->DeallocateMemory(class_infos_addr); | |||
2316 | ||||
2317 | return DescriptorMapUpdateResult(success, num_class_infos); | |||
2318 | } | |||
2319 | ||||
2320 | lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheReadOnlyAddress() { | |||
2321 | Process *process = GetProcess(); | |||
2322 | ||||
2323 | if (process) { | |||
2324 | ModuleSP objc_module_sp(GetObjCModule()); | |||
2325 | ||||
2326 | if (objc_module_sp) { | |||
2327 | ObjectFile *objc_object = objc_module_sp->GetObjectFile(); | |||
2328 | ||||
2329 | if (objc_object) { | |||
2330 | SectionList *section_list = objc_module_sp->GetSectionList(); | |||
2331 | ||||
2332 | if (section_list) { | |||
2333 | SectionSP text_segment_sp( | |||
2334 | section_list->FindSectionByName(ConstString("__TEXT"))); | |||
2335 | ||||
2336 | if (text_segment_sp) { | |||
2337 | SectionSP objc_opt_section_sp( | |||
2338 | text_segment_sp->GetChildren().FindSectionByName( | |||
2339 | ConstString("__objc_opt_ro"))); | |||
2340 | ||||
2341 | if (objc_opt_section_sp) { | |||
2342 | return objc_opt_section_sp->GetLoadBaseAddress( | |||
2343 | &process->GetTarget()); | |||
2344 | } | |||
2345 | } | |||
2346 | } | |||
2347 | } | |||
2348 | } | |||
2349 | } | |||
2350 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
2351 | } | |||
2352 | ||||
2353 | lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheBaseAddress() { | |||
2354 | StructuredData::ObjectSP info = m_process->GetSharedCacheInfo(); | |||
2355 | if (!info) | |||
2356 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
2357 | ||||
2358 | StructuredData::Dictionary *info_dict = info->GetAsDictionary(); | |||
2359 | if (!info_dict) | |||
2360 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
2361 | ||||
2362 | StructuredData::ObjectSP value = | |||
2363 | info_dict->GetValueForKey("shared_cache_base_address"); | |||
2364 | if (!value) | |||
2365 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
2366 | ||||
2367 | return value->GetIntegerValue(LLDB_INVALID_ADDRESS(18446744073709551615UL)); | |||
2368 | } | |||
2369 | ||||
2370 | void AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded() { | |||
2371 | LLDB_SCOPED_TIMER()static ::lldb_private::Timer::Category _cat(__PRETTY_FUNCTION__ ); ::lldb_private::Timer _scoped_timer(_cat, "%s", __PRETTY_FUNCTION__ ); | |||
2372 | ||||
2373 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
2374 | ||||
2375 | // Else we need to check with our process to see when the map was updated. | |||
2376 | Process *process = GetProcess(); | |||
2377 | ||||
2378 | if (process) { | |||
2379 | RemoteNXMapTable hash_table; | |||
2380 | ||||
2381 | // Update the process stop ID that indicates the last time we updated the | |||
2382 | // map, whether it was successful or not. | |||
2383 | m_isa_to_descriptor_stop_id = process->GetStopID(); | |||
2384 | ||||
2385 | // Ask the runtime is the realized class generation count changed. Unlike | |||
2386 | // the hash table, this accounts for lazily named classes. | |||
2387 | const bool class_count_changed = RealizedClassGenerationCountChanged(); | |||
2388 | ||||
2389 | if (!m_hash_signature.NeedsUpdate(process, this, hash_table) && | |||
2390 | !class_count_changed) | |||
2391 | return; | |||
2392 | ||||
2393 | m_hash_signature.UpdateSignature(hash_table); | |||
2394 | ||||
2395 | // Grab the dynamically loaded Objective-C classes from memory. | |||
2396 | DescriptorMapUpdateResult dynamic_update_result = | |||
2397 | m_dynamic_class_info_extractor.UpdateISAToDescriptorMap(hash_table); | |||
2398 | ||||
2399 | // Now get the objc classes that are baked into the Objective-C runtime in | |||
2400 | // the shared cache, but only once per process as this data never changes | |||
2401 | if (!m_loaded_objc_opt) { | |||
2402 | // it is legitimately possible for the shared cache to be empty - in that | |||
2403 | // case, the dynamic hash table will contain all the class information we | |||
2404 | // need; the situation we're trying to detect is one where we aren't | |||
2405 | // seeing class information from the runtime - in order to detect that | |||
2406 | // vs. just the shared cache being empty or sparsely populated, we set an | |||
2407 | // arbitrary (very low) threshold for the number of classes that we want | |||
2408 | // to see in a "good" scenario - anything below that is suspicious | |||
2409 | // (Foundation alone has thousands of classes) | |||
2410 | const uint32_t num_classes_to_warn_at = 500; | |||
2411 | ||||
2412 | DescriptorMapUpdateResult shared_cache_update_result = | |||
2413 | m_shared_cache_class_info_extractor.UpdateISAToDescriptorMap(); | |||
2414 | ||||
2415 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2416 | "attempted to read objc class data - results: "do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2417 | "[dynamic_update]: ran: %s, count: %" PRIu32do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2418 | " [shared_cache_update]: ran: %s, count: %" PRIu32,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2419 | dynamic_update_result.m_update_ran ? "yes" : "no",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2420 | dynamic_update_result.m_num_found,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2421 | shared_cache_update_result.m_update_ran ? "yes" : "no",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0) | |||
2422 | shared_cache_update_result.m_num_found)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("attempted to read objc class data - results: " "[dynamic_update]: ran: %s, count: %" "u" " [shared_cache_update]: ran: %s, count: %" "u", dynamic_update_result.m_update_ran ? "yes" : "no", dynamic_update_result .m_num_found, shared_cache_update_result.m_update_ran ? "yes" : "no", shared_cache_update_result.m_num_found); } while (0); | |||
2423 | ||||
2424 | // warn if: | |||
2425 | // - we could not run either expression | |||
2426 | // - we found fewer than num_classes_to_warn_at classes total | |||
2427 | if ((!shared_cache_update_result.m_update_ran) || | |||
2428 | (!dynamic_update_result.m_update_ran)) | |||
2429 | WarnIfNoClassesCached( | |||
2430 | SharedCacheWarningReason::eExpressionExecutionFailure); | |||
2431 | else if (dynamic_update_result.m_num_found + | |||
2432 | shared_cache_update_result.m_num_found < | |||
2433 | num_classes_to_warn_at) | |||
2434 | WarnIfNoClassesCached(SharedCacheWarningReason::eNotEnoughClassesRead); | |||
2435 | else | |||
2436 | m_loaded_objc_opt = true; | |||
2437 | } | |||
2438 | } else { | |||
2439 | m_isa_to_descriptor_stop_id = UINT32_MAX(4294967295U); | |||
2440 | } | |||
2441 | } | |||
2442 | ||||
2443 | bool AppleObjCRuntimeV2::RealizedClassGenerationCountChanged() { | |||
2444 | Process *process = GetProcess(); | |||
2445 | if (!process) | |||
2446 | return false; | |||
2447 | ||||
2448 | Status error; | |||
2449 | uint64_t objc_debug_realized_class_generation_count = | |||
2450 | ExtractRuntimeGlobalSymbol( | |||
2451 | process, ConstString("objc_debug_realized_class_generation_count"), | |||
2452 | GetObjCModule(), error); | |||
2453 | if (error.Fail()) | |||
2454 | return false; | |||
2455 | ||||
2456 | if (m_realized_class_generation_count == | |||
2457 | objc_debug_realized_class_generation_count) | |||
2458 | return false; | |||
2459 | ||||
2460 | Log *log = GetLog(LLDBLog::Process | LLDBLog::Types); | |||
2461 | LLDB_LOG(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "objc_debug_realized_class_generation_count changed from {0} to {1}" , m_realized_class_generation_count, objc_debug_realized_class_generation_count ); } while (0) | |||
2462 | "objc_debug_realized_class_generation_count changed from {0} to {1}",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "objc_debug_realized_class_generation_count changed from {0} to {1}" , m_realized_class_generation_count, objc_debug_realized_class_generation_count ); } while (0) | |||
2463 | m_realized_class_generation_count,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "objc_debug_realized_class_generation_count changed from {0} to {1}" , m_realized_class_generation_count, objc_debug_realized_class_generation_count ); } while (0) | |||
2464 | objc_debug_realized_class_generation_count)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Format("lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp" , __func__, "objc_debug_realized_class_generation_count changed from {0} to {1}" , m_realized_class_generation_count, objc_debug_realized_class_generation_count ); } while (0); | |||
2465 | ||||
2466 | m_realized_class_generation_count = | |||
2467 | objc_debug_realized_class_generation_count; | |||
2468 | ||||
2469 | return true; | |||
2470 | } | |||
2471 | ||||
2472 | static bool DoesProcessHaveSharedCache(Process &process) { | |||
2473 | PlatformSP platform_sp = process.GetTarget().GetPlatform(); | |||
2474 | if (!platform_sp) | |||
2475 | return true; // this should not happen | |||
2476 | ||||
2477 | llvm::StringRef platform_plugin_name_sr = platform_sp->GetPluginName(); | |||
2478 | if (platform_plugin_name_sr.endswith("-simulator")) | |||
2479 | return false; | |||
2480 | ||||
2481 | return true; | |||
2482 | } | |||
2483 | ||||
2484 | void AppleObjCRuntimeV2::WarnIfNoClassesCached( | |||
2485 | SharedCacheWarningReason reason) { | |||
2486 | if (GetProcess() && !DoesProcessHaveSharedCache(*GetProcess())) { | |||
2487 | // Simulators do not have the objc_opt_ro class table so don't actually | |||
2488 | // complain to the user | |||
2489 | return; | |||
2490 | } | |||
2491 | ||||
2492 | Debugger &debugger(GetProcess()->GetTarget().GetDebugger()); | |||
2493 | switch (reason) { | |||
2494 | case SharedCacheWarningReason::eNotEnoughClassesRead: | |||
2495 | Debugger::ReportWarning("could not find Objective-C class data in " | |||
2496 | "the process. This may reduce the quality of type " | |||
2497 | "information available.\n", | |||
2498 | debugger.GetID(), &m_no_classes_cached_warning); | |||
2499 | break; | |||
2500 | case SharedCacheWarningReason::eExpressionExecutionFailure: | |||
2501 | Debugger::ReportWarning( | |||
2502 | "could not execute support code to read " | |||
2503 | "Objective-C class data in the process. This may " | |||
2504 | "reduce the quality of type information available.\n", | |||
2505 | debugger.GetID(), &m_no_classes_cached_warning); | |||
2506 | break; | |||
2507 | } | |||
2508 | } | |||
2509 | ||||
2510 | void AppleObjCRuntimeV2::WarnIfNoExpandedSharedCache() { | |||
2511 | if (!m_objc_module_sp) | |||
2512 | return; | |||
2513 | ||||
2514 | ObjectFile *object_file = m_objc_module_sp->GetObjectFile(); | |||
2515 | if (!object_file) | |||
2516 | return; | |||
2517 | ||||
2518 | if (!object_file->IsInMemory()) | |||
2519 | return; | |||
2520 | ||||
2521 | Target &target = GetProcess()->GetTarget(); | |||
2522 | Debugger &debugger = target.GetDebugger(); | |||
2523 | ||||
2524 | std::string buffer; | |||
2525 | llvm::raw_string_ostream os(buffer); | |||
2526 | ||||
2527 | os << "libobjc.A.dylib is being read from process memory. This " | |||
2528 | "indicates that LLDB could not "; | |||
2529 | if (PlatformSP platform_sp = target.GetPlatform()) { | |||
2530 | if (platform_sp->IsHost()) { | |||
2531 | os << "read from the host's in-memory shared cache"; | |||
2532 | } else { | |||
2533 | os << "find the on-disk shared cache for this device"; | |||
2534 | } | |||
2535 | } else { | |||
2536 | os << "read from the shared cache"; | |||
2537 | } | |||
2538 | os << ". This will likely reduce debugging performance.\n"; | |||
2539 | ||||
2540 | Debugger::ReportWarning(os.str(), debugger.GetID(), | |||
2541 | &m_no_expanded_cache_warning); | |||
2542 | } | |||
2543 | ||||
2544 | DeclVendor *AppleObjCRuntimeV2::GetDeclVendor() { | |||
2545 | if (!m_decl_vendor_up) | |||
2546 | m_decl_vendor_up = std::make_unique<AppleObjCDeclVendor>(*this); | |||
2547 | ||||
2548 | return m_decl_vendor_up.get(); | |||
2549 | } | |||
2550 | ||||
2551 | lldb::addr_t AppleObjCRuntimeV2::LookupRuntimeSymbol(ConstString name) { | |||
2552 | lldb::addr_t ret = LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
2553 | ||||
2554 | const char *name_cstr = name.AsCString(); | |||
2555 | ||||
2556 | if (name_cstr) { | |||
2557 | llvm::StringRef name_strref(name_cstr); | |||
2558 | ||||
2559 | llvm::StringRef ivar_prefix("OBJC_IVAR_$_"); | |||
2560 | llvm::StringRef class_prefix("OBJC_CLASS_$_"); | |||
2561 | ||||
2562 | if (name_strref.startswith(ivar_prefix)) { | |||
2563 | llvm::StringRef ivar_skipped_prefix = | |||
2564 | name_strref.substr(ivar_prefix.size()); | |||
2565 | std::pair<llvm::StringRef, llvm::StringRef> class_and_ivar = | |||
2566 | ivar_skipped_prefix.split('.'); | |||
2567 | ||||
2568 | if (class_and_ivar.first.size() && class_and_ivar.second.size()) { | |||
2569 | const ConstString class_name_cs(class_and_ivar.first); | |||
2570 | ClassDescriptorSP descriptor = | |||
2571 | ObjCLanguageRuntime::GetClassDescriptorFromClassName(class_name_cs); | |||
2572 | ||||
2573 | if (descriptor) { | |||
2574 | const ConstString ivar_name_cs(class_and_ivar.second); | |||
2575 | const char *ivar_name_cstr = ivar_name_cs.AsCString(); | |||
2576 | ||||
2577 | auto ivar_func = [&ret, | |||
2578 | ivar_name_cstr](const char *name, const char *type, | |||
2579 | lldb::addr_t offset_addr, | |||
2580 | uint64_t size) -> lldb::addr_t { | |||
2581 | if (!strcmp(name, ivar_name_cstr)) { | |||
2582 | ret = offset_addr; | |||
2583 | return true; | |||
2584 | } | |||
2585 | return false; | |||
2586 | }; | |||
2587 | ||||
2588 | descriptor->Describe( | |||
2589 | std::function<void(ObjCISA)>(nullptr), | |||
2590 | std::function<bool(const char *, const char *)>(nullptr), | |||
2591 | std::function<bool(const char *, const char *)>(nullptr), | |||
2592 | ivar_func); | |||
2593 | } | |||
2594 | } | |||
2595 | } else if (name_strref.startswith(class_prefix)) { | |||
2596 | llvm::StringRef class_skipped_prefix = | |||
2597 | name_strref.substr(class_prefix.size()); | |||
2598 | const ConstString class_name_cs(class_skipped_prefix); | |||
2599 | ClassDescriptorSP descriptor = | |||
2600 | GetClassDescriptorFromClassName(class_name_cs); | |||
2601 | ||||
2602 | if (descriptor) | |||
2603 | ret = descriptor->GetISA(); | |||
2604 | } | |||
2605 | } | |||
2606 | ||||
2607 | return ret; | |||
2608 | } | |||
2609 | ||||
2610 | AppleObjCRuntimeV2::NonPointerISACache * | |||
2611 | AppleObjCRuntimeV2::NonPointerISACache::CreateInstance( | |||
2612 | AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) { | |||
2613 | Process *process(runtime.GetProcess()); | |||
2614 | ||||
2615 | Status error; | |||
2616 | ||||
2617 | Log *log = GetLog(LLDBLog::Types); | |||
2618 | ||||
2619 | auto objc_debug_isa_magic_mask = ExtractRuntimeGlobalSymbol( | |||
2620 | process, ConstString("objc_debug_isa_magic_mask"), objc_module_sp, error); | |||
2621 | if (error.Fail()) | |||
2622 | return nullptr; | |||
2623 | ||||
2624 | auto objc_debug_isa_magic_value = ExtractRuntimeGlobalSymbol( | |||
2625 | process, ConstString("objc_debug_isa_magic_value"), objc_module_sp, | |||
2626 | error); | |||
2627 | if (error.Fail()) | |||
2628 | return nullptr; | |||
2629 | ||||
2630 | auto objc_debug_isa_class_mask = ExtractRuntimeGlobalSymbol( | |||
2631 | process, ConstString("objc_debug_isa_class_mask"), objc_module_sp, error); | |||
2632 | if (error.Fail()) | |||
2633 | return nullptr; | |||
2634 | ||||
2635 | if (log) | |||
2636 | log->PutCString("AOCRT::NPI: Found all the non-indexed ISA masks"); | |||
2637 | ||||
2638 | bool foundError = false; | |||
2639 | auto objc_debug_indexed_isa_magic_mask = ExtractRuntimeGlobalSymbol( | |||
2640 | process, ConstString("objc_debug_indexed_isa_magic_mask"), objc_module_sp, | |||
2641 | error); | |||
2642 | foundError |= error.Fail(); | |||
2643 | ||||
2644 | auto objc_debug_indexed_isa_magic_value = ExtractRuntimeGlobalSymbol( | |||
2645 | process, ConstString("objc_debug_indexed_isa_magic_value"), | |||
2646 | objc_module_sp, error); | |||
2647 | foundError |= error.Fail(); | |||
2648 | ||||
2649 | auto objc_debug_indexed_isa_index_mask = ExtractRuntimeGlobalSymbol( | |||
2650 | process, ConstString("objc_debug_indexed_isa_index_mask"), objc_module_sp, | |||
2651 | error); | |||
2652 | foundError |= error.Fail(); | |||
2653 | ||||
2654 | auto objc_debug_indexed_isa_index_shift = ExtractRuntimeGlobalSymbol( | |||
2655 | process, ConstString("objc_debug_indexed_isa_index_shift"), | |||
2656 | objc_module_sp, error); | |||
2657 | foundError |= error.Fail(); | |||
2658 | ||||
2659 | auto objc_indexed_classes = | |||
2660 | ExtractRuntimeGlobalSymbol(process, ConstString("objc_indexed_classes"), | |||
2661 | objc_module_sp, error, false); | |||
2662 | foundError |= error.Fail(); | |||
2663 | ||||
2664 | if (log) | |||
2665 | log->PutCString("AOCRT::NPI: Found all the indexed ISA masks"); | |||
2666 | ||||
2667 | // we might want to have some rules to outlaw these other values (e.g if the | |||
2668 | // mask is zero but the value is non-zero, ...) | |||
2669 | ||||
2670 | return new NonPointerISACache( | |||
2671 | runtime, objc_module_sp, objc_debug_isa_class_mask, | |||
2672 | objc_debug_isa_magic_mask, objc_debug_isa_magic_value, | |||
2673 | objc_debug_indexed_isa_magic_mask, objc_debug_indexed_isa_magic_value, | |||
2674 | objc_debug_indexed_isa_index_mask, objc_debug_indexed_isa_index_shift, | |||
2675 | foundError ? 0 : objc_indexed_classes); | |||
2676 | } | |||
2677 | ||||
2678 | AppleObjCRuntimeV2::TaggedPointerVendorV2 * | |||
2679 | AppleObjCRuntimeV2::TaggedPointerVendorV2::CreateInstance( | |||
2680 | AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) { | |||
2681 | Process *process(runtime.GetProcess()); | |||
2682 | ||||
2683 | Status error; | |||
2684 | ||||
2685 | auto objc_debug_taggedpointer_mask = ExtractRuntimeGlobalSymbol( | |||
2686 | process, ConstString("objc_debug_taggedpointer_mask"), objc_module_sp, | |||
2687 | error); | |||
2688 | if (error.Fail()) | |||
2689 | return new TaggedPointerVendorLegacy(runtime); | |||
2690 | ||||
2691 | auto objc_debug_taggedpointer_slot_shift = ExtractRuntimeGlobalSymbol( | |||
2692 | process, ConstString("objc_debug_taggedpointer_slot_shift"), | |||
2693 | objc_module_sp, error, true, 4); | |||
2694 | if (error.Fail()) | |||
2695 | return new TaggedPointerVendorLegacy(runtime); | |||
2696 | ||||
2697 | auto objc_debug_taggedpointer_slot_mask = ExtractRuntimeGlobalSymbol( | |||
2698 | process, ConstString("objc_debug_taggedpointer_slot_mask"), | |||
2699 | objc_module_sp, error, true, 4); | |||
2700 | if (error.Fail()) | |||
2701 | return new TaggedPointerVendorLegacy(runtime); | |||
2702 | ||||
2703 | auto objc_debug_taggedpointer_payload_lshift = ExtractRuntimeGlobalSymbol( | |||
2704 | process, ConstString("objc_debug_taggedpointer_payload_lshift"), | |||
2705 | objc_module_sp, error, true, 4); | |||
2706 | if (error.Fail()) | |||
2707 | return new TaggedPointerVendorLegacy(runtime); | |||
2708 | ||||
2709 | auto objc_debug_taggedpointer_payload_rshift = ExtractRuntimeGlobalSymbol( | |||
2710 | process, ConstString("objc_debug_taggedpointer_payload_rshift"), | |||
2711 | objc_module_sp, error, true, 4); | |||
2712 | if (error.Fail()) | |||
2713 | return new TaggedPointerVendorLegacy(runtime); | |||
2714 | ||||
2715 | auto objc_debug_taggedpointer_classes = ExtractRuntimeGlobalSymbol( | |||
2716 | process, ConstString("objc_debug_taggedpointer_classes"), objc_module_sp, | |||
2717 | error, false); | |||
2718 | if (error.Fail()) | |||
2719 | return new TaggedPointerVendorLegacy(runtime); | |||
2720 | ||||
2721 | // try to detect the "extended tagged pointer" variables - if any are | |||
2722 | // missing, use the non-extended vendor | |||
2723 | do { | |||
2724 | auto objc_debug_taggedpointer_ext_mask = ExtractRuntimeGlobalSymbol( | |||
2725 | process, ConstString("objc_debug_taggedpointer_ext_mask"), | |||
2726 | objc_module_sp, error); | |||
2727 | if (error.Fail()) | |||
2728 | break; | |||
2729 | ||||
2730 | auto objc_debug_taggedpointer_ext_slot_shift = ExtractRuntimeGlobalSymbol( | |||
2731 | process, ConstString("objc_debug_taggedpointer_ext_slot_shift"), | |||
2732 | objc_module_sp, error, true, 4); | |||
2733 | if (error.Fail()) | |||
2734 | break; | |||
2735 | ||||
2736 | auto objc_debug_taggedpointer_ext_slot_mask = ExtractRuntimeGlobalSymbol( | |||
2737 | process, ConstString("objc_debug_taggedpointer_ext_slot_mask"), | |||
2738 | objc_module_sp, error, true, 4); | |||
2739 | if (error.Fail()) | |||
2740 | break; | |||
2741 | ||||
2742 | auto objc_debug_taggedpointer_ext_classes = ExtractRuntimeGlobalSymbol( | |||
2743 | process, ConstString("objc_debug_taggedpointer_ext_classes"), | |||
2744 | objc_module_sp, error, false); | |||
2745 | if (error.Fail()) | |||
2746 | break; | |||
2747 | ||||
2748 | auto objc_debug_taggedpointer_ext_payload_lshift = | |||
2749 | ExtractRuntimeGlobalSymbol( | |||
2750 | process, ConstString("objc_debug_taggedpointer_ext_payload_lshift"), | |||
2751 | objc_module_sp, error, true, 4); | |||
2752 | if (error.Fail()) | |||
2753 | break; | |||
2754 | ||||
2755 | auto objc_debug_taggedpointer_ext_payload_rshift = | |||
2756 | ExtractRuntimeGlobalSymbol( | |||
2757 | process, ConstString("objc_debug_taggedpointer_ext_payload_rshift"), | |||
2758 | objc_module_sp, error, true, 4); | |||
2759 | if (error.Fail()) | |||
2760 | break; | |||
2761 | ||||
2762 | return new TaggedPointerVendorExtended( | |||
2763 | runtime, objc_debug_taggedpointer_mask, | |||
2764 | objc_debug_taggedpointer_ext_mask, objc_debug_taggedpointer_slot_shift, | |||
2765 | objc_debug_taggedpointer_ext_slot_shift, | |||
2766 | objc_debug_taggedpointer_slot_mask, | |||
2767 | objc_debug_taggedpointer_ext_slot_mask, | |||
2768 | objc_debug_taggedpointer_payload_lshift, | |||
2769 | objc_debug_taggedpointer_payload_rshift, | |||
2770 | objc_debug_taggedpointer_ext_payload_lshift, | |||
2771 | objc_debug_taggedpointer_ext_payload_rshift, | |||
2772 | objc_debug_taggedpointer_classes, objc_debug_taggedpointer_ext_classes); | |||
2773 | } while (false); | |||
2774 | ||||
2775 | // we might want to have some rules to outlaw these values (e.g if the | |||
2776 | // table's address is zero) | |||
2777 | ||||
2778 | return new TaggedPointerVendorRuntimeAssisted( | |||
2779 | runtime, objc_debug_taggedpointer_mask, | |||
2780 | objc_debug_taggedpointer_slot_shift, objc_debug_taggedpointer_slot_mask, | |||
2781 | objc_debug_taggedpointer_payload_lshift, | |||
2782 | objc_debug_taggedpointer_payload_rshift, | |||
2783 | objc_debug_taggedpointer_classes); | |||
2784 | } | |||
2785 | ||||
2786 | bool AppleObjCRuntimeV2::TaggedPointerVendorLegacy::IsPossibleTaggedPointer( | |||
2787 | lldb::addr_t ptr) { | |||
2788 | return (ptr & 1); | |||
2789 | } | |||
2790 | ||||
2791 | ObjCLanguageRuntime::ClassDescriptorSP | |||
2792 | AppleObjCRuntimeV2::TaggedPointerVendorLegacy::GetClassDescriptor( | |||
2793 | lldb::addr_t ptr) { | |||
2794 | if (!IsPossibleTaggedPointer(ptr)) | |||
2795 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2796 | ||||
2797 | uint32_t foundation_version = m_runtime.GetFoundationVersion(); | |||
2798 | ||||
2799 | if (foundation_version == LLDB_INVALID_MODULE_VERSION(4294967295U)) | |||
2800 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2801 | ||||
2802 | uint64_t class_bits = (ptr & 0xE) >> 1; | |||
2803 | ConstString name; | |||
2804 | ||||
2805 | static ConstString g_NSAtom("NSAtom"); | |||
2806 | static ConstString g_NSNumber("NSNumber"); | |||
2807 | static ConstString g_NSDateTS("NSDateTS"); | |||
2808 | static ConstString g_NSManagedObject("NSManagedObject"); | |||
2809 | static ConstString g_NSDate("NSDate"); | |||
2810 | ||||
2811 | if (foundation_version >= 900) { | |||
2812 | switch (class_bits) { | |||
2813 | case 0: | |||
2814 | name = g_NSAtom; | |||
2815 | break; | |||
2816 | case 3: | |||
2817 | name = g_NSNumber; | |||
2818 | break; | |||
2819 | case 4: | |||
2820 | name = g_NSDateTS; | |||
2821 | break; | |||
2822 | case 5: | |||
2823 | name = g_NSManagedObject; | |||
2824 | break; | |||
2825 | case 6: | |||
2826 | name = g_NSDate; | |||
2827 | break; | |||
2828 | default: | |||
2829 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2830 | } | |||
2831 | } else { | |||
2832 | switch (class_bits) { | |||
2833 | case 1: | |||
2834 | name = g_NSNumber; | |||
2835 | break; | |||
2836 | case 5: | |||
2837 | name = g_NSManagedObject; | |||
2838 | break; | |||
2839 | case 6: | |||
2840 | name = g_NSDate; | |||
2841 | break; | |||
2842 | case 7: | |||
2843 | name = g_NSDateTS; | |||
2844 | break; | |||
2845 | default: | |||
2846 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2847 | } | |||
2848 | } | |||
2849 | ||||
2850 | lldb::addr_t unobfuscated = ptr ^ m_runtime.GetTaggedPointerObfuscator(); | |||
2851 | return ClassDescriptorSP(new ClassDescriptorV2Tagged(name, unobfuscated)); | |||
2852 | } | |||
2853 | ||||
2854 | AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted:: | |||
2855 | TaggedPointerVendorRuntimeAssisted( | |||
2856 | AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, | |||
2857 | uint32_t objc_debug_taggedpointer_slot_shift, | |||
2858 | uint32_t objc_debug_taggedpointer_slot_mask, | |||
2859 | uint32_t objc_debug_taggedpointer_payload_lshift, | |||
2860 | uint32_t objc_debug_taggedpointer_payload_rshift, | |||
2861 | lldb::addr_t objc_debug_taggedpointer_classes) | |||
2862 | : TaggedPointerVendorV2(runtime), m_cache(), | |||
2863 | m_objc_debug_taggedpointer_mask(objc_debug_taggedpointer_mask), | |||
2864 | m_objc_debug_taggedpointer_slot_shift( | |||
2865 | objc_debug_taggedpointer_slot_shift), | |||
2866 | m_objc_debug_taggedpointer_slot_mask(objc_debug_taggedpointer_slot_mask), | |||
2867 | m_objc_debug_taggedpointer_payload_lshift( | |||
2868 | objc_debug_taggedpointer_payload_lshift), | |||
2869 | m_objc_debug_taggedpointer_payload_rshift( | |||
2870 | objc_debug_taggedpointer_payload_rshift), | |||
2871 | m_objc_debug_taggedpointer_classes(objc_debug_taggedpointer_classes) {} | |||
2872 | ||||
2873 | bool AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted:: | |||
2874 | IsPossibleTaggedPointer(lldb::addr_t ptr) { | |||
2875 | return (ptr & m_objc_debug_taggedpointer_mask) != 0; | |||
2876 | } | |||
2877 | ||||
2878 | ObjCLanguageRuntime::ClassDescriptorSP | |||
2879 | AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::GetClassDescriptor( | |||
2880 | lldb::addr_t ptr) { | |||
2881 | ClassDescriptorSP actual_class_descriptor_sp; | |||
2882 | uint64_t unobfuscated = (ptr) ^ m_runtime.GetTaggedPointerObfuscator(); | |||
2883 | ||||
2884 | if (!IsPossibleTaggedPointer(unobfuscated)) | |||
2885 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2886 | ||||
2887 | uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_slot_shift) & | |||
2888 | m_objc_debug_taggedpointer_slot_mask; | |||
2889 | ||||
2890 | CacheIterator iterator = m_cache.find(slot), end = m_cache.end(); | |||
2891 | if (iterator != end) { | |||
2892 | actual_class_descriptor_sp = iterator->second; | |||
2893 | } else { | |||
2894 | Process *process(m_runtime.GetProcess()); | |||
2895 | uintptr_t slot_ptr = slot * process->GetAddressByteSize() + | |||
2896 | m_objc_debug_taggedpointer_classes; | |||
2897 | Status error; | |||
2898 | uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error); | |||
2899 | if (error.Fail() || slot_data == 0 || | |||
2900 | slot_data == uintptr_t(LLDB_INVALID_ADDRESS(18446744073709551615UL))) | |||
2901 | return nullptr; | |||
2902 | actual_class_descriptor_sp = | |||
2903 | m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data); | |||
2904 | if (!actual_class_descriptor_sp) { | |||
2905 | if (ABISP abi_sp = process->GetABI()) { | |||
2906 | ObjCISA fixed_isa = abi_sp->FixCodeAddress((ObjCISA)slot_data); | |||
2907 | actual_class_descriptor_sp = | |||
2908 | m_runtime.GetClassDescriptorFromISA(fixed_isa); | |||
2909 | } | |||
2910 | } | |||
2911 | if (!actual_class_descriptor_sp) | |||
2912 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2913 | m_cache[slot] = actual_class_descriptor_sp; | |||
2914 | } | |||
2915 | ||||
2916 | uint64_t data_payload = | |||
2917 | ((unobfuscated << m_objc_debug_taggedpointer_payload_lshift) >> | |||
2918 | m_objc_debug_taggedpointer_payload_rshift); | |||
2919 | int64_t data_payload_signed = | |||
2920 | ((int64_t)(unobfuscated << m_objc_debug_taggedpointer_payload_lshift) >> | |||
2921 | m_objc_debug_taggedpointer_payload_rshift); | |||
2922 | return ClassDescriptorSP(new ClassDescriptorV2Tagged( | |||
2923 | actual_class_descriptor_sp, data_payload, data_payload_signed)); | |||
2924 | } | |||
2925 | ||||
2926 | AppleObjCRuntimeV2::TaggedPointerVendorExtended::TaggedPointerVendorExtended( | |||
2927 | AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, | |||
2928 | uint64_t objc_debug_taggedpointer_ext_mask, | |||
2929 | uint32_t objc_debug_taggedpointer_slot_shift, | |||
2930 | uint32_t objc_debug_taggedpointer_ext_slot_shift, | |||
2931 | uint32_t objc_debug_taggedpointer_slot_mask, | |||
2932 | uint32_t objc_debug_taggedpointer_ext_slot_mask, | |||
2933 | uint32_t objc_debug_taggedpointer_payload_lshift, | |||
2934 | uint32_t objc_debug_taggedpointer_payload_rshift, | |||
2935 | uint32_t objc_debug_taggedpointer_ext_payload_lshift, | |||
2936 | uint32_t objc_debug_taggedpointer_ext_payload_rshift, | |||
2937 | lldb::addr_t objc_debug_taggedpointer_classes, | |||
2938 | lldb::addr_t objc_debug_taggedpointer_ext_classes) | |||
2939 | : TaggedPointerVendorRuntimeAssisted( | |||
2940 | runtime, objc_debug_taggedpointer_mask, | |||
2941 | objc_debug_taggedpointer_slot_shift, | |||
2942 | objc_debug_taggedpointer_slot_mask, | |||
2943 | objc_debug_taggedpointer_payload_lshift, | |||
2944 | objc_debug_taggedpointer_payload_rshift, | |||
2945 | objc_debug_taggedpointer_classes), | |||
2946 | m_ext_cache(), | |||
2947 | m_objc_debug_taggedpointer_ext_mask(objc_debug_taggedpointer_ext_mask), | |||
2948 | m_objc_debug_taggedpointer_ext_slot_shift( | |||
2949 | objc_debug_taggedpointer_ext_slot_shift), | |||
2950 | m_objc_debug_taggedpointer_ext_slot_mask( | |||
2951 | objc_debug_taggedpointer_ext_slot_mask), | |||
2952 | m_objc_debug_taggedpointer_ext_payload_lshift( | |||
2953 | objc_debug_taggedpointer_ext_payload_lshift), | |||
2954 | m_objc_debug_taggedpointer_ext_payload_rshift( | |||
2955 | objc_debug_taggedpointer_ext_payload_rshift), | |||
2956 | m_objc_debug_taggedpointer_ext_classes( | |||
2957 | objc_debug_taggedpointer_ext_classes) {} | |||
2958 | ||||
2959 | bool AppleObjCRuntimeV2::TaggedPointerVendorExtended:: | |||
2960 | IsPossibleExtendedTaggedPointer(lldb::addr_t ptr) { | |||
2961 | if (!IsPossibleTaggedPointer(ptr)) | |||
2962 | return false; | |||
2963 | ||||
2964 | if (m_objc_debug_taggedpointer_ext_mask == 0) | |||
2965 | return false; | |||
2966 | ||||
2967 | return ((ptr & m_objc_debug_taggedpointer_ext_mask) == | |||
2968 | m_objc_debug_taggedpointer_ext_mask); | |||
2969 | } | |||
2970 | ||||
2971 | ObjCLanguageRuntime::ClassDescriptorSP | |||
2972 | AppleObjCRuntimeV2::TaggedPointerVendorExtended::GetClassDescriptor( | |||
2973 | lldb::addr_t ptr) { | |||
2974 | ClassDescriptorSP actual_class_descriptor_sp; | |||
2975 | uint64_t unobfuscated = (ptr) ^ m_runtime.GetTaggedPointerObfuscator(); | |||
2976 | ||||
2977 | if (!IsPossibleTaggedPointer(unobfuscated)) | |||
2978 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
2979 | ||||
2980 | if (!IsPossibleExtendedTaggedPointer(unobfuscated)) | |||
2981 | return this->TaggedPointerVendorRuntimeAssisted::GetClassDescriptor(ptr); | |||
2982 | ||||
2983 | uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_ext_slot_shift) & | |||
2984 | m_objc_debug_taggedpointer_ext_slot_mask; | |||
2985 | ||||
2986 | CacheIterator iterator = m_ext_cache.find(slot), end = m_ext_cache.end(); | |||
2987 | if (iterator != end) { | |||
2988 | actual_class_descriptor_sp = iterator->second; | |||
2989 | } else { | |||
2990 | Process *process(m_runtime.GetProcess()); | |||
2991 | uintptr_t slot_ptr = slot * process->GetAddressByteSize() + | |||
2992 | m_objc_debug_taggedpointer_ext_classes; | |||
2993 | Status error; | |||
2994 | uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error); | |||
2995 | if (error.Fail() || slot_data == 0 || | |||
2996 | slot_data == uintptr_t(LLDB_INVALID_ADDRESS(18446744073709551615UL))) | |||
2997 | return nullptr; | |||
2998 | actual_class_descriptor_sp = | |||
2999 | m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data); | |||
3000 | if (!actual_class_descriptor_sp) | |||
3001 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
3002 | m_ext_cache[slot] = actual_class_descriptor_sp; | |||
3003 | } | |||
3004 | ||||
3005 | uint64_t data_payload = (((uint64_t)unobfuscated | |||
3006 | << m_objc_debug_taggedpointer_ext_payload_lshift) >> | |||
3007 | m_objc_debug_taggedpointer_ext_payload_rshift); | |||
3008 | int64_t data_payload_signed = | |||
3009 | ((int64_t)((int64_t)unobfuscated | |||
3010 | << m_objc_debug_taggedpointer_ext_payload_lshift) >> | |||
3011 | m_objc_debug_taggedpointer_ext_payload_rshift); | |||
3012 | ||||
3013 | return ClassDescriptorSP(new ClassDescriptorV2Tagged( | |||
3014 | actual_class_descriptor_sp, data_payload, data_payload_signed)); | |||
3015 | } | |||
3016 | ||||
3017 | AppleObjCRuntimeV2::NonPointerISACache::NonPointerISACache( | |||
3018 | AppleObjCRuntimeV2 &runtime, const ModuleSP &objc_module_sp, | |||
3019 | uint64_t objc_debug_isa_class_mask, uint64_t objc_debug_isa_magic_mask, | |||
3020 | uint64_t objc_debug_isa_magic_value, | |||
3021 | uint64_t objc_debug_indexed_isa_magic_mask, | |||
3022 | uint64_t objc_debug_indexed_isa_magic_value, | |||
3023 | uint64_t objc_debug_indexed_isa_index_mask, | |||
3024 | uint64_t objc_debug_indexed_isa_index_shift, | |||
3025 | lldb::addr_t objc_indexed_classes) | |||
3026 | : m_runtime(runtime), m_cache(), m_objc_module_wp(objc_module_sp), | |||
3027 | m_objc_debug_isa_class_mask(objc_debug_isa_class_mask), | |||
3028 | m_objc_debug_isa_magic_mask(objc_debug_isa_magic_mask), | |||
3029 | m_objc_debug_isa_magic_value(objc_debug_isa_magic_value), | |||
3030 | m_objc_debug_indexed_isa_magic_mask(objc_debug_indexed_isa_magic_mask), | |||
3031 | m_objc_debug_indexed_isa_magic_value(objc_debug_indexed_isa_magic_value), | |||
3032 | m_objc_debug_indexed_isa_index_mask(objc_debug_indexed_isa_index_mask), | |||
3033 | m_objc_debug_indexed_isa_index_shift(objc_debug_indexed_isa_index_shift), | |||
3034 | m_objc_indexed_classes(objc_indexed_classes), m_indexed_isa_cache() {} | |||
3035 | ||||
3036 | ObjCLanguageRuntime::ClassDescriptorSP | |||
3037 | AppleObjCRuntimeV2::NonPointerISACache::GetClassDescriptor(ObjCISA isa) { | |||
3038 | ObjCISA real_isa = 0; | |||
3039 | if (!EvaluateNonPointerISA(isa, real_isa)) | |||
3040 | return ObjCLanguageRuntime::ClassDescriptorSP(); | |||
3041 | auto cache_iter = m_cache.find(real_isa); | |||
3042 | if (cache_iter != m_cache.end()) | |||
3043 | return cache_iter->second; | |||
3044 | auto descriptor_sp = | |||
3045 | m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(real_isa); | |||
3046 | if (descriptor_sp) // cache only positive matches since the table might grow | |||
3047 | m_cache[real_isa] = descriptor_sp; | |||
3048 | return descriptor_sp; | |||
3049 | } | |||
3050 | ||||
3051 | bool AppleObjCRuntimeV2::NonPointerISACache::EvaluateNonPointerISA( | |||
3052 | ObjCISA isa, ObjCISA &ret_isa) { | |||
3053 | Log *log = GetLog(LLDBLog::Types); | |||
3054 | ||||
3055 | LLDB_LOGF(log, "AOCRT::NPI Evaluate(isa = 0x%" PRIx64 ")", (uint64_t)isa)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI Evaluate(isa = 0x%" "l" "x" ")", (uint64_t)isa); } while (0); | |||
3056 | ||||
3057 | if ((isa & ~m_objc_debug_isa_class_mask) == 0) | |||
3058 | return false; | |||
3059 | ||||
3060 | // If all of the indexed ISA variables are set, then its possible that this | |||
3061 | // ISA is indexed, and we should first try to get its value using the index. | |||
3062 | // Note, we check these variables first as the ObjC runtime will set at least | |||
3063 | // one of their values to 0 if they aren't needed. | |||
3064 | if (m_objc_debug_indexed_isa_magic_mask && | |||
3065 | m_objc_debug_indexed_isa_magic_value && | |||
3066 | m_objc_debug_indexed_isa_index_mask && | |||
3067 | m_objc_debug_indexed_isa_index_shift && m_objc_indexed_classes) { | |||
3068 | if ((isa & ~m_objc_debug_indexed_isa_index_mask) == 0) | |||
3069 | return false; | |||
3070 | ||||
3071 | if ((isa & m_objc_debug_indexed_isa_magic_mask) == | |||
3072 | m_objc_debug_indexed_isa_magic_value) { | |||
3073 | // Magic bits are correct, so try extract the index. | |||
3074 | uintptr_t index = (isa & m_objc_debug_indexed_isa_index_mask) >> | |||
3075 | m_objc_debug_indexed_isa_index_shift; | |||
3076 | // If the index is out of bounds of the length of the array then check if | |||
3077 | // the array has been updated. If that is the case then we should try | |||
3078 | // read the count again, and update the cache if the count has been | |||
3079 | // updated. | |||
3080 | if (index > m_indexed_isa_cache.size()) { | |||
3081 | LLDB_LOGF(log,do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (index = %" "l" "u" ") exceeds cache (size = %" "l" "u" ")", (uint64_t)index, (uint64_t)m_indexed_isa_cache. size()); } while (0) | |||
3082 | "AOCRT::NPI (index = %" PRIu64do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (index = %" "l" "u" ") exceeds cache (size = %" "l" "u" ")", (uint64_t)index, (uint64_t)m_indexed_isa_cache. size()); } while (0) | |||
3083 | ") exceeds cache (size = %" PRIu64 ")",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (index = %" "l" "u" ") exceeds cache (size = %" "l" "u" ")", (uint64_t)index, (uint64_t)m_indexed_isa_cache. size()); } while (0) | |||
3084 | (uint64_t)index, (uint64_t)m_indexed_isa_cache.size())do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (index = %" "l" "u" ") exceeds cache (size = %" "l" "u" ")", (uint64_t)index, (uint64_t)m_indexed_isa_cache. size()); } while (0); | |||
3085 | ||||
3086 | Process *process(m_runtime.GetProcess()); | |||
3087 | ||||
3088 | ModuleSP objc_module_sp(m_objc_module_wp.lock()); | |||
3089 | if (!objc_module_sp) | |||
3090 | return false; | |||
3091 | ||||
3092 | Status error; | |||
3093 | auto objc_indexed_classes_count = ExtractRuntimeGlobalSymbol( | |||
3094 | process, ConstString("objc_indexed_classes_count"), objc_module_sp, | |||
3095 | error); | |||
3096 | if (error.Fail()) | |||
3097 | return false; | |||
3098 | ||||
3099 | LLDB_LOGF(log, "AOCRT::NPI (new class count = %" PRIu64 ")",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (new class count = %" "l" "u" ")", (uint64_t)objc_indexed_classes_count); } while (0) | |||
3100 | (uint64_t)objc_indexed_classes_count)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (new class count = %" "l" "u" ")", (uint64_t)objc_indexed_classes_count); } while (0); | |||
3101 | ||||
3102 | if (objc_indexed_classes_count > m_indexed_isa_cache.size()) { | |||
3103 | // Read the class entries we don't have. We should just read all of | |||
3104 | // them instead of just the one we need as then we can cache those we | |||
3105 | // may need later. | |||
3106 | auto num_new_classes = | |||
3107 | objc_indexed_classes_count - m_indexed_isa_cache.size(); | |||
3108 | const uint32_t addr_size = process->GetAddressByteSize(); | |||
3109 | DataBufferHeap buffer(num_new_classes * addr_size, 0); | |||
3110 | ||||
3111 | lldb::addr_t last_read_class = | |||
3112 | m_objc_indexed_classes + (m_indexed_isa_cache.size() * addr_size); | |||
3113 | size_t bytes_read = process->ReadMemory( | |||
3114 | last_read_class, buffer.GetBytes(), buffer.GetByteSize(), error); | |||
3115 | if (error.Fail() || bytes_read != buffer.GetByteSize()) | |||
3116 | return false; | |||
3117 | ||||
3118 | LLDB_LOGF(log, "AOCRT::NPI (read new classes count = %" PRIu64 ")",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (read new classes count = %" "l" "u" ")", (uint64_t)num_new_classes); } while (0) | |||
3119 | (uint64_t)num_new_classes)do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI (read new classes count = %" "l" "u" ")", (uint64_t)num_new_classes); } while (0); | |||
3120 | ||||
3121 | // Append the new entries to the existing cache. | |||
3122 | DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), | |||
3123 | process->GetByteOrder(), | |||
3124 | process->GetAddressByteSize()); | |||
3125 | ||||
3126 | lldb::offset_t offset = 0; | |||
3127 | for (unsigned i = 0; i != num_new_classes; ++i) | |||
3128 | m_indexed_isa_cache.push_back(data.GetAddress(&offset)); | |||
3129 | } | |||
3130 | } | |||
3131 | ||||
3132 | // If the index is still out of range then this isn't a pointer. | |||
3133 | if (index > m_indexed_isa_cache.size()) | |||
3134 | return false; | |||
3135 | ||||
3136 | LLDB_LOGF(log, "AOCRT::NPI Evaluate(ret_isa = 0x%" PRIx64 ")",do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI Evaluate(ret_isa = 0x%" "l" "x" ")", (uint64_t)m_indexed_isa_cache[index]); } while (0) | |||
3137 | (uint64_t)m_indexed_isa_cache[index])do { ::lldb_private::Log *log_private = (log); if (log_private ) log_private->Printf("AOCRT::NPI Evaluate(ret_isa = 0x%" "l" "x" ")", (uint64_t)m_indexed_isa_cache[index]); } while (0); | |||
3138 | ||||
3139 | ret_isa = m_indexed_isa_cache[index]; | |||
3140 | return (ret_isa != 0); // this is a pointer so 0 is not a valid value | |||
3141 | } | |||
3142 | ||||
3143 | return false; | |||
3144 | } | |||
3145 | ||||
3146 | // Definitely not an indexed ISA, so try to use a mask to extract the pointer | |||
3147 | // from the ISA. | |||
3148 | if ((isa & m_objc_debug_isa_magic_mask) == m_objc_debug_isa_magic_value) { | |||
3149 | ret_isa = isa & m_objc_debug_isa_class_mask; | |||
3150 | return (ret_isa != 0); // this is a pointer so 0 is not a valid value | |||
3151 | } | |||
3152 | return false; | |||
3153 | } | |||
3154 | ||||
3155 | ObjCLanguageRuntime::EncodingToTypeSP AppleObjCRuntimeV2::GetEncodingToType() { | |||
3156 | if (!m_encoding_to_type_sp) | |||
3157 | m_encoding_to_type_sp = | |||
3158 | std::make_shared<AppleObjCTypeEncodingParser>(*this); | |||
3159 | return m_encoding_to_type_sp; | |||
3160 | } | |||
3161 | ||||
3162 | lldb_private::AppleObjCRuntime::ObjCISA | |||
3163 | AppleObjCRuntimeV2::GetPointerISA(ObjCISA isa) { | |||
3164 | ObjCISA ret = isa; | |||
3165 | ||||
3166 | if (auto *non_pointer_isa_cache = GetNonPointerIsaCache()) | |||
3167 | non_pointer_isa_cache->EvaluateNonPointerISA(isa, ret); | |||
3168 | ||||
3169 | return ret; | |||
3170 | } | |||
3171 | ||||
3172 | bool AppleObjCRuntimeV2::GetCFBooleanValuesIfNeeded() { | |||
3173 | if (m_CFBoolean_values) | |||
3174 | return true; | |||
3175 | ||||
3176 | static ConstString g_dunder_kCFBooleanFalse("__kCFBooleanFalse"); | |||
3177 | static ConstString g_dunder_kCFBooleanTrue("__kCFBooleanTrue"); | |||
3178 | static ConstString g_kCFBooleanFalse("kCFBooleanFalse"); | |||
3179 | static ConstString g_kCFBooleanTrue("kCFBooleanTrue"); | |||
3180 | ||||
3181 | std::function<lldb::addr_t(ConstString, ConstString)> get_symbol = | |||
3182 | [this](ConstString sym, ConstString real_sym) -> lldb::addr_t { | |||
3183 | SymbolContextList sc_list; | |||
3184 | GetProcess()->GetTarget().GetImages().FindSymbolsWithNameAndType( | |||
3185 | sym, lldb::eSymbolTypeData, sc_list); | |||
3186 | if (sc_list.GetSize() == 1) { | |||
3187 | SymbolContext sc; | |||
3188 | sc_list.GetContextAtIndex(0, sc); | |||
3189 | if (sc.symbol) | |||
3190 | return sc.symbol->GetLoadAddress(&GetProcess()->GetTarget()); | |||
3191 | } | |||
3192 | GetProcess()->GetTarget().GetImages().FindSymbolsWithNameAndType( | |||
3193 | real_sym, lldb::eSymbolTypeData, sc_list); | |||
3194 | if (sc_list.GetSize() != 1) | |||
3195 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
3196 | ||||
3197 | SymbolContext sc; | |||
3198 | sc_list.GetContextAtIndex(0, sc); | |||
3199 | if (!sc.symbol) | |||
3200 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
3201 | ||||
3202 | lldb::addr_t addr = sc.symbol->GetLoadAddress(&GetProcess()->GetTarget()); | |||
3203 | Status error; | |||
3204 | addr = GetProcess()->ReadPointerFromMemory(addr, error); | |||
3205 | if (error.Fail()) | |||
3206 | return LLDB_INVALID_ADDRESS(18446744073709551615UL); | |||
3207 | return addr; | |||
3208 | }; | |||
3209 | ||||
3210 | lldb::addr_t false_addr = get_symbol(g_dunder_kCFBooleanFalse, g_kCFBooleanFalse); | |||
3211 | lldb::addr_t true_addr = get_symbol(g_dunder_kCFBooleanTrue, g_kCFBooleanTrue); | |||
3212 | ||||
3213 | return (m_CFBoolean_values = {false_addr, true_addr}).operator bool(); | |||
3214 | } | |||
3215 | ||||
3216 | void AppleObjCRuntimeV2::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, | |||
3217 | lldb::addr_t &cf_false) { | |||
3218 | if (GetCFBooleanValuesIfNeeded()) { | |||
3219 | cf_true = m_CFBoolean_values->second; | |||
3220 | cf_false = m_CFBoolean_values->first; | |||
3221 | } else | |||
3222 | this->AppleObjCRuntime::GetValuesForGlobalCFBooleans(cf_true, cf_false); | |||
3223 | } | |||
3224 | ||||
3225 | #pragma mark Frame recognizers | |||
3226 | ||||
3227 | class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame { | |||
3228 | public: | |||
3229 | ObjCExceptionRecognizedStackFrame(StackFrameSP frame_sp) { | |||
3230 | ThreadSP thread_sp = frame_sp->GetThread(); | |||
3231 | ProcessSP process_sp = thread_sp->GetProcess(); | |||
3232 | ||||
3233 | const lldb::ABISP &abi = process_sp->GetABI(); | |||
3234 | if (!abi) | |||
3235 | return; | |||
3236 | ||||
3237 | TypeSystemClang *clang_ast_context = | |||
3238 | ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget()); | |||
3239 | if (!clang_ast_context) | |||
3240 | return; | |||
3241 | CompilerType voidstar = | |||
3242 | clang_ast_context->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); | |||
3243 | ||||
3244 | ValueList args; | |||
3245 | Value input_value; | |||
3246 | input_value.SetCompilerType(voidstar); | |||
3247 | args.PushValue(input_value); | |||
3248 | ||||
3249 | if (!abi->GetArgumentValues(*thread_sp, args)) | |||
3250 | return; | |||
3251 | ||||
3252 | addr_t exception_addr = args.GetValueAtIndex(0)->GetScalar().ULongLong(); | |||
3253 | ||||
3254 | Value value(exception_addr); | |||
3255 | value.SetCompilerType(voidstar); | |||
3256 | exception = ValueObjectConstResult::Create(frame_sp.get(), value, | |||
3257 | ConstString("exception")); | |||
3258 | exception = ValueObjectRecognizerSynthesizedValue::Create( | |||
3259 | *exception, eValueTypeVariableArgument); | |||
3260 | exception = exception->GetDynamicValue(eDynamicDontRunTarget); | |||
3261 | ||||
3262 | m_arguments = ValueObjectListSP(new ValueObjectList()); | |||
3263 | m_arguments->Append(exception); | |||
3264 | ||||
3265 | m_stop_desc = "hit Objective-C exception"; | |||
3266 | } | |||
3267 | ||||
3268 | ValueObjectSP exception; | |||
3269 | ||||
3270 | lldb::ValueObjectSP GetExceptionObject() override { return exception; } | |||
3271 | }; | |||
3272 | ||||
3273 | class ObjCExceptionThrowFrameRecognizer : public StackFrameRecognizer { | |||
3274 | lldb::RecognizedStackFrameSP | |||
3275 | RecognizeFrame(lldb::StackFrameSP frame) override { | |||
3276 | return lldb::RecognizedStackFrameSP( | |||
3277 | new ObjCExceptionRecognizedStackFrame(frame)); | |||
| ||||
3278 | }; | |||
3279 | std::string GetName() override { | |||
3280 | return "ObjC Exception Throw StackFrame Recognizer"; | |||
3281 | } | |||
3282 | }; | |||
3283 | ||||
3284 | static void RegisterObjCExceptionRecognizer(Process *process) { | |||
3285 | FileSpec module; | |||
3286 | ConstString function; | |||
3287 | std::tie(module, function) = AppleObjCRuntime::GetExceptionThrowLocation(); | |||
3288 | std::vector<ConstString> symbols = {function}; | |||
3289 | ||||
3290 | process->GetTarget().GetFrameRecognizerManager().AddRecognizer( | |||
3291 | StackFrameRecognizerSP(new ObjCExceptionThrowFrameRecognizer()), | |||
3292 | module.GetFilename(), symbols, | |||
3293 | /*first_instruction_only*/ true); | |||
3294 | } |
1 | //===-- StackFrameRecognizer.h ----------------------------------*- C++ -*-===// | |||
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 | #ifndef LLDB_TARGET_STACKFRAMERECOGNIZER_H | |||
10 | #define LLDB_TARGET_STACKFRAMERECOGNIZER_H | |||
11 | ||||
12 | #include "lldb/Core/ValueObject.h" | |||
13 | #include "lldb/Core/ValueObjectList.h" | |||
14 | #include "lldb/Symbol/VariableList.h" | |||
15 | #include "lldb/Target/StopInfo.h" | |||
16 | #include "lldb/Utility/StructuredData.h" | |||
17 | #include "lldb/lldb-private-forward.h" | |||
18 | #include "lldb/lldb-public.h" | |||
19 | ||||
20 | #include <vector> | |||
21 | ||||
22 | namespace lldb_private { | |||
23 | ||||
24 | /// \class RecognizedStackFrame | |||
25 | /// | |||
26 | /// This class provides extra information about a stack frame that was | |||
27 | /// provided by a specific stack frame recognizer. Right now, this class only | |||
28 | /// holds recognized arguments (via GetRecognizedArguments). | |||
29 | ||||
30 | class RecognizedStackFrame | |||
31 | : public std::enable_shared_from_this<RecognizedStackFrame> { | |||
32 | public: | |||
33 | virtual lldb::ValueObjectListSP GetRecognizedArguments() { | |||
34 | return m_arguments; | |||
35 | } | |||
36 | virtual lldb::ValueObjectSP GetExceptionObject() { | |||
37 | return lldb::ValueObjectSP(); | |||
38 | } | |||
39 | virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; }; | |||
40 | virtual ~RecognizedStackFrame() = default; | |||
41 | ||||
42 | std::string GetStopDescription() { return m_stop_desc; } | |||
43 | ||||
44 | protected: | |||
45 | lldb::ValueObjectListSP m_arguments; | |||
46 | std::string m_stop_desc; | |||
47 | }; | |||
48 | ||||
49 | /// \class StackFrameRecognizer | |||
50 | /// | |||
51 | /// A base class for frame recognizers. Subclasses (actual frame recognizers) | |||
52 | /// should implement RecognizeFrame to provide a RecognizedStackFrame for a | |||
53 | /// given stack frame. | |||
54 | ||||
55 | class StackFrameRecognizer | |||
56 | : public std::enable_shared_from_this<StackFrameRecognizer> { | |||
57 | public: | |||
58 | virtual lldb::RecognizedStackFrameSP RecognizeFrame( | |||
59 | lldb::StackFrameSP frame) { | |||
60 | return lldb::RecognizedStackFrameSP(); | |||
61 | }; | |||
62 | virtual std::string GetName() { | |||
63 | return ""; | |||
64 | } | |||
65 | ||||
66 | virtual ~StackFrameRecognizer() = default; | |||
67 | }; | |||
68 | ||||
69 | /// \class ScriptedStackFrameRecognizer | |||
70 | /// | |||
71 | /// Python implementation for frame recognizers. An instance of this class | |||
72 | /// tracks a particular Python classobject, which will be asked to recognize | |||
73 | /// stack frames. | |||
74 | ||||
75 | class ScriptedStackFrameRecognizer : public StackFrameRecognizer { | |||
76 | lldb_private::ScriptInterpreter *m_interpreter; | |||
77 | lldb_private::StructuredData::ObjectSP m_python_object_sp; | |||
78 | std::string m_python_class; | |||
79 | ||||
80 | public: | |||
81 | ScriptedStackFrameRecognizer(lldb_private::ScriptInterpreter *interpreter, | |||
82 | const char *pclass); | |||
83 | ~ScriptedStackFrameRecognizer() override = default; | |||
84 | ||||
85 | std::string GetName() override { | |||
86 | return GetPythonClassName(); | |||
87 | } | |||
88 | ||||
89 | const char *GetPythonClassName() { return m_python_class.c_str(); } | |||
90 | ||||
91 | lldb::RecognizedStackFrameSP RecognizeFrame( | |||
92 | lldb::StackFrameSP frame) override; | |||
93 | ||||
94 | private: | |||
95 | ScriptedStackFrameRecognizer(const ScriptedStackFrameRecognizer &) = delete; | |||
96 | const ScriptedStackFrameRecognizer & | |||
97 | operator=(const ScriptedStackFrameRecognizer &) = delete; | |||
98 | }; | |||
99 | ||||
100 | /// Class that provides a registry of known stack frame recognizers. | |||
101 | class StackFrameRecognizerManager { | |||
102 | public: | |||
103 | void AddRecognizer(lldb::StackFrameRecognizerSP recognizer, | |||
104 | ConstString module, llvm::ArrayRef<ConstString> symbols, | |||
105 | bool first_instruction_only = true); | |||
106 | ||||
107 | void AddRecognizer(lldb::StackFrameRecognizerSP recognizer, | |||
108 | lldb::RegularExpressionSP module, | |||
109 | lldb::RegularExpressionSP symbol, | |||
110 | bool first_instruction_only = true); | |||
111 | ||||
112 | void ForEach(std::function< | |||
113 | void(uint32_t recognizer_id, std::string recognizer_name, | |||
114 | std::string module, llvm::ArrayRef<ConstString> symbols, | |||
115 | bool regexp)> const &callback); | |||
116 | ||||
117 | bool RemoveRecognizerWithID(uint32_t recognizer_id); | |||
118 | ||||
119 | void RemoveAllRecognizers(); | |||
120 | ||||
121 | lldb::StackFrameRecognizerSP GetRecognizerForFrame(lldb::StackFrameSP frame); | |||
122 | ||||
123 | lldb::RecognizedStackFrameSP RecognizeFrame(lldb::StackFrameSP frame); | |||
124 | ||||
125 | private: | |||
126 | struct RegisteredEntry { | |||
127 | uint32_t recognizer_id; | |||
128 | lldb::StackFrameRecognizerSP recognizer; | |||
129 | bool is_regexp; | |||
130 | ConstString module; | |||
131 | lldb::RegularExpressionSP module_regexp; | |||
132 | std::vector<ConstString> symbols; | |||
133 | lldb::RegularExpressionSP symbol_regexp; | |||
134 | bool first_instruction_only; | |||
135 | }; | |||
136 | ||||
137 | std::deque<RegisteredEntry> m_recognizers; | |||
138 | }; | |||
139 | ||||
140 | /// \class ValueObjectRecognizerSynthesizedValue | |||
141 | /// | |||
142 | /// ValueObject subclass that presents the passed ValueObject as a recognized | |||
143 | /// value with the specified ValueType. Frame recognizers should return | |||
144 | /// instances of this class as the returned objects in GetRecognizedArguments(). | |||
145 | ||||
146 | class ValueObjectRecognizerSynthesizedValue : public ValueObject { | |||
147 | public: | |||
148 | static lldb::ValueObjectSP Create(ValueObject &parent, lldb::ValueType type) { | |||
149 | return (new ValueObjectRecognizerSynthesizedValue(parent, type))->GetSP(); | |||
| ||||
150 | } | |||
151 | ValueObjectRecognizerSynthesizedValue(ValueObject &parent, | |||
152 | lldb::ValueType type) | |||
153 | : ValueObject(parent), m_type(type) { | |||
154 | SetName(parent.GetName()); | |||
155 | } | |||
156 | ||||
157 | llvm::Optional<uint64_t> GetByteSize() override { | |||
158 | return m_parent->GetByteSize(); | |||
159 | } | |||
160 | lldb::ValueType GetValueType() const override { return m_type; } | |||
161 | bool UpdateValue() override { | |||
162 | if (!m_parent->UpdateValueIfNeeded()) return false; | |||
163 | m_value = m_parent->GetValue(); | |||
164 | return true; | |||
165 | } | |||
166 | size_t CalculateNumChildren(uint32_t max = UINT32_MAX(4294967295U)) override { | |||
167 | return m_parent->GetNumChildren(max); | |||
168 | } | |||
169 | CompilerType GetCompilerTypeImpl() override { | |||
170 | return m_parent->GetCompilerType(); | |||
171 | } | |||
172 | bool IsSynthetic() override { return true; } | |||
173 | ||||
174 | private: | |||
175 | lldb::ValueType m_type; | |||
176 | }; | |||
177 | ||||
178 | } // namespace lldb_private | |||
179 | ||||
180 | #endif // LLDB_TARGET_STACKFRAMERECOGNIZER_H |