ObjCLanguageRuntime::ClassDescriptorSP ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) { ClassDescriptorSP objc_class_sp; // if we get an invalid VO (which might still happen when playing around // with pointers returned by the expression parser, don't consider this // a valid ObjC object) if (valobj.GetCompilerType().IsValid()) { addr_t isa_pointer = valobj.GetPointerValue(); if (isa_pointer != LLDB_INVALID_ADDRESS) { ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) { Error error; ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error); if (isa != LLDB_INVALID_ADDRESS) objc_class_sp = GetClassDescriptorFromISA(isa); } } } return objc_class_sp; }
lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer () { if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) { ModuleSP objc_module_sp(GetObjCModule()); if (!objc_module_sp) return LLDB_INVALID_ADDRESS; static ConstString g_objc_debug_class_hash("_objc_debug_class_hash"); const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData); if (symbol && symbol->ValueIsAddress()) { Process *process = GetProcess(); if (process) { lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) { Error error; lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error); if (objc_debug_class_hash_ptr != 0 && objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) { m_isa_hash_table_ptr = objc_debug_class_hash_ptr; } } } } } return m_isa_hash_table_ptr; }
bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style, uint32_t addr_size) const { // If the section was nullptr, only load address is going to work unless we // are // trying to deref a pointer SectionSP section_sp(GetSection()); if (!section_sp && style != DumpStyleResolvedPointerDescription) style = DumpStyleLoadAddress; ExecutionContext exe_ctx(exe_scope); Target *target = exe_ctx.GetTargetPtr(); // If addr_byte_size is UINT32_MAX, then determine the correct address // byte size for the process or default to the size of addr_t if (addr_size == UINT32_MAX) { if (target) addr_size = target->GetArchitecture().GetAddressByteSize(); else addr_size = sizeof(addr_t); } Address so_addr; switch (style) { case DumpStyleInvalid: return false; case DumpStyleSectionNameOffset: if (section_sp) { section_sp->DumpName(s); s->Printf(" + %" PRIu64, m_offset); } else { s->Address(m_offset, addr_size); } break; case DumpStyleSectionPointerOffset: s->Printf("(Section *)%p + ", static_cast<void *>(section_sp.get())); s->Address(m_offset, addr_size); break; case DumpStyleModuleWithFileAddress: if (section_sp) { ModuleSP module_sp = section_sp->GetModule(); if (module_sp) s->Printf("%s[", module_sp->GetFileSpec().GetFilename().AsCString( "<Unknown>")); else s->Printf("%s[", "<Unknown>"); } LLVM_FALLTHROUGH; case DumpStyleFileAddress: { addr_t file_addr = GetFileAddress(); if (file_addr == LLDB_INVALID_ADDRESS) { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } s->Address(file_addr, addr_size); if (style == DumpStyleModuleWithFileAddress && section_sp) s->PutChar(']'); } break; case DumpStyleLoadAddress: { addr_t load_addr = GetLoadAddress(target); /* * MIPS: * Display address in compressed form for MIPS16 or microMIPS * if the address belongs to eAddressClassCodeAlternateISA. */ if (target) { const llvm::Triple::ArchType llvm_arch = target->GetArchitecture().GetMachine(); if (llvm_arch == llvm::Triple::mips || llvm_arch == llvm::Triple::mipsel || llvm_arch == llvm::Triple::mips64 || llvm_arch == llvm::Triple::mips64el) load_addr = GetCallableLoadAddress(target); } if (load_addr == LLDB_INVALID_ADDRESS) { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } s->Address(load_addr, addr_size); } break; case DumpStyleResolvedDescription: case DumpStyleResolvedDescriptionNoModule: case DumpStyleResolvedDescriptionNoFunctionArguments: case DumpStyleNoFunctionName: if (IsSectionOffset()) { uint32_t pointer_size = 4; ModuleSP module_sp(GetModule()); if (target) pointer_size = target->GetArchitecture().GetAddressByteSize(); else if (module_sp) pointer_size = module_sp->GetArchitecture().GetAddressByteSize(); bool showed_info = false; if (section_sp) { SectionType sect_type = section_sp->GetType(); switch (sect_type) { case eSectionTypeData: if (module_sp) { SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { const addr_t file_Addr = GetFileAddress(); Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_Addr); if (symbol) { const char *symbol_name = symbol->GetName().AsCString(); if (symbol_name) { s->PutCString(symbol_name); addr_t delta = file_Addr - symbol->GetAddressRef().GetFileAddress(); if (delta) s->Printf(" + %" PRIu64, delta); showed_info = true; } } } } } break; case eSectionTypeDataCString: // Read the C string from memory and display it showed_info = true; ReadCStringFromMemory(exe_scope, *this, s); break; case eSectionTypeDataCStringPointers: if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) { #if VERBOSE_OUTPUT s->PutCString("(char *)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); s->PutCString(": "); #endif showed_info = true; ReadCStringFromMemory(exe_scope, so_addr, s); } break; case eSectionTypeDataObjCMessageRefs: if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) { if (target && so_addr.IsSectionOffset()) { SymbolContext func_sc; target->GetImages().ResolveSymbolContextForAddress( so_addr, eSymbolContextEverything, func_sc); if (func_sc.function != nullptr || func_sc.symbol != nullptr) { showed_info = true; #if VERBOSE_OUTPUT s->PutCString("(objc_msgref *) -> { (func*)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); #else s->PutCString("{ "); #endif Address cstr_addr(*this); cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false, true, true); if (ReadAddress(exe_scope, cstr_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT s->PutCString("), (char *)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); s->PutCString(" ("); #else s->PutCString(", "); #endif ReadCStringFromMemory(exe_scope, so_addr, s); } #if VERBOSE_OUTPUT s->PutCString(") }"); #else s->PutCString(" }"); #endif } } } break; case eSectionTypeDataObjCCFStrings: { Address cfstring_data_addr(*this); cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() + (2 * pointer_size)); if (ReadAddress(exe_scope, cfstring_data_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT s->PutCString("(CFString *) "); cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); s->PutCString(" -> @"); #else s->PutChar('@'); #endif if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription)) showed_info = true; } } break; case eSectionTypeData4: // Read the 4 byte data and display it showed_info = true; s->PutCString("(uint32_t) "); DumpUInt(exe_scope, *this, 4, s); break; case eSectionTypeData8: // Read the 8 byte data and display it showed_info = true; s->PutCString("(uint64_t) "); DumpUInt(exe_scope, *this, 8, s); break; case eSectionTypeData16: // Read the 16 byte data and display it showed_info = true; s->PutCString("(uint128_t) "); DumpUInt(exe_scope, *this, 16, s); break; case eSectionTypeDataPointers: // Read the pointer data and display it if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) { s->PutCString("(void *)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); showed_info = true; if (so_addr.IsSectionOffset()) { SymbolContext pointer_sc; if (target) { target->GetImages().ResolveSymbolContextForAddress( so_addr, eSymbolContextEverything, pointer_sc); if (pointer_sc.function != nullptr || pointer_sc.symbol != nullptr) { s->PutCString(": "); pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false, true, true); } } } } break; default: break; } } if (!showed_info) { if (module_sp) { SymbolContext sc; module_sp->ResolveSymbolContextForAddress( *this, eSymbolContextEverything, sc); if (sc.function || sc.symbol) { bool show_stop_context = true; const bool show_module = (style == DumpStyleResolvedDescription); const bool show_fullpaths = false; const bool show_inlined_frames = true; const bool show_function_arguments = (style != DumpStyleResolvedDescriptionNoFunctionArguments); const bool show_function_name = (style != DumpStyleNoFunctionName); if (sc.function == nullptr && sc.symbol != nullptr) { // If we have just a symbol make sure it is in the right section if (sc.symbol->ValueIsAddress()) { if (sc.symbol->GetAddressRef().GetSection() != GetSection()) { // don't show the module if the symbol is a trampoline symbol show_stop_context = false; } } } if (show_stop_context) { // We have a function or a symbol from the same // sections as this address. sc.DumpStopContext(s, exe_scope, *this, show_fullpaths, show_module, show_inlined_frames, show_function_arguments, show_function_name); } else { // We found a symbol but it was in a different // section so it isn't the symbol we should be // showing, just show the section name + offset Dump(s, exe_scope, DumpStyleSectionNameOffset); } } } } } else { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } break; case DumpStyleDetailedSymbolContext: if (IsSectionOffset()) { ModuleSP module_sp(GetModule()); if (module_sp) { SymbolContext sc; module_sp->ResolveSymbolContextForAddress( *this, eSymbolContextEverything | eSymbolContextVariable, sc); if (sc.symbol) { // If we have just a symbol make sure it is in the same section // as our address. If it isn't, then we might have just found // the last symbol that came before the address that we are // looking up that has nothing to do with our address lookup. if (sc.symbol->ValueIsAddress() && sc.symbol->GetAddressRef().GetSection() != GetSection()) sc.symbol = nullptr; } sc.GetDescription(s, eDescriptionLevelBrief, target); if (sc.block) { bool can_create = true; bool get_parent_variables = true; bool stop_if_block_is_inlined_function = false; VariableList variable_list; sc.block->AppendVariables(can_create, get_parent_variables, stop_if_block_is_inlined_function, [](Variable *) { return true; }, &variable_list); const size_t num_variables = variable_list.GetSize(); for (size_t var_idx = 0; var_idx < num_variables; ++var_idx) { Variable *var = variable_list.GetVariableAtIndex(var_idx).get(); if (var && var->LocationIsValidForAddress(*this)) { s->Indent(); s->Printf(" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\"", var->GetID(), var->GetName().GetCString()); Type *type = var->GetType(); if (type) s->Printf(", type = \"%s\"", type->GetName().GetCString()); else s->PutCString(", type = <unknown>"); s->PutCString(", location = "); var->DumpLocationForAddress(s, *this); s->PutCString(", decl = "); var->GetDeclaration().DumpStopContext(s, false); s->EOL(); } } } } } else { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } break; case DumpStyleResolvedPointerDescription: { Process *process = exe_ctx.GetProcessPtr(); if (process) { addr_t load_addr = GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { Error memory_error; addr_t dereferenced_load_addr = process->ReadPointerFromMemory(load_addr, memory_error); if (dereferenced_load_addr != LLDB_INVALID_ADDRESS) { Address dereferenced_addr; if (dereferenced_addr.SetLoadAddress(dereferenced_load_addr, target)) { StreamString strm; if (dereferenced_addr.Dump(&strm, exe_scope, DumpStyleResolvedDescription, DumpStyleInvalid, addr_size)) { s->Address(dereferenced_load_addr, addr_size, " -> ", " "); s->Write(strm.GetData(), strm.GetSize()); return true; } } } } } if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } break; } return true; }
bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &dynamic_address, Value::ValueType &value_type) { // For Itanium, if the type has a vtable pointer in the object, it will be at // offset 0 // in the object. That will point to the "address point" within the vtable // (not the beginning of the // vtable.) We can then look up the symbol containing this "address point" // and that symbol's name // demangled will contain the full class name. // The second pointer above the "address point" is the "offset_to_top". We'll // use that to get the // start of the value object which holds the dynamic type. // class_type_or_name.Clear(); value_type = Value::ValueType::eValueTypeScalar; // Only a pointer or reference type can have a different dynamic and static // type: if (CouldHaveDynamicValue(in_value)) { // First job, pull out the address at 0 offset from the object. AddressType address_type; lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type); if (original_ptr == LLDB_INVALID_ADDRESS) return false; ExecutionContext exe_ctx(in_value.GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process == nullptr) return false; Error error; const lldb::addr_t vtable_address_point = process->ReadPointerFromMemory(original_ptr, error); if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS) { return false; } class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr, vtable_address_point); if (class_type_or_name) { TypeSP type_sp = class_type_or_name.GetTypeSP(); // There can only be one type with a given name, // so we've just found duplicate definitions, and this // one will do as well as any other. // We don't consider something to have a dynamic type if // it is the same as the static type. So compare against // the value we were handed. if (type_sp) { if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(), type_sp->GetForwardCompilerType())) { // The dynamic type we found was the same type, // so we don't have a dynamic type here... return false; } // The offset_to_top is two pointers above the vtable pointer. const uint32_t addr_byte_size = process->GetAddressByteSize(); const lldb::addr_t offset_to_top_location = vtable_address_point - 2 * addr_byte_size; // Watch for underflow, offset_to_top_location should be less than // vtable_address_point if (offset_to_top_location >= vtable_address_point) return false; const int64_t offset_to_top = process->ReadSignedIntegerFromMemory( offset_to_top_location, addr_byte_size, INT64_MIN, error); if (offset_to_top == INT64_MIN) return false; // So the dynamic type is a value that starts at offset_to_top // above the original address. lldb::addr_t dynamic_addr = original_ptr + offset_to_top; if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress( dynamic_addr, dynamic_address)) { dynamic_address.SetRawAddress(dynamic_addr); } return true; } } } return class_type_or_name.IsEmpty() == false; }
CPPLanguageRuntime::LibCppStdFunctionCallableInfo CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( lldb::ValueObjectSP &valobj_sp) { LibCppStdFunctionCallableInfo optional_info; if (!valobj_sp) return optional_info; // Member __f_ has type __base*, the contents of which will hold: // 1) a vtable entry which may hold type information needed to discover the // lambda being called // 2) possibly hold a pointer to the callable object // e.g. // // (lldb) frame var -R f_display // (std::__1::function<void (int)>) f_display = { // __buf_ = { // … // } // __f_ = 0x00007ffeefbffa00 // } // (lldb) memory read -fA 0x00007ffeefbffa00 // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ... // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ... // // We will be handling five cases below, std::function is wrapping: // // 1) a lambda we know at compile time. We will obtain the name of the lambda // from the first template pameter from __func's vtable. We will look up // the lambda's operator()() and obtain the line table entry. // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method // will be stored after the vtable. We will obtain the lambdas name from // this entry and lookup operator()() and obtain the line table entry. // 3) a callable object via operator()(). We will obtain the name of the // object from the first template parameter from __func's vtable. We will // look up the objectc operator()() and obtain the line table entry. // 4) a member function. A pointer to the function will stored after the // we will obtain the name from this pointer. // 5) a free function. A pointer to the function will stored after the vtable // we will obtain the name from this pointer. ValueObjectSP member__f_( valobj_sp->GetChildMemberWithName(ConstString("__f_"), true)); if (member__f_) { ValueObjectSP sub_member__f_( member__f_->GetChildMemberWithName(ConstString("__f_"), true)); if (sub_member__f_) member__f_ = sub_member__f_; } lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0); optional_info.member__f_pointer_value = member__f_pointer_value; ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process == nullptr) return optional_info; uint32_t address_size = process->GetAddressByteSize(); Status status; // First item pointed to by __f_ should be the pointer to the vtable for // a __base object. lldb::addr_t vtable_address = process->ReadPointerFromMemory(member__f_pointer_value, status); if (status.Fail()) return optional_info; lldb::addr_t address_after_vtable = member__f_pointer_value + address_size; // As commened above we may not have a function pointer but if we do we will // need it. lldb::addr_t possible_function_address = process->ReadPointerFromMemory(address_after_vtable, status); if (status.Fail()) return optional_info; Target &target = process->GetTarget(); if (target.GetSectionLoadList().IsEmpty()) return optional_info; Address vtable_addr_resolved; SymbolContext sc; Symbol *symbol; if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address, vtable_addr_resolved)) return optional_info; target.GetImages().ResolveSymbolContextForAddress( vtable_addr_resolved, eSymbolContextEverything, sc); symbol = sc.symbol; if (symbol == nullptr) return optional_info; llvm::StringRef vtable_name(symbol->GetName().GetCString()); bool found_expected_start_string = vtable_name.startswith("vtable for std::__1::__function::__func<"); if (!found_expected_start_string) return optional_info; // Given case 1 or 3 we have a vtable name, we are want to extract the first // template parameter // // ... __func<main::$_0, std::__1::allocator<main::$_0> ... // ^^^^^^^^^ // // We do this by find the first < and , and extracting in between. // // This covers the case of the lambda known at compile time. size_t first_open_angle_bracket = vtable_name.find('<') + 1; size_t first_comma = vtable_name.find(','); llvm::StringRef first_template_parameter = vtable_name.slice(first_open_angle_bracket, first_comma); Address function_address_resolved; // Setup for cases 2, 4 and 5 we have a pointer to a function after the // vtable. We will use a process of elimination to drop through each case // and obtain the data we need. if (target.GetSectionLoadList().ResolveLoadAddress( possible_function_address, function_address_resolved)) { target.GetImages().ResolveSymbolContextForAddress( function_address_resolved, eSymbolContextEverything, sc); symbol = sc.symbol; } auto get_name = [&first_template_parameter, &symbol]() { // Given case 1: // // main::$_0 // // we want to append ::operator()() if (first_template_parameter.contains("$_")) return llvm::Regex::escape(first_template_parameter.str()) + R"(::operator\(\)\(.*\))"; if (symbol != NULL && symbol->GetName().GetStringRef().contains("__invoke")) { llvm::StringRef symbol_name = symbol->GetName().GetStringRef(); size_t pos2 = symbol_name.find_last_of(':'); // Given case 2: // // main::$_1::__invoke(...) // // We want to slice off __invoke(...) and append operator()() std::string lambda_operator = llvm::Regex::escape(symbol_name.slice(0, pos2 + 1).str()) + R"(operator\(\)\(.*\))"; return lambda_operator; } // Case 3 return first_template_parameter.str() + R"(::operator\(\)\(.*\))"; ; }; std::string func_to_match = get_name(); SymbolContextList scl; target.GetImages().FindFunctions(RegularExpression{func_to_match}, true, true, true, scl); // Case 1,2 or 3 if (scl.GetSize() >= 1) { SymbolContext sc2 = scl[0]; AddressRange range; sc2.GetAddressRange(eSymbolContextEverything, 0, false, range); Address address = range.GetBaseAddress(); Address addr; if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target), addr)) { LineEntry line_entry; addr.CalculateSymbolContextLineEntry(line_entry); if (first_template_parameter.contains("$_") || (symbol != nullptr && symbol->GetName().GetStringRef().contains("__invoke"))) { // Case 1 and 2 optional_info.callable_case = LibCppStdFunctionCallableCase::Lambda; } else { // Case 3 optional_info.callable_case = LibCppStdFunctionCallableCase::CallableObject; } optional_info.callable_symbol = *symbol; optional_info.callable_line_entry = line_entry; optional_info.callable_address = addr; return optional_info; }