aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp
new file mode 100644
index 000000000000..386ba44c5ea6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp
@@ -0,0 +1,1118 @@
+//===-- 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 <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 ({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<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::make_error<llvm::StringError>(
+ 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::StringError>(
+ 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::StringError>(
+ 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<lldb::TypeSP>
+SymbolFileCTF::CreateModifier(const CTFModifier &ctf_modifier) {
+ Type *ref_type = ResolveTypeUID(ctf_modifier.type);
+ if (!ref_type)
+ return llvm::make_error<llvm::StringError>(
+ 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::StringError>(
+ 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<lldb::TypeSP>
+SymbolFileCTF::CreateTypedef(const CTFTypedef &ctf_typedef) {
+ Type *underlying_type = ResolveTypeUID(ctf_typedef.type);
+ if (!underlying_type)
+ return llvm::make_error<llvm::StringError>(
+ 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<lldb::TypeSP>
+SymbolFileCTF::CreateArray(const CTFArray &ctf_array) {
+ Type *element_type = ResolveTypeUID(ctf_array.type);
+ if (!element_type)
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Could not find array element type: {0}", ctf_array.type),
+ llvm::inconvertibleErrorCode());
+
+ std::optional<uint64_t> element_size = element_type->GetByteSize(nullptr);
+ if (!element_size)
+ return llvm::make_error<llvm::StringError>(
+ 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<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::make_error<llvm::StringError>(
+ 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<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(), 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<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 = 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<lldb::TypeSP>
+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<TypeSP> SymbolFileCTF::CreateType(CTFType *ctf_type) {
+ if (!ctf_type)
+ return llvm::make_error<llvm::StringError>(
+ "cannot create type for unparsed type", llvm::inconvertibleErrorCode());
+
+ 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::make_error<llvm::StringError>(
+ 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<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::make_error<llvm::StringError>(
+ 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<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);
+
+ 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<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.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<Function>(
+ &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<DataBufferHeap>(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<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(),
+ 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<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 (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<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_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<CTFRecord>(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 &regex, 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 &regex,
+ 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 &regex, 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++;
+ }
+ }
+}