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