1 //===-- AppleObjCClassDescriptorV2.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_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCCLASSDESCRIPTORV2_H 10 #define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCCLASSDESCRIPTORV2_H 11 12 #include <mutex> 13 14 #include "AppleObjCRuntimeV2.h" 15 #include "lldb/lldb-enumerations.h" 16 #include "lldb/lldb-private.h" 17 18 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" 19 20 namespace lldb_private { 21 22 class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor { 23 public: 24 friend class lldb_private::AppleObjCRuntimeV2; 25 26 ~ClassDescriptorV2() override = default; 27 28 ConstString GetClassName() override; 29 30 ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override; 31 32 ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override; 33 34 bool IsValid() override { 35 return true; // any Objective-C v2 runtime class descriptor we vend is valid 36 } 37 38 lldb::LanguageType GetImplementationLanguage() const override; 39 40 // a custom descriptor is used for tagged pointers 41 bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr, 42 uint64_t *value_bits = nullptr, 43 uint64_t *payload = nullptr) override { 44 return false; 45 } 46 47 bool GetTaggedPointerInfoSigned(uint64_t *info_bits = nullptr, 48 int64_t *value_bits = nullptr, 49 uint64_t *payload = nullptr) override { 50 return false; 51 } 52 53 uint64_t GetInstanceSize() override; 54 55 ObjCLanguageRuntime::ObjCISA GetISA() override { return m_objc_class_ptr; } 56 57 bool Describe( 58 std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func, 59 std::function<bool(const char *, const char *)> const 60 &instance_method_func, 61 std::function<bool(const char *, const char *)> const &class_method_func, 62 std::function<bool(const char *, const char *, lldb::addr_t, 63 uint64_t)> const &ivar_func) const override; 64 65 size_t GetNumIVars() override { 66 GetIVarInformation(); 67 return m_ivars_storage.size(); 68 } 69 70 iVarDescriptor GetIVarAtIndex(size_t idx) override { 71 if (idx >= GetNumIVars()) 72 return iVarDescriptor(); 73 return m_ivars_storage[idx]; 74 } 75 76 protected: 77 void GetIVarInformation(); 78 79 private: 80 static const uint32_t RW_REALIZED = (1 << 31); 81 82 struct objc_class_t { 83 ObjCLanguageRuntime::ObjCISA m_isa = 0; // The class's metaclass. 84 ObjCLanguageRuntime::ObjCISA m_superclass = 0; 85 lldb::addr_t m_cache_ptr = 0; 86 lldb::addr_t m_vtable_ptr = 0; 87 lldb::addr_t m_data_ptr = 0; 88 uint8_t m_flags = 0; 89 90 objc_class_t() = default; 91 92 void Clear() { 93 m_isa = 0; 94 m_superclass = 0; 95 m_cache_ptr = 0; 96 m_vtable_ptr = 0; 97 m_data_ptr = 0; 98 m_flags = 0; 99 } 100 101 bool Read(Process *process, lldb::addr_t addr); 102 }; 103 104 struct class_ro_t { 105 uint32_t m_flags; 106 uint32_t m_instanceStart; 107 uint32_t m_instanceSize; 108 uint32_t m_reserved; 109 110 lldb::addr_t m_ivarLayout_ptr; 111 lldb::addr_t m_name_ptr; 112 lldb::addr_t m_baseMethods_ptr; 113 lldb::addr_t m_baseProtocols_ptr; 114 lldb::addr_t m_ivars_ptr; 115 116 lldb::addr_t m_weakIvarLayout_ptr; 117 lldb::addr_t m_baseProperties_ptr; 118 119 std::string m_name; 120 121 bool Read(Process *process, lldb::addr_t addr); 122 }; 123 124 struct class_rw_t { 125 uint32_t m_flags; 126 uint32_t m_version; 127 128 lldb::addr_t m_ro_ptr; 129 union { 130 lldb::addr_t m_method_list_ptr; 131 lldb::addr_t m_method_lists_ptr; 132 }; 133 lldb::addr_t m_properties_ptr; 134 lldb::addr_t m_protocols_ptr; 135 136 ObjCLanguageRuntime::ObjCISA m_firstSubclass; 137 ObjCLanguageRuntime::ObjCISA m_nextSiblingClass; 138 139 bool Read(Process *process, lldb::addr_t addr); 140 }; 141 142 struct method_list_t { 143 uint16_t m_entsize; 144 bool m_is_small; 145 bool m_has_direct_selector; 146 uint32_t m_count; 147 lldb::addr_t m_first_ptr; 148 149 bool Read(Process *process, lldb::addr_t addr); 150 }; 151 152 std::optional<method_list_t> 153 GetMethodList(Process *process, lldb::addr_t method_list_ptr) const; 154 155 struct method_t { 156 lldb::addr_t m_name_ptr; 157 lldb::addr_t m_types_ptr; 158 lldb::addr_t m_imp_ptr; 159 160 std::string m_name; 161 std::string m_types; 162 163 static size_t GetSize(Process *process, bool is_small) { 164 size_t field_size; 165 if (is_small) 166 field_size = 4; // uint32_t relative indirect fields 167 else 168 field_size = process->GetAddressByteSize(); 169 170 return field_size // SEL name; 171 + field_size // const char *types; 172 + field_size; // IMP imp; 173 } 174 175 bool Read(Process *process, lldb::addr_t addr, 176 lldb::addr_t relative_method_lists_base_addr, bool, bool); 177 }; 178 179 struct ivar_list_t { 180 uint32_t m_entsize; 181 uint32_t m_count; 182 lldb::addr_t m_first_ptr; 183 184 bool Read(Process *process, lldb::addr_t addr); 185 }; 186 187 struct ivar_t { 188 lldb::addr_t m_offset_ptr; 189 lldb::addr_t m_name_ptr; 190 lldb::addr_t m_type_ptr; 191 uint32_t m_alignment; 192 uint32_t m_size; 193 194 std::string m_name; 195 std::string m_type; 196 197 static size_t GetSize(Process *process) { 198 size_t ptr_size = process->GetAddressByteSize(); 199 200 return ptr_size // uintptr_t *offset; 201 + ptr_size // const char *name; 202 + ptr_size // const char *type; 203 + sizeof(uint32_t) // uint32_t alignment; 204 + sizeof(uint32_t); // uint32_t size; 205 } 206 207 bool Read(Process *process, lldb::addr_t addr); 208 }; 209 210 struct relative_list_entry_t { 211 uint16_t m_image_index; 212 int64_t m_list_offset; 213 214 bool Read(Process *process, lldb::addr_t addr); 215 }; 216 217 struct relative_list_list_t { 218 uint32_t m_entsize; 219 uint32_t m_count; 220 lldb::addr_t m_first_ptr; 221 222 bool Read(Process *process, lldb::addr_t addr); 223 }; 224 225 class iVarsStorage { 226 public: 227 iVarsStorage(); 228 229 size_t size(); 230 231 iVarDescriptor &operator[](size_t idx); 232 233 void fill(AppleObjCRuntimeV2 &runtime, ClassDescriptorV2 &descriptor); 234 235 private: 236 bool m_filled = false; 237 std::vector<iVarDescriptor> m_ivars; 238 std::recursive_mutex m_mutex; 239 }; 240 241 // The constructor should only be invoked by the runtime as it builds its 242 // caches 243 // or populates them. A ClassDescriptorV2 should only ever exist in a cache. 244 ClassDescriptorV2(AppleObjCRuntimeV2 &runtime, 245 ObjCLanguageRuntime::ObjCISA isa, const char *name) 246 : m_runtime(runtime), m_objc_class_ptr(isa), m_name(name), 247 m_ivars_storage(), m_image_to_method_lists(), m_last_version_updated() { 248 } 249 250 bool Read_objc_class(Process *process, 251 std::unique_ptr<objc_class_t> &objc_class) const; 252 253 bool Read_class_row(Process *process, const objc_class_t &objc_class, 254 std::unique_ptr<class_ro_t> &class_ro, 255 std::unique_ptr<class_rw_t> &class_rw) const; 256 257 bool ProcessMethodList(std::function<bool(const char *, const char *)> const 258 &instance_method_func, 259 method_list_t &method_list) const; 260 261 bool ProcessRelativeMethodLists( 262 std::function<bool(const char *, const char *)> const 263 &instance_method_func, 264 lldb::addr_t relative_method_list_ptr) const; 265 266 AppleObjCRuntimeV2 267 &m_runtime; // The runtime, so we can read information lazily. 268 lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t. (I.e., 269 // objects of this class type have this as 270 // their ISA) 271 ConstString m_name; // May be NULL 272 iVarsStorage m_ivars_storage; 273 274 mutable std::map<uint16_t, std::vector<method_list_t>> 275 m_image_to_method_lists; 276 mutable std::optional<uint64_t> m_last_version_updated; 277 }; 278 279 // tagged pointer descriptor 280 class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor { 281 public: 282 ClassDescriptorV2Tagged(ConstString class_name, uint64_t payload) { 283 m_name = class_name; 284 if (!m_name) { 285 m_valid = false; 286 return; 287 } 288 m_valid = true; 289 m_payload = payload; 290 m_info_bits = (m_payload & 0xF0ULL) >> 4; 291 m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8; 292 } 293 294 ClassDescriptorV2Tagged( 295 ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp, 296 uint64_t u_payload, int64_t s_payload) { 297 if (!actual_class_sp) { 298 m_valid = false; 299 return; 300 } 301 m_name = actual_class_sp->GetClassName(); 302 if (!m_name) { 303 m_valid = false; 304 return; 305 } 306 m_valid = true; 307 m_payload = u_payload; 308 m_info_bits = (m_payload & 0x0FULL); 309 m_value_bits = (m_payload & ~0x0FULL) >> 4; 310 m_value_bits_signed = (s_payload & ~0x0FLL) >> 4; 311 } 312 313 ~ClassDescriptorV2Tagged() override = default; 314 315 ConstString GetClassName() override { return m_name; } 316 317 ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override { 318 // tagged pointers can represent a class that has a superclass, but since 319 // that information is not 320 // stored in the object itself, we would have to query the runtime to 321 // discover the hierarchy 322 // for the time being, we skip this step in the interest of static discovery 323 return ObjCLanguageRuntime::ClassDescriptorSP(); 324 } 325 326 ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override { 327 return ObjCLanguageRuntime::ClassDescriptorSP(); 328 } 329 330 bool IsValid() override { return m_valid; } 331 332 bool IsKVO() override { 333 return false; // tagged pointers are not KVO'ed 334 } 335 336 bool IsCFType() override { 337 return false; // tagged pointers are not CF objects 338 } 339 340 bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr, 341 uint64_t *value_bits = nullptr, 342 uint64_t *payload = nullptr) override { 343 if (info_bits) 344 *info_bits = GetInfoBits(); 345 if (value_bits) 346 *value_bits = GetValueBits(); 347 if (payload) 348 *payload = GetPayload(); 349 return true; 350 } 351 352 bool GetTaggedPointerInfoSigned(uint64_t *info_bits = nullptr, 353 int64_t *value_bits = nullptr, 354 uint64_t *payload = nullptr) override { 355 if (info_bits) 356 *info_bits = GetInfoBits(); 357 if (value_bits) 358 *value_bits = GetValueBitsSigned(); 359 if (payload) 360 *payload = GetPayload(); 361 return true; 362 } 363 364 uint64_t GetInstanceSize() override { 365 return (IsValid() ? m_pointer_size : 0); 366 } 367 368 ObjCLanguageRuntime::ObjCISA GetISA() override { 369 return 0; // tagged pointers have no ISA 370 } 371 372 // these calls are not part of any formal tagged pointers specification 373 virtual uint64_t GetValueBits() { return (IsValid() ? m_value_bits : 0); } 374 375 virtual int64_t GetValueBitsSigned() { 376 return (IsValid() ? m_value_bits_signed : 0); 377 } 378 379 virtual uint64_t GetInfoBits() { return (IsValid() ? m_info_bits : 0); } 380 381 virtual uint64_t GetPayload() { return (IsValid() ? m_payload : 0); } 382 383 private: 384 ConstString m_name; 385 uint8_t m_pointer_size = 0; 386 bool m_valid = false; 387 uint64_t m_info_bits = 0; 388 uint64_t m_value_bits = 0; 389 int64_t m_value_bits_signed = 0; 390 uint64_t m_payload = 0; 391 }; 392 393 } // namespace lldb_private 394 395 #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCCLASSDESCRIPTORV2_H 396