AppleObjCRuntimeV1.cpp revision 3e11c7ec050648ba865f1d451f8cb46fd39072a8
1//===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "AppleObjCRuntimeV1.h" 11#include "AppleObjCTrampolineHandler.h" 12#include "AppleObjCTypeVendor.h" 13 14#include "llvm/Support/MachO.h" 15#include "clang/AST/Type.h" 16 17#include "lldb/Breakpoint/BreakpointLocation.h" 18#include "lldb/Core/ConstString.h" 19#include "lldb/Core/Error.h" 20#include "lldb/Core/Log.h" 21#include "lldb/Core/Module.h" 22#include "lldb/Core/PluginManager.h" 23#include "lldb/Core/Scalar.h" 24#include "lldb/Core/StreamString.h" 25#include "lldb/Expression/ClangFunction.h" 26#include "lldb/Expression/ClangUtilityFunction.h" 27#include "lldb/Symbol/ClangASTContext.h" 28#include "lldb/Symbol/Symbol.h" 29#include "lldb/Target/ExecutionContext.h" 30#include "lldb/Target/Process.h" 31#include "lldb/Target/RegisterContext.h" 32#include "lldb/Target/Target.h" 33#include "lldb/Target/Thread.h" 34 35#include <vector> 36 37using namespace lldb; 38using namespace lldb_private; 39 40AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process) : 41 AppleObjCRuntime (process), 42 m_hash_signature (), 43 m_isa_hash_table_ptr (LLDB_INVALID_ADDRESS) 44{ 45} 46 47// for V1 runtime we just try to return a class name as that is the minimum level of support 48// required for the data formatters to work 49bool 50AppleObjCRuntimeV1::GetDynamicTypeAndAddress (ValueObject &in_value, 51 lldb::DynamicValueType use_dynamic, 52 TypeAndOrName &class_type_or_name, 53 Address &address) 54{ 55 class_type_or_name.Clear(); 56 if (CouldHaveDynamicValue(in_value)) 57 { 58 auto class_descriptor(GetClassDescriptor(in_value)); 59 if (class_descriptor && class_descriptor->IsValid() && class_descriptor->GetClassName()) 60 { 61 const addr_t object_ptr = in_value.GetPointerValue(); 62 address.SetRawAddress(object_ptr); 63 class_type_or_name.SetName(class_descriptor->GetClassName()); 64 } 65 } 66 return class_type_or_name.IsEmpty() == false; 67} 68 69//------------------------------------------------------------------ 70// Static Functions 71//------------------------------------------------------------------ 72lldb_private::LanguageRuntime * 73AppleObjCRuntimeV1::CreateInstance (Process *process, lldb::LanguageType language) 74{ 75 // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make 76 // sure we aren't using the V1 runtime. 77 if (language == eLanguageTypeObjC) 78 { 79 ModuleSP objc_module_sp; 80 81 if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == eAppleObjC_V1) 82 return new AppleObjCRuntimeV1 (process); 83 else 84 return NULL; 85 } 86 else 87 return NULL; 88} 89 90 91void 92AppleObjCRuntimeV1::Initialize() 93{ 94 PluginManager::RegisterPlugin (GetPluginNameStatic(), 95 "Apple Objective C Language Runtime - Version 1", 96 CreateInstance); 97} 98 99void 100AppleObjCRuntimeV1::Terminate() 101{ 102 PluginManager::UnregisterPlugin (CreateInstance); 103} 104 105lldb_private::ConstString 106AppleObjCRuntimeV1::GetPluginNameStatic() 107{ 108 static ConstString g_name("apple-objc-v1"); 109 return g_name; 110} 111 112//------------------------------------------------------------------ 113// PluginInterface protocol 114//------------------------------------------------------------------ 115ConstString 116AppleObjCRuntimeV1::GetPluginName() 117{ 118 return GetPluginNameStatic(); 119} 120 121uint32_t 122AppleObjCRuntimeV1::GetPluginVersion() 123{ 124 return 1; 125} 126 127BreakpointResolverSP 128AppleObjCRuntimeV1::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) 129{ 130 BreakpointResolverSP resolver_sp; 131 132 if (throw_bp) 133 resolver_sp.reset (new BreakpointResolverName (bkpt, 134 "objc_exception_throw", 135 eFunctionNameTypeBase, 136 Breakpoint::Exact, 137 eLazyBoolNo)); 138 // FIXME: don't do catch yet. 139 return resolver_sp; 140} 141 142struct BufStruct { 143 char contents[2048]; 144}; 145 146ClangUtilityFunction * 147AppleObjCRuntimeV1::CreateObjectChecker(const char *name) 148{ 149 std::unique_ptr<BufStruct> buf(new BufStruct); 150 151 assert(snprintf(&buf->contents[0], sizeof(buf->contents), 152 "struct __objc_class \n" 153 "{ \n" 154 " struct __objc_class *isa; \n" 155 " struct __objc_class *super_class; \n" 156 " const char *name; \n" 157 " // rest of struct elided because unused \n" 158 "}; \n" 159 " \n" 160 "struct __objc_object \n" 161 "{ \n" 162 " struct __objc_class *isa; \n" 163 "}; \n" 164 " \n" 165 "extern \"C\" void \n" 166 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" 167 "{ \n" 168 " struct __objc_object *obj = (struct __objc_object*)$__lldb_arg_obj; \n" 169 " (int)strlen(obj->isa->name); \n" 170 "} \n", 171 name) < (int)sizeof(buf->contents)); 172 173 return new ClangUtilityFunction(buf->contents, name); 174} 175 176// this code relies on the assumption that an Objective-C object always starts 177// with an ISA at offset 0. 178//ObjCLanguageRuntime::ObjCISA 179//AppleObjCRuntimeV1::GetISA(ValueObject& valobj) 180//{ 181//// if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != eLanguageTypeObjC) 182//// return 0; 183// 184// // if we get an invalid VO (which might still happen when playing around 185// // with pointers returned by the expression parser, don't consider this 186// // a valid ObjC object) 187// if (valobj.GetValue().GetContextType() == Value::eContextTypeInvalid) 188// return 0; 189// 190// addr_t isa_pointer = valobj.GetPointerValue(); 191// 192// ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); 193// 194// Process *process = exe_ctx.GetProcessPtr(); 195// if (process) 196// { 197// uint8_t pointer_size = process->GetAddressByteSize(); 198// 199// Error error; 200// return process->ReadUnsignedIntegerFromMemory (isa_pointer, 201// pointer_size, 202// 0, 203// error); 204// } 205// return 0; 206//} 207 208AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer) 209{ 210 Initialize (isa_pointer.GetValueAsUnsigned(0), 211 isa_pointer.GetProcessSP()); 212} 213 214AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp) 215{ 216 Initialize (isa, process_sp); 217} 218 219void 220AppleObjCRuntimeV1::ClassDescriptorV1::Initialize (ObjCISA isa, lldb::ProcessSP process_sp) 221{ 222 if (!isa || !process_sp) 223 { 224 m_valid = false; 225 return; 226 } 227 228 m_valid = true; 229 230 Error error; 231 232 m_isa = process_sp->ReadPointerFromMemory(isa, error); 233 234 if (error.Fail()) 235 { 236 m_valid = false; 237 return; 238 } 239 240 uint32_t ptr_size = process_sp->GetAddressByteSize(); 241 242 if (!IsPointerValid(m_isa,ptr_size)) 243 { 244 m_valid = false; 245 return; 246 } 247 248 m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size,error); 249 250 if (error.Fail()) 251 { 252 m_valid = false; 253 return; 254 } 255 256 if (!IsPointerValid(m_parent_isa,ptr_size,true)) 257 { 258 m_valid = false; 259 return; 260 } 261 262 lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size,error); 263 264 if (error.Fail()) 265 { 266 m_valid = false; 267 return; 268 } 269 270 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); 271 272 size_t count = process_sp->ReadCStringFromMemory(name_ptr, (char*)buffer_sp->GetBytes(), 1024, error); 273 274 if (error.Fail()) 275 { 276 m_valid = false; 277 return; 278 } 279 280 if (count) 281 m_name = ConstString((char*)buffer_sp->GetBytes()); 282 else 283 m_name = ConstString(); 284 285 m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(m_isa + 5 * ptr_size, ptr_size, 0, error); 286 287 if (error.Fail()) 288 { 289 m_valid = false; 290 return; 291 } 292 293 m_process_wp = lldb::ProcessWP(process_sp); 294} 295 296AppleObjCRuntime::ClassDescriptorSP 297AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass () 298{ 299 if (!m_valid) 300 return AppleObjCRuntime::ClassDescriptorSP(); 301 ProcessSP process_sp = m_process_wp.lock(); 302 if (!process_sp) 303 return AppleObjCRuntime::ClassDescriptorSP(); 304 return ObjCLanguageRuntime::ClassDescriptorSP(new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa,process_sp)); 305} 306 307bool 308AppleObjCRuntimeV1::ClassDescriptorV1::Describe (std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func, 309 std::function <bool (const char *, const char *)> const &instance_method_func, 310 std::function <bool (const char *, const char *)> const &class_method_func, 311 std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func) 312{ 313 return false; 314} 315 316lldb::addr_t 317AppleObjCRuntimeV1::GetISAHashTablePointer () 318{ 319 if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) 320 { 321 ModuleSP objc_module_sp(GetObjCModule()); 322 323 if (!objc_module_sp) 324 return LLDB_INVALID_ADDRESS; 325 326 static ConstString g_objc_debug_class_hash("_objc_debug_class_hash"); 327 328 const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData); 329 if (symbol) 330 { 331 Process *process = GetProcess(); 332 if (process) 333 { 334 335 lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddress().GetLoadAddress(&process->GetTarget()); 336 337 if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) 338 { 339 Error error; 340 lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error); 341 if (objc_debug_class_hash_ptr != 0 && 342 objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) 343 { 344 m_isa_hash_table_ptr = objc_debug_class_hash_ptr; 345 } 346 } 347 } 348 } 349 } 350 return m_isa_hash_table_ptr; 351} 352 353void 354AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() 355{ 356 // TODO: implement HashTableSignature... 357 Process *process = GetProcess(); 358 359 if (process) 360 { 361 // Update the process stop ID that indicates the last time we updated the 362 // map, wether it was successful or not. 363 m_isa_to_descriptor_stop_id = process->GetStopID(); 364 365 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); 366 367 ProcessSP process_sp = process->shared_from_this(); 368 369 ModuleSP objc_module_sp(GetObjCModule()); 370 371 if (!objc_module_sp) 372 return; 373 374 uint32_t isa_count = 0; 375 376 lldb::addr_t hash_table_ptr = GetISAHashTablePointer (); 377 if (hash_table_ptr != LLDB_INVALID_ADDRESS) 378 { 379 // Read the NXHashTable struct: 380 // 381 // typedef struct { 382 // const NXHashTablePrototype *prototype; 383 // unsigned count; 384 // unsigned nbBuckets; 385 // void *buckets; 386 // const void *info; 387 // } NXHashTable; 388 389 Error error; 390 DataBufferHeap buffer(1024, 0); 391 if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 20) 392 { 393 const uint32_t addr_size = m_process->GetAddressByteSize(); 394 const ByteOrder byte_order = m_process->GetByteOrder(); 395 DataExtractor data (buffer.GetBytes(), buffer.GetByteSize(), byte_order, addr_size); 396 lldb::offset_t offset = addr_size; // Skip prototype 397 const uint32_t count = data.GetU32(&offset); 398 const uint32_t num_buckets = data.GetU32(&offset); 399 const addr_t buckets_ptr = data.GetPointer(&offset); 400 if (m_hash_signature.NeedsUpdate (count, num_buckets, buckets_ptr)) 401 { 402 m_hash_signature.UpdateSignature (count, num_buckets, buckets_ptr); 403 404 const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t); 405 buffer.SetByteSize(data_size); 406 407 if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, error) == data_size) 408 { 409 data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order); 410 offset = 0; 411 for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx) 412 { 413 const uint32_t bucket_isa_count = data.GetU32 (&offset); 414 const lldb::addr_t bucket_data = data.GetU32 (&offset); 415 416 417 if (bucket_isa_count == 0) 418 continue; 419 420 isa_count += bucket_isa_count; 421 422 ObjCISA isa; 423 if (bucket_isa_count == 1) 424 { 425 // When we only have one entry in the bucket, the bucket data is the "isa" 426 isa = bucket_data; 427 if (isa) 428 { 429 if (!ISAIsCached(isa)) 430 { 431 ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp)); 432 433 if (log && log->GetVerbose()) 434 log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa); 435 436 AddClass (isa, descriptor_sp); 437 } 438 } 439 } 440 else 441 { 442 // When we have more than one entry in the bucket, the bucket data is a pointer 443 // to an array of "isa" values 444 addr_t isa_addr = bucket_data; 445 for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count; ++isa_idx, isa_addr += addr_size) 446 { 447 isa = m_process->ReadPointerFromMemory(isa_addr, error); 448 449 if (isa && isa != LLDB_INVALID_ADDRESS) 450 { 451 if (!ISAIsCached(isa)) 452 { 453 ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp)); 454 455 if (log && log->GetVerbose()) 456 log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa); 457 458 AddClass (isa, descriptor_sp); 459 } 460 } 461 } 462 } 463 } 464 } 465 } 466 } 467 } 468 } 469 else 470 { 471 m_isa_to_descriptor_stop_id = UINT32_MAX; 472 } 473} 474 475TypeVendor * 476AppleObjCRuntimeV1::GetTypeVendor() 477{ 478 if (!m_type_vendor_ap.get()) 479 m_type_vendor_ap.reset(new AppleObjCTypeVendor(*this)); 480 481 return m_type_vendor_ap.get(); 482} 483