//===-- SymbolFileCTF.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SymbolFileCTF.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/Config.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Symtab.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/StreamBuffer.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" #include "llvm/Support/MemoryBuffer.h" #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include #include #if LLVM_ENABLE_ZLIB #include #endif using namespace llvm; using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE(SymbolFileCTF) char SymbolFileCTF::ID; SymbolFileCTF::SymbolFileCTF(lldb::ObjectFileSP objfile_sp) : SymbolFileCommon(std::move(objfile_sp)) {} void SymbolFileCTF::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); } void SymbolFileCTF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } llvm::StringRef SymbolFileCTF::GetPluginDescriptionStatic() { return "Compact C Type Format Symbol Reader"; } SymbolFile *SymbolFileCTF::CreateInstance(ObjectFileSP objfile_sp) { return new SymbolFileCTF(std::move(objfile_sp)); } bool SymbolFileCTF::ParseHeader() { if (m_header) return true; Log *log = GetLog(LLDBLog::Symbols); ModuleSP module_sp(m_objfile_sp->GetModule()); const SectionList *section_list = module_sp->GetSectionList(); if (!section_list) return false; SectionSP section_sp( section_list->FindSectionByType(lldb::eSectionTypeCTF, true)); if (!section_sp) return false; m_objfile_sp->ReadSectionData(section_sp.get(), m_data); if (m_data.GetByteSize() == 0) return false; StreamString module_desc; GetObjectFile()->GetModule()->GetDescription(module_desc.AsRawOstream(), lldb::eDescriptionLevelBrief); LLDB_LOG(log, "Parsing Compact C Type format for {0}", module_desc.GetData()); lldb::offset_t offset = 0; // Parse CTF header. constexpr size_t ctf_header_size = sizeof(ctf_header_t); if (!m_data.ValidOffsetForDataOfSize(offset, ctf_header_size)) { LLDB_LOG(log, "CTF parsing failed: insufficient data for CTF header"); return false; } m_header.emplace(); ctf_header_t &ctf_header = *m_header; ctf_header.preamble.magic = m_data.GetU16(&offset); ctf_header.preamble.version = m_data.GetU8(&offset); ctf_header.preamble.flags = m_data.GetU8(&offset); ctf_header.parlabel = m_data.GetU32(&offset); ctf_header.parname = m_data.GetU32(&offset); ctf_header.lbloff = m_data.GetU32(&offset); ctf_header.objtoff = m_data.GetU32(&offset); ctf_header.funcoff = m_data.GetU32(&offset); ctf_header.typeoff = m_data.GetU32(&offset); ctf_header.stroff = m_data.GetU32(&offset); ctf_header.strlen = m_data.GetU32(&offset); // Validate the preamble. if (ctf_header.preamble.magic != g_ctf_magic) { LLDB_LOG(log, "CTF parsing failed: invalid magic: {0:x}", ctf_header.preamble.magic); return false; } if (ctf_header.preamble.version != g_ctf_version) { LLDB_LOG(log, "CTF parsing failed: unsupported version: {0}", ctf_header.preamble.version); return false; } LLDB_LOG(log, "Parsed valid CTF preamble: version {0}, flags {1:x}", ctf_header.preamble.version, ctf_header.preamble.flags); m_body_offset = offset; if (ctf_header.preamble.flags & eFlagCompress) { // The body has been compressed with zlib deflate. Header offsets point into // the decompressed data. #if LLVM_ENABLE_ZLIB const std::size_t decompressed_size = ctf_header.stroff + ctf_header.strlen; DataBufferSP decompressed_data = std::make_shared(decompressed_size, 0x0); z_stream zstr; memset(&zstr, 0, sizeof(zstr)); zstr.next_in = (Bytef *)const_cast(m_data.GetDataStart() + sizeof(ctf_header_t)); zstr.avail_in = m_data.BytesLeft(offset); zstr.next_out = (Bytef *)const_cast(decompressed_data->GetBytes()); zstr.avail_out = decompressed_size; int rc = inflateInit(&zstr); if (rc != Z_OK) { LLDB_LOG(log, "CTF parsing failed: inflate initialization error: {0}", zError(rc)); return false; } rc = inflate(&zstr, Z_FINISH); if (rc != Z_STREAM_END) { LLDB_LOG(log, "CTF parsing failed: inflate error: {0}", zError(rc)); return false; } rc = inflateEnd(&zstr); if (rc != Z_OK) { LLDB_LOG(log, "CTF parsing failed: inflate end error: {0}", zError(rc)); return false; } if (zstr.total_out != decompressed_size) { LLDB_LOG(log, "CTF parsing failed: decompressed size ({0}) doesn't match " "expected size ([1})", zstr.total_out, decompressed_size); return false; } m_data = DataExtractor(decompressed_data, m_data.GetByteOrder(), m_data.GetAddressByteSize()); m_body_offset = 0; #else LLDB_LOG( log, "CTF parsing failed: data is compressed but no zlib inflate support"); return false; #endif } // Validate the header. if (!m_data.ValidOffset(m_body_offset + ctf_header.lbloff)) { LLDB_LOG(log, "CTF parsing failed: invalid label section offset in header: {0}", ctf_header.lbloff); return false; } if (!m_data.ValidOffset(m_body_offset + ctf_header.objtoff)) { LLDB_LOG(log, "CTF parsing failed: invalid object section offset in header: {0}", ctf_header.objtoff); return false; } if (!m_data.ValidOffset(m_body_offset + ctf_header.funcoff)) { LLDB_LOG( log, "CTF parsing failed: invalid function section offset in header: {0}", ctf_header.funcoff); return false; } if (!m_data.ValidOffset(m_body_offset + ctf_header.typeoff)) { LLDB_LOG(log, "CTF parsing failed: invalid type section offset in header: {0}", ctf_header.typeoff); return false; } if (!m_data.ValidOffset(m_body_offset + ctf_header.stroff)) { LLDB_LOG(log, "CTF parsing failed: invalid string section offset in header: {0}", ctf_header.stroff); return false; } const lldb::offset_t str_end_offset = m_body_offset + ctf_header.stroff + ctf_header.strlen; if (!m_data.ValidOffset(str_end_offset - 1)) { LLDB_LOG(log, "CTF parsing failed: invalid string section length in header: {0}", ctf_header.strlen); return false; } if (m_body_offset + ctf_header.stroff + ctf_header.parlabel > str_end_offset) { LLDB_LOG(log, "CTF parsing failed: invalid parent label offset: {0} exceeds end " "of string section ({1})", ctf_header.parlabel, str_end_offset); return false; } if (m_body_offset + ctf_header.stroff + ctf_header.parname > str_end_offset) { LLDB_LOG(log, "CTF parsing failed: invalid parent name offset: {0} exceeds end " "of string section ({1})", ctf_header.parname, str_end_offset); return false; } LLDB_LOG(log, "Parsed valid CTF header: lbloff = {0}, objtoff = {1}, funcoff = " "{2}, typeoff = {3}, stroff = {4}, strlen = {5}", ctf_header.lbloff, ctf_header.objtoff, ctf_header.funcoff, ctf_header.typeoff, ctf_header.stroff, ctf_header.strlen); return true; } void SymbolFileCTF::InitializeObject() { Log *log = GetLog(LLDBLog::Symbols); auto type_system_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC); if (auto err = type_system_or_err.takeError()) { LLDB_LOG_ERROR(log, std::move(err), "Unable to get type system: {0}"); return; } auto ts = *type_system_or_err; m_ast = llvm::dyn_cast_or_null(ts.get()); LazyBool optimized = eLazyBoolNo; m_comp_unit_sp = std::make_shared( m_objfile_sp->GetModule(), nullptr, "", 0, eLanguageTypeC, optimized); ParseTypes(*m_comp_unit_sp); } llvm::StringRef SymbolFileCTF::ReadString(lldb::offset_t str_offset) const { lldb::offset_t offset = m_body_offset + m_header->stroff + str_offset; if (!m_data.ValidOffset(offset)) return "(invalid)"; const char *str = m_data.GetCStr(&offset); if (str && !*str) return "(anon)"; return llvm::StringRef(str); } /// Return the integer display representation encoded in the given data. static uint32_t GetEncoding(uint32_t data) { // Mask bits 24–31. return ((data)&0xff000000) >> 24; } /// Return the integral width in bits encoded in the given data. static uint32_t GetBits(uint32_t data) { // Mask bits 0-15. return (data)&0x0000ffff; } /// Return the type kind encoded in the given data. uint32_t GetKind(uint32_t data) { // Mask bits 26–31. return ((data)&0xf800) >> 11; } /// Return the variable length encoded in the given data. uint32_t GetVLen(uint32_t data) { // Mask bits 0–24. return (data)&0x3ff; } static uint32_t GetBytes(uint32_t bits) { return bits / sizeof(unsigned); } static clang::TagTypeKind TranslateRecordKind(CTFType::Kind type) { switch (type) { case CTFType::Kind::eStruct: return clang::TagTypeKind::Struct; case CTFType::Kind::eUnion: return clang::TagTypeKind::Union; default: lldbassert(false && "Invalid record kind!"); return clang::TagTypeKind::Struct; } } llvm::Expected SymbolFileCTF::CreateInteger(const CTFInteger &ctf_integer) { lldb::BasicType basic_type = TypeSystemClang::GetBasicTypeEnumeration(ctf_integer.name); if (basic_type == eBasicTypeInvalid) return llvm::make_error( llvm::formatv("unsupported integer type: no corresponding basic clang " "type for '{0}'", ctf_integer.name), llvm::inconvertibleErrorCode()); CompilerType compiler_type = m_ast->GetBasicType(basic_type); if (basic_type != eBasicTypeVoid && basic_type != eBasicTypeBool) { // Make sure the type we got is an integer type. bool compiler_type_is_signed = false; if (!compiler_type.IsIntegerType(compiler_type_is_signed)) return llvm::make_error( llvm::formatv( "Found compiler type for '{0}' but it's not an integer type: {1}", ctf_integer.name, compiler_type.GetDisplayTypeName().GetStringRef()), llvm::inconvertibleErrorCode()); // Make sure the signing matches between the CTF and the compiler type. const bool type_is_signed = (ctf_integer.encoding & IntEncoding::eSigned); if (compiler_type_is_signed != type_is_signed) return llvm::make_error( llvm::formatv("Found integer compiler type for {0} but compiler type " "is {1} and {0} is {2}", ctf_integer.name, compiler_type_is_signed ? "signed" : "unsigned", type_is_signed ? "signed" : "unsigned"), llvm::inconvertibleErrorCode()); } Declaration decl; return MakeType(ctf_integer.uid, ConstString(ctf_integer.name), GetBytes(ctf_integer.bits), nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, compiler_type, lldb_private::Type::ResolveState::Full); } llvm::Expected SymbolFileCTF::CreateModifier(const CTFModifier &ctf_modifier) { Type *ref_type = ResolveTypeUID(ctf_modifier.type); if (!ref_type) return llvm::make_error( llvm::formatv("Could not find modified type: {0}", ctf_modifier.type), llvm::inconvertibleErrorCode()); CompilerType compiler_type; switch (ctf_modifier.kind) { case CTFType::ePointer: compiler_type = ref_type->GetFullCompilerType().GetPointerType(); break; case CTFType::eConst: compiler_type = ref_type->GetFullCompilerType().AddConstModifier(); break; case CTFType::eVolatile: compiler_type = ref_type->GetFullCompilerType().AddVolatileModifier(); break; case CTFType::eRestrict: compiler_type = ref_type->GetFullCompilerType().AddRestrictModifier(); break; default: return llvm::make_error( llvm::formatv("ParseModifier called with unsupported kind: {0}", ctf_modifier.kind), llvm::inconvertibleErrorCode()); } Declaration decl; return MakeType(ctf_modifier.uid, ConstString(), 0, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, compiler_type, lldb_private::Type::ResolveState::Full); } llvm::Expected SymbolFileCTF::CreateTypedef(const CTFTypedef &ctf_typedef) { Type *underlying_type = ResolveTypeUID(ctf_typedef.type); if (!underlying_type) return llvm::make_error( llvm::formatv("Could not find typedef underlying type: {0}", ctf_typedef.type), llvm::inconvertibleErrorCode()); CompilerType target_ast_type = underlying_type->GetFullCompilerType(); clang::DeclContext *decl_ctx = m_ast->GetTranslationUnitDecl(); CompilerType ast_typedef = target_ast_type.CreateTypedef( ctf_typedef.name.data(), m_ast->CreateDeclContext(decl_ctx), 0); Declaration decl; return MakeType(ctf_typedef.uid, ConstString(ctf_typedef.name), 0, nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ast_typedef, lldb_private::Type::ResolveState::Full); } llvm::Expected SymbolFileCTF::CreateArray(const CTFArray &ctf_array) { Type *element_type = ResolveTypeUID(ctf_array.type); if (!element_type) return llvm::make_error( llvm::formatv("Could not find array element type: {0}", ctf_array.type), llvm::inconvertibleErrorCode()); std::optional element_size = element_type->GetByteSize(nullptr); if (!element_size) return llvm::make_error( llvm::formatv("could not get element size of type: {0}", ctf_array.type), llvm::inconvertibleErrorCode()); uint64_t size = ctf_array.nelems * *element_size; CompilerType compiler_type = m_ast->CreateArrayType( element_type->GetFullCompilerType(), ctf_array.nelems, /*is_gnu_vector*/ false); Declaration decl; return MakeType(ctf_array.uid, ConstString(), size, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, compiler_type, lldb_private::Type::ResolveState::Full); } llvm::Expected SymbolFileCTF::CreateEnum(const CTFEnum &ctf_enum) { Declaration decl; CompilerType enum_type = m_ast->CreateEnumerationType( ctf_enum.name, m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), decl, m_ast->GetBasicType(eBasicTypeInt), /*is_scoped=*/false); for (const CTFEnum::Value &value : ctf_enum.values) { Declaration value_decl; m_ast->AddEnumerationValueToEnumerationType( enum_type, value_decl, value.name.data(), value.value, ctf_enum.size); } TypeSystemClang::CompleteTagDeclarationDefinition(enum_type); return MakeType(ctf_enum.uid, ConstString(), 0, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, enum_type, lldb_private::Type::ResolveState::Full); } llvm::Expected SymbolFileCTF::CreateFunction(const CTFFunction &ctf_function) { std::vector arg_types; for (uint32_t arg : ctf_function.args) { if (Type *arg_type = ResolveTypeUID(arg)) arg_types.push_back(arg_type->GetFullCompilerType()); } Type *ret_type = ResolveTypeUID(ctf_function.return_type); if (!ret_type) return llvm::make_error( llvm::formatv("Could not find function return type: {0}", ctf_function.return_type), llvm::inconvertibleErrorCode()); CompilerType func_type = m_ast->CreateFunctionType( ret_type->GetFullCompilerType(), arg_types.data(), arg_types.size(), ctf_function.variadic, 0, clang::CallingConv::CC_C); Declaration decl; return MakeType(ctf_function.uid, ConstString(ctf_function.name), 0, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, func_type, lldb_private::Type::ResolveState::Full); } llvm::Expected SymbolFileCTF::CreateRecord(const CTFRecord &ctf_record) { const clang::TagTypeKind tag_kind = TranslateRecordKind(ctf_record.kind); CompilerType record_type = m_ast->CreateRecordType( nullptr, OptionalClangModuleID(), eAccessPublic, ctf_record.name.data(), llvm::to_underlying(tag_kind), eLanguageTypeC); m_compiler_types[record_type.GetOpaqueQualType()] = &ctf_record; Declaration decl; return MakeType(ctf_record.uid, ConstString(ctf_record.name), ctf_record.size, nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, record_type, lldb_private::Type::ResolveState::Forward); } bool SymbolFileCTF::CompleteType(CompilerType &compiler_type) { // Check if we have a CTF type for the given incomplete compiler type. auto it = m_compiler_types.find(compiler_type.GetOpaqueQualType()); if (it == m_compiler_types.end()) return false; const CTFType *ctf_type = it->second; assert(ctf_type && "m_compiler_types should only contain valid CTF types"); // We only support resolving record types. assert(llvm::isa(ctf_type)); // Cast to the appropriate CTF type. const CTFRecord *ctf_record = static_cast(ctf_type); // If any of the fields are incomplete, we cannot complete the type. for (const CTFRecord::Field &field : ctf_record->fields) { if (!ResolveTypeUID(field.type)) { LLDB_LOG(GetLog(LLDBLog::Symbols), "Cannot complete type {0} because field {1} is incomplete", ctf_type->uid, field.type); return false; } } // Complete the record type. m_ast->StartTagDeclarationDefinition(compiler_type); for (const CTFRecord::Field &field : ctf_record->fields) { Type *field_type = ResolveTypeUID(field.type); assert(field_type && "field must be complete"); const uint32_t field_size = field_type->GetByteSize(nullptr).value_or(0); TypeSystemClang::AddFieldToRecordType(compiler_type, field.name, field_type->GetFullCompilerType(), eAccessPublic, field_size); } m_ast->CompleteTagDeclarationDefinition(compiler_type); // Now that the compiler type is complete, we don't need to remember it // anymore and can remove the CTF record type. m_compiler_types.erase(compiler_type.GetOpaqueQualType()); m_ctf_types.erase(ctf_type->uid); return true; } llvm::Expected SymbolFileCTF::CreateForward(const CTFForward &ctf_forward) { CompilerType forward_compiler_type = m_ast->CreateRecordType( nullptr, OptionalClangModuleID(), eAccessPublic, ctf_forward.name, llvm::to_underlying(clang::TagTypeKind::Struct), eLanguageTypeC); Declaration decl; return MakeType(ctf_forward.uid, ConstString(ctf_forward.name), 0, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, forward_compiler_type, Type::ResolveState::Forward); } llvm::Expected SymbolFileCTF::CreateType(CTFType *ctf_type) { if (!ctf_type) return llvm::make_error( "cannot create type for unparsed type", llvm::inconvertibleErrorCode()); switch (ctf_type->kind) { case CTFType::Kind::eInteger: return CreateInteger(*static_cast(ctf_type)); case CTFType::Kind::eConst: case CTFType::Kind::ePointer: case CTFType::Kind::eRestrict: case CTFType::Kind::eVolatile: return CreateModifier(*static_cast(ctf_type)); case CTFType::Kind::eTypedef: return CreateTypedef(*static_cast(ctf_type)); case CTFType::Kind::eArray: return CreateArray(*static_cast(ctf_type)); case CTFType::Kind::eEnum: return CreateEnum(*static_cast(ctf_type)); case CTFType::Kind::eFunction: return CreateFunction(*static_cast(ctf_type)); case CTFType::Kind::eStruct: case CTFType::Kind::eUnion: return CreateRecord(*static_cast(ctf_type)); case CTFType::Kind::eForward: return CreateForward(*static_cast(ctf_type)); case CTFType::Kind::eUnknown: case CTFType::Kind::eFloat: case CTFType::Kind::eSlice: return llvm::make_error( llvm::formatv("unsupported type (uid = {0}, name = {1}, kind = {2})", ctf_type->uid, ctf_type->name, ctf_type->kind), llvm::inconvertibleErrorCode()); } llvm_unreachable("Unexpected CTF type kind"); } llvm::Expected> SymbolFileCTF::ParseType(lldb::offset_t &offset, lldb::user_id_t uid) { ctf_stype_t ctf_stype; ctf_stype.name = m_data.GetU32(&offset); ctf_stype.info = m_data.GetU32(&offset); ctf_stype.size = m_data.GetU32(&offset); llvm::StringRef name = ReadString(ctf_stype.name); const uint32_t kind = GetKind(ctf_stype.info); const uint32_t variable_length = GetVLen(ctf_stype.info); const uint32_t type = ctf_stype.GetType(); const uint32_t size = ctf_stype.GetSize(); switch (kind) { case TypeKind::eInteger: { const uint32_t vdata = m_data.GetU32(&offset); const uint32_t bits = GetBits(vdata); const uint32_t encoding = GetEncoding(vdata); return std::make_unique(uid, name, bits, encoding); } case TypeKind::eConst: return std::make_unique(uid, type); case TypeKind::ePointer: return std::make_unique(uid, type); case TypeKind::eRestrict: return std::make_unique(uid, type); case TypeKind::eVolatile: return std::make_unique(uid, type); case TypeKind::eTypedef: return std::make_unique(uid, name, type); case TypeKind::eArray: { const uint32_t type = m_data.GetU32(&offset); const uint32_t index = m_data.GetU32(&offset); const uint32_t nelems = m_data.GetU32(&offset); return std::make_unique(uid, name, type, index, nelems); } case TypeKind::eEnum: { std::vector values; for (uint32_t i = 0; i < variable_length; ++i) { const uint32_t value_name = m_data.GetU32(&offset); const uint32_t value = m_data.GetU32(&offset); values.emplace_back(ReadString(value_name), value); } return std::make_unique(uid, name, variable_length, size, values); } case TypeKind::eFunction: { std::vector args; bool variadic = false; for (uint32_t i = 0; i < variable_length; ++i) { const uint32_t arg_uid = m_data.GetU32(&offset); // If the last argument is 0, this is a variadic function. if (arg_uid == 0) { variadic = true; break; } args.push_back(arg_uid); } // If the number of arguments is odd, a single uint32_t of padding is // inserted to maintain alignment. if (variable_length % 2 == 1) m_data.GetU32(&offset); return std::make_unique(uid, name, variable_length, type, args, variadic); } case TypeKind::eStruct: case TypeKind::eUnion: { std::vector fields; for (uint32_t i = 0; i < variable_length; ++i) { const uint32_t field_name = m_data.GetU32(&offset); const uint32_t type = m_data.GetU32(&offset); uint64_t field_offset = 0; if (size < g_ctf_field_threshold) { field_offset = m_data.GetU16(&offset); m_data.GetU16(&offset); // Padding } else { const uint32_t offset_hi = m_data.GetU32(&offset); const uint32_t offset_lo = m_data.GetU32(&offset); field_offset = (((uint64_t)offset_hi) << 32) | ((uint64_t)offset_lo); } fields.emplace_back(ReadString(field_name), type, field_offset); } return std::make_unique(static_cast(kind), uid, name, variable_length, size, fields); } case TypeKind::eForward: return std::make_unique(uid, name); case TypeKind::eUnknown: return std::make_unique(static_cast(kind), uid, name); case TypeKind::eFloat: case TypeKind::eSlice: offset += (variable_length * sizeof(uint32_t)); break; } return llvm::make_error( llvm::formatv("unsupported type (name = {0}, kind = {1}, vlength = {2})", name, kind, variable_length), llvm::inconvertibleErrorCode()); } size_t SymbolFileCTF::ParseTypes(CompileUnit &cu) { if (!ParseHeader()) return 0; if (!m_types.empty()) return 0; if (!m_ast) return 0; Log *log = GetLog(LLDBLog::Symbols); LLDB_LOG(log, "Parsing CTF types"); lldb::offset_t type_offset = m_body_offset + m_header->typeoff; const lldb::offset_t type_offset_end = m_body_offset + m_header->stroff; lldb::user_id_t type_uid = 1; while (type_offset < type_offset_end) { llvm::Expected> type_or_error = ParseType(type_offset, type_uid); if (type_or_error) { m_ctf_types[(*type_or_error)->uid] = std::move(*type_or_error); } else { LLDB_LOG_ERROR(log, type_or_error.takeError(), "Failed to parse type {1} at offset {2}: {0}", type_uid, type_offset); } type_uid++; } LLDB_LOG(log, "Parsed {0} CTF types", m_ctf_types.size()); for (lldb::user_id_t uid = 1; uid < type_uid; ++uid) ResolveTypeUID(uid); LLDB_LOG(log, "Created {0} CTF types", m_types.size()); return m_types.size(); } size_t SymbolFileCTF::ParseFunctions(CompileUnit &cu) { if (!ParseHeader()) return 0; if (!m_functions.empty()) return 0; if (!m_ast) return 0; Symtab *symtab = GetObjectFile()->GetModule()->GetSymtab(); if (!symtab) return 0; Log *log = GetLog(LLDBLog::Symbols); LLDB_LOG(log, "Parsing CTF functions"); lldb::offset_t function_offset = m_body_offset + m_header->funcoff; const lldb::offset_t function_offset_end = m_body_offset + m_header->typeoff; uint32_t symbol_idx = 0; Declaration decl; while (function_offset < function_offset_end) { const uint32_t info = m_data.GetU32(&function_offset); const uint16_t kind = GetKind(info); const uint16_t variable_length = GetVLen(info); Symbol *symbol = symtab->FindSymbolWithType( eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, symbol_idx); // Skip padding. if (kind == TypeKind::eUnknown && variable_length == 0) continue; // Skip unexpected kinds. if (kind != TypeKind::eFunction) continue; const uint32_t ret_uid = m_data.GetU32(&function_offset); const uint32_t num_args = variable_length; std::vector arg_types; arg_types.reserve(num_args); bool is_variadic = false; for (uint32_t i = 0; i < variable_length; i++) { const uint32_t arg_uid = m_data.GetU32(&function_offset); // If the last argument is 0, this is a variadic function. if (arg_uid == 0) { is_variadic = true; break; } Type *arg_type = ResolveTypeUID(arg_uid); arg_types.push_back(arg_type ? arg_type->GetFullCompilerType() : CompilerType()); } if (symbol) { Type *ret_type = ResolveTypeUID(ret_uid); AddressRange func_range = AddressRange(symbol->GetFileAddress(), symbol->GetByteSize(), GetObjectFile()->GetModule()->GetSectionList()); // Create function type. CompilerType func_type = m_ast->CreateFunctionType( ret_type ? ret_type->GetFullCompilerType() : CompilerType(), arg_types.data(), arg_types.size(), is_variadic, 0, clang::CallingConv::CC_C); lldb::user_id_t function_type_uid = m_types.size() + 1; TypeSP type_sp = MakeType(function_type_uid, symbol->GetName(), 0, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, func_type, lldb_private::Type::ResolveState::Full); m_types[function_type_uid] = type_sp; // Create function. lldb::user_id_t func_uid = m_functions.size(); FunctionSP function_sp = std::make_shared( &cu, func_uid, function_type_uid, symbol->GetMangled(), type_sp.get(), func_range); m_functions.emplace_back(function_sp); cu.AddFunction(function_sp); } } LLDB_LOG(log, "CTF parsed {0} functions", m_functions.size()); return m_functions.size(); } static DWARFExpression CreateDWARFExpression(ModuleSP module_sp, const Symbol &symbol) { if (!module_sp) return DWARFExpression(); const ArchSpec &architecture = module_sp->GetArchitecture(); ByteOrder byte_order = architecture.GetByteOrder(); uint32_t address_size = architecture.GetAddressByteSize(); uint32_t byte_size = architecture.GetDataByteSize(); StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); stream.PutHex8(lldb_private::dwarf::DW_OP_addr); stream.PutMaxHex64(symbol.GetFileAddress(), address_size, byte_order); DataBufferSP buffer = std::make_shared(stream.GetData(), stream.GetSize()); lldb_private::DataExtractor extractor(buffer, byte_order, address_size, byte_size); DWARFExpression result(extractor); result.SetRegisterKind(eRegisterKindDWARF); return result; } size_t SymbolFileCTF::ParseObjects(CompileUnit &comp_unit) { if (!ParseHeader()) return 0; if (!m_variables.empty()) return 0; if (!m_ast) return 0; ModuleSP module_sp = GetObjectFile()->GetModule(); Symtab *symtab = module_sp->GetSymtab(); if (!symtab) return 0; Log *log = GetLog(LLDBLog::Symbols); LLDB_LOG(log, "Parsing CTF objects"); lldb::offset_t object_offset = m_body_offset + m_header->objtoff; const lldb::offset_t object_offset_end = m_body_offset + m_header->funcoff; uint32_t symbol_idx = 0; Declaration decl; while (object_offset < object_offset_end) { const uint32_t type_uid = m_data.GetU32(&object_offset); if (Symbol *symbol = symtab->FindSymbolWithType(eSymbolTypeData, Symtab::eDebugYes, Symtab::eVisibilityAny, symbol_idx)) { Variable::RangeList ranges; ranges.Append(symbol->GetFileAddress(), symbol->GetByteSize()); auto type_sp = std::make_shared(*this, type_uid); DWARFExpressionList location( module_sp, CreateDWARFExpression(module_sp, *symbol), nullptr); lldb::user_id_t variable_type_uid = m_variables.size(); m_variables.emplace_back(std::make_shared( variable_type_uid, symbol->GetName().AsCString(), symbol->GetName().AsCString(), type_sp, eValueTypeVariableGlobal, m_comp_unit_sp.get(), ranges, &decl, location, symbol->IsExternal(), /*artificial=*/false, /*location_is_constant_data*/ false)); } } LLDB_LOG(log, "Parsed {0} CTF objects", m_variables.size()); return m_variables.size(); } uint32_t SymbolFileCTF::CalculateAbilities() { if (!m_objfile_sp) return 0; if (!ParseHeader()) return 0; return VariableTypes | Functions | GlobalVariables; } uint32_t SymbolFileCTF::ResolveSymbolContext(const Address &so_addr, SymbolContextItem resolve_scope, SymbolContext &sc) { std::lock_guard guard(GetModuleMutex()); if (m_objfile_sp->GetSymtab() == nullptr) return 0; uint32_t resolved_flags = 0; // Resolve symbols. if (resolve_scope & eSymbolContextSymbol) { sc.symbol = m_objfile_sp->GetSymtab()->FindSymbolContainingFileAddress( so_addr.GetFileAddress()); if (sc.symbol) resolved_flags |= eSymbolContextSymbol; } // Resolve functions. if (resolve_scope & eSymbolContextFunction) { for (FunctionSP function_sp : m_functions) { if (function_sp->GetAddressRange().ContainsFileAddress( so_addr.GetFileAddress())) { sc.function = function_sp.get(); resolved_flags |= eSymbolContextFunction; break; } } } // Resolve variables. if (resolve_scope & eSymbolContextVariable) { for (VariableSP variable_sp : m_variables) { if (variable_sp->LocationIsValidForAddress(so_addr.GetFileAddress())) { sc.variable = variable_sp.get(); break; } } } return resolved_flags; } CompUnitSP SymbolFileCTF::ParseCompileUnitAtIndex(uint32_t idx) { if (idx == 0) return m_comp_unit_sp; return {}; } size_t SymbolFileCTF::ParseVariablesForContext(const lldb_private::SymbolContext &sc) { return ParseObjects(*m_comp_unit_sp); } void SymbolFileCTF::AddSymbols(Symtab &symtab) { // CTF does not encode symbols. // We rely on the existing symbol table to map symbols to type. } lldb_private::Type *SymbolFileCTF::ResolveTypeUID(lldb::user_id_t type_uid) { auto type_it = m_types.find(type_uid); if (type_it != m_types.end()) return type_it->second.get(); auto ctf_type_it = m_ctf_types.find(type_uid); if (ctf_type_it == m_ctf_types.end()) return nullptr; CTFType *ctf_type = ctf_type_it->second.get(); assert(ctf_type && "m_ctf_types should only contain valid CTF types"); Log *log = GetLog(LLDBLog::Symbols); llvm::Expected type_or_error = CreateType(ctf_type); if (!type_or_error) { LLDB_LOG_ERROR(log, type_or_error.takeError(), "Failed to create type for {1}: {0}", ctf_type->uid); return {}; } TypeSP type_sp = *type_or_error; if (log) { StreamString ss; type_sp->Dump(&ss, true); LLDB_LOGV(log, "Adding type {0}: {1}", type_sp->GetID(), llvm::StringRef(ss.GetString()).rtrim()); } m_types[type_uid] = type_sp; // Except for record types which we'll need to complete later, we don't need // the CTF type anymore. if (!isa(ctf_type)) m_ctf_types.erase(type_uid); return type_sp.get(); } void SymbolFileCTF::FindTypes(const lldb_private::TypeQuery &match, lldb_private::TypeResults &results) { // Make sure we haven't already searched this SymbolFile before. if (results.AlreadySearched(this)) return; ConstString name = match.GetTypeBasename(); for (TypeSP type_sp : GetTypeList().Types()) { if (type_sp && type_sp->GetName() == name) { results.InsertUnique(type_sp); if (results.Done(match)) return; } } } void SymbolFileCTF::FindTypesByRegex( const lldb_private::RegularExpression ®ex, uint32_t max_matches, lldb_private::TypeMap &types) { ParseTypes(*m_comp_unit_sp); size_t matches = 0; for (TypeSP type_sp : GetTypeList().Types()) { if (matches == max_matches) break; if (type_sp && regex.Execute(type_sp->GetName())) types.Insert(type_sp); matches++; } } void SymbolFileCTF::FindFunctions( const lldb_private::Module::LookupInfo &lookup_info, const lldb_private::CompilerDeclContext &parent_decl_ctx, bool include_inlines, lldb_private::SymbolContextList &sc_list) { ParseFunctions(*m_comp_unit_sp); ConstString name = lookup_info.GetLookupName(); for (FunctionSP function_sp : m_functions) { if (function_sp && function_sp->GetName() == name) { lldb_private::SymbolContext sc; sc.comp_unit = m_comp_unit_sp.get(); sc.function = function_sp.get(); sc_list.Append(sc); } } } void SymbolFileCTF::FindFunctions(const lldb_private::RegularExpression ®ex, bool include_inlines, lldb_private::SymbolContextList &sc_list) { for (FunctionSP function_sp : m_functions) { if (function_sp && regex.Execute(function_sp->GetName())) { lldb_private::SymbolContext sc; sc.comp_unit = m_comp_unit_sp.get(); sc.function = function_sp.get(); sc_list.Append(sc); } } } void SymbolFileCTF::FindGlobalVariables( lldb_private::ConstString name, const lldb_private::CompilerDeclContext &parent_decl_ctx, uint32_t max_matches, lldb_private::VariableList &variables) { ParseObjects(*m_comp_unit_sp); size_t matches = 0; for (VariableSP variable_sp : m_variables) { if (matches == max_matches) break; if (variable_sp && variable_sp->GetName() == name) { variables.AddVariable(variable_sp); matches++; } } } void SymbolFileCTF::FindGlobalVariables( const lldb_private::RegularExpression ®ex, uint32_t max_matches, lldb_private::VariableList &variables) { ParseObjects(*m_comp_unit_sp); size_t matches = 0; for (VariableSP variable_sp : m_variables) { if (matches == max_matches) break; if (variable_sp && regex.Execute(variable_sp->GetName())) { variables.AddVariable(variable_sp); matches++; } } }