| //===-- 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/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB | |
| #include "llvm/Support/ErrorExtras.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" | |
| #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | |
| #include <memory> | |
| #include <optional> | |
| #if LLVM_ENABLE_ZLIB | |
| #include <zlib.h> | |
| #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<DataBufferHeap>(decompressed_size, 0x0); | |
| z_stream zstr; | |
| memset(&zstr, 0, sizeof(zstr)); | |
| zstr.next_in = (Bytef *)const_cast<uint8_t *>(m_data.GetDataStart() + | |
| sizeof(ctf_header_t)); | |
| zstr.avail_in = m_data.BytesLeft(offset); | |
| zstr.next_out = | |
| (Bytef *)const_cast<uint8_t *>(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 ({}) doesn't match " | |
| "expected size ({})", | |
| 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<TypeSystemClang>(ts.get()); | |
| LazyBool optimized = eLazyBoolNo; | |
| m_comp_unit_sp = std::make_shared<CompileUnit>( | |
| 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<TypeSP> | |
| SymbolFileCTF::CreateInteger(const CTFInteger &ctf_integer) { | |
| lldb::BasicType basic_type = | |
| TypeSystemClang::GetBasicTypeEnumeration(ctf_integer.name); | |
| if (basic_type == eBasicTypeInvalid) | |
| return llvm::createStringErrorV( | |
| "unsupported integer type: no corresponding basic clang " | |
| "type for '{0}'", | |
| ctf_integer.name); | |
| 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::createStringErrorV( | |
| "Found compiler type for '{0}' but it's not an integer type: {1}", | |
| ctf_integer.name, compiler_type.GetDisplayTypeName().GetStringRef()); | |
| // 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::createStringErrorV( | |
| "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"); | |
| } | |
| 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<lldb::TypeSP> | |
| SymbolFileCTF::CreateModifier(const CTFModifier &ctf_modifier) { | |
| Type *ref_type = ResolveTypeUID(ctf_modifier.type); | |
| if (!ref_type) | |
| return llvm::createStringErrorV("could not find modified type: {0}", | |
| ctf_modifier.type); | |
| 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::createStringErrorV( | |
| "ParseModifier called with unsupported kind: {0}", ctf_modifier.kind); | |
| } | |
| 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<lldb::TypeSP> | |
| SymbolFileCTF::CreateTypedef(const CTFTypedef &ctf_typedef) { | |
| Type *underlying_type = ResolveTypeUID(ctf_typedef.type); | |
| if (!underlying_type) | |
| return llvm::createStringErrorV( | |
| "could not find typedef underlying type: {0}", ctf_typedef.type); | |
| 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<lldb::TypeSP> | |
| SymbolFileCTF::CreateArray(const CTFArray &ctf_array) { | |
| Type *element_type = ResolveTypeUID(ctf_array.type); | |
| if (!element_type) | |
| return llvm::createStringErrorV("could not find array element type: {0}", | |
| ctf_array.type); | |
| auto element_size_or_err = element_type->GetByteSize(nullptr); | |
| if (!element_size_or_err) | |
| return element_size_or_err.takeError(); | |
| uint64_t size = ctf_array.nelems * *element_size_or_err; | |
| 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<lldb::TypeSP> | |
| 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<lldb::TypeSP> | |
| SymbolFileCTF::CreateFunction(const CTFFunction &ctf_function) { | |
| std::vector<CompilerType> 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::createStringErrorV("could not find function return type: {0}", | |
| ctf_function.return_type); | |
| CompilerType func_type = m_ast->CreateFunctionType( | |
| ret_type->GetFullCompilerType(), arg_types, 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<lldb::TypeSP> | |
| SymbolFileCTF::CreateRecord(const CTFRecord &ctf_record) { | |
| const clang::TagTypeKind tag_kind = TranslateRecordKind(ctf_record.kind); | |
| CompilerType record_type = m_ast->CreateRecordType( | |
| nullptr, OptionalClangModuleID(), 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<CTFRecord>(ctf_type)); | |
| // Cast to the appropriate CTF type. | |
| const CTFRecord *ctf_record = static_cast<const CTFRecord *>(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 = | |
| llvm::expectedToOptional(field_type->GetByteSize(nullptr)).value_or(0); | |
| TypeSystemClang::AddFieldToRecordType(compiler_type, field.name, | |
| field_type->GetFullCompilerType(), | |
| 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<lldb::TypeSP> | |
| SymbolFileCTF::CreateForward(const CTFForward &ctf_forward) { | |
| CompilerType forward_compiler_type = m_ast->CreateRecordType( | |
| nullptr, OptionalClangModuleID(), 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<TypeSP> SymbolFileCTF::CreateType(CTFType *ctf_type) { | |
| if (!ctf_type) | |
| return llvm::createStringError("cannot create type for unparsed type"); | |
| switch (ctf_type->kind) { | |
| case CTFType::Kind::eInteger: | |
| return CreateInteger(*static_cast<CTFInteger *>(ctf_type)); | |
| case CTFType::Kind::eConst: | |
| case CTFType::Kind::ePointer: | |
| case CTFType::Kind::eRestrict: | |
| case CTFType::Kind::eVolatile: | |
| return CreateModifier(*static_cast<CTFModifier *>(ctf_type)); | |
| case CTFType::Kind::eTypedef: | |
| return CreateTypedef(*static_cast<CTFTypedef *>(ctf_type)); | |
| case CTFType::Kind::eArray: | |
| return CreateArray(*static_cast<CTFArray *>(ctf_type)); | |
| case CTFType::Kind::eEnum: | |
| return CreateEnum(*static_cast<CTFEnum *>(ctf_type)); | |
| case CTFType::Kind::eFunction: | |
| return CreateFunction(*static_cast<CTFFunction *>(ctf_type)); | |
| case CTFType::Kind::eStruct: | |
| case CTFType::Kind::eUnion: | |
| return CreateRecord(*static_cast<CTFRecord *>(ctf_type)); | |
| case CTFType::Kind::eForward: | |
| return CreateForward(*static_cast<CTFForward *>(ctf_type)); | |
| case CTFType::Kind::eUnknown: | |
| case CTFType::Kind::eFloat: | |
| case CTFType::Kind::eSlice: | |
| return llvm::createStringErrorV( | |
| "unsupported type (uid = {0}, name = {1}, kind = {2})", ctf_type->uid, | |
| ctf_type->name, ctf_type->kind); | |
| } | |
| llvm_unreachable("Unexpected CTF type kind"); | |
| } | |
| llvm::Expected<std::unique_ptr<CTFType>> | |
| 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<CTFInteger>(uid, name, bits, encoding); | |
| } | |
| case TypeKind::eConst: | |
| return std::make_unique<CTFConst>(uid, type); | |
| case TypeKind::ePointer: | |
| return std::make_unique<CTFPointer>(uid, type); | |
| case TypeKind::eRestrict: | |
| return std::make_unique<CTFRestrict>(uid, type); | |
| case TypeKind::eVolatile: | |
| return std::make_unique<CTFVolatile>(uid, type); | |
| case TypeKind::eTypedef: | |
| return std::make_unique<CTFTypedef>(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<CTFArray>(uid, name, type, index, nelems); | |
| } | |
| case TypeKind::eEnum: { | |
| std::vector<CTFEnum::Value> 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<CTFEnum>(uid, name, variable_length, size, values); | |
| } | |
| case TypeKind::eFunction: { | |
| std::vector<uint32_t> 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<CTFFunction>(uid, name, variable_length, type, args, | |
| variadic); | |
| } | |
| case TypeKind::eStruct: | |
| case TypeKind::eUnion: { | |
| std::vector<CTFRecord::Field> 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<CTFRecord>(static_cast<CTFType::Kind>(kind), uid, | |
| name, variable_length, size, fields); | |
| } | |
| case TypeKind::eForward: | |
| return std::make_unique<CTFForward>(uid, name); | |
| case TypeKind::eUnknown: | |
| return std::make_unique<CTFType>(static_cast<CTFType::Kind>(kind), uid, | |
| name); | |
| case TypeKind::eFloat: | |
| case TypeKind::eSlice: | |
| offset += (variable_length * sizeof(uint32_t)); | |
| break; | |
| } | |
| return llvm::createStringErrorV( | |
| "unsupported type (name = {0}, kind = {1}, vlength = {2})", name, kind, | |
| variable_length); | |
| } | |
| 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<std::unique_ptr<CTFType>> 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); | |
| // Remove the CTF type because we don't need it anymore, except for record | |
| // types which we may need to complete later. | |
| auto ctf_type_it = m_ctf_types.find(uid); | |
| if (ctf_type_it != m_ctf_types.end()) { | |
| CTFType *ctf_type = ctf_type_it->second.get(); | |
| if (!llvm::isa<CTFRecord>(ctf_type)) | |
| m_ctf_types.erase(uid); | |
| } | |
| } | |
| #ifndef NDEBUG | |
| // Verify that the only CTF types left at this point are record types. | |
| for (auto &t : m_ctf_types) { | |
| CTFType *ctf_type = t.second.get(); | |
| assert(ctf_type && "invalid type in m_ctf_types"); | |
| assert(llvm::isa<CTFRecord>(ctf_type) && "leaking non record type"); | |
| } | |
| #endif | |
| 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); | |
| const 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<CompilerType> 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, 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<Function>( | |
| &cu, func_uid, function_type_uid, symbol->GetMangled(), type_sp.get(), | |
| symbol->GetAddress(), AddressRanges{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(); | |
| StreamBuffer<32> stream(Stream::eBinary, byte_order); | |
| stream.PutHex8(llvm::dwarf::DW_OP_addr); | |
| stream.PutMaxHex64(symbol.GetFileAddress(), address_size, byte_order); | |
| DataBufferSP buffer = | |
| std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); | |
| lldb_private::DataExtractor extractor(buffer, byte_order, address_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 (const 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<SymbolFileType>(*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>( | |
| variable_type_uid, symbol->GetName().AsCString(nullptr), | |
| symbol->GetName().AsCString(nullptr), 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<std::recursive_mutex> 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 (llvm::any_of( | |
| function_sp->GetAddressRanges(), [&](const AddressRange range) { | |
| return range.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( | |
| Address(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"); | |
| assert(ctf_type->uid == type_uid && | |
| "CTF type UID doesn't match UID in m_ctf_types"); | |
| Log *log = GetLog(LLDBLog::Symbols); | |
| llvm::Expected<TypeSP> 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_LOG_VERBOSE(log, "Adding type {0}: {1}", type_sp->GetID(), | |
| llvm::StringRef(ss.GetString()).rtrim()); | |
| } | |
| m_types[type_uid] = type_sp; | |
| 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++; | |
| } | |
| } | |
| } |