diff --git a/lldb/include/lldb/Symbol/Symbol.h b/lldb/include/lldb/Symbol/Symbol.h --- a/lldb/include/lldb/Symbol/Symbol.h +++ b/lldb/include/lldb/Symbol/Symbol.h @@ -11,12 +11,23 @@ #include "lldb/Core/AddressRange.h" #include "lldb/Core/Mangled.h" +#include "lldb/Core/Section.h" #include "lldb/Symbol/SymbolContextScope.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-private.h" +#include "llvm/Support/JSON.h" namespace lldb_private { +struct JSONSymbol { + std::optional address; + std::optional value; + std::optional size; + std::optional id; + std::optional type; + std::string name; +}; + class Symbol : public SymbolContextScope { public: // ObjectFile readers can classify their symbol table entries and searches @@ -39,6 +50,9 @@ const Symbol &operator=(const Symbol &rhs); + static llvm::Expected FromJSON(const JSONSymbol &symbol, + SectionList *section_list); + void Clear(); bool Compare(ConstString name, lldb::SymbolType type) const; @@ -187,7 +201,7 @@ bool IsWeak() const { return m_is_weak; } - void SetIsWeak (bool b) { m_is_weak = b; } + void SetIsWeak(bool b) { m_is_weak = b; } bool GetByteSizeIsValid() const { return m_size_is_valid; } @@ -332,4 +346,16 @@ } // namespace lldb_private +namespace llvm { +namespace json { + +bool fromJSON(const llvm::json::Value &value, lldb_private::JSONSymbol &symbol, + llvm::json::Path path); + +bool fromJSON(const llvm::json::Value &value, lldb::SymbolType &type, + llvm::json::Path path); + +} // namespace json +} // namespace llvm + #endif // LLDB_SYMBOL_SYMBOL_H diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Breakpad) add_subdirectory(ELF) add_subdirectory(JIT) +add_subdirectory(JSON) add_subdirectory(Mach-O) add_subdirectory(Minidump) add_subdirectory(PDB) diff --git a/lldb/source/Plugins/ObjectFile/JSON/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/JSON/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/JSON/CMakeLists.txt @@ -0,0 +1,12 @@ +add_lldb_library(lldbPluginObjectFileJSON PLUGIN + ObjectFileJSON.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbUtility + LINK_COMPONENTS + Support + TargetParser + ) diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h @@ -0,0 +1,119 @@ +//===-- ObjectFileJSON.h -------------------------------------- -*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "llvm/Support/JSON.h" + +namespace lldb_private { + +class ObjectFileJSON : public ObjectFile { +public: + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "JSON"; } + + static const char *GetPluginDescriptionStatic() { + return "JSON object file reader."; + } + + static ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp, + lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + ModuleSpecList &specs); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // LLVM RTTI support + static char ID; + bool isA(const void *ClassID) const override { + return ClassID == &ID || ObjectFile::isA(ClassID); + } + static bool classof(const ObjectFile *obj) { return obj->isA(&ID); } + + bool ParseHeader() override; + + lldb::ByteOrder GetByteOrder() const override { + return m_arch.GetByteOrder(); + } + + bool IsExecutable() const override { return false; } + + uint32_t GetAddressByteSize() const override { + return m_arch.GetAddressByteSize(); + } + + AddressClass GetAddressClass(lldb::addr_t file_addr) override { + return AddressClass::eInvalid; + } + + void ParseSymtab(lldb_private::Symtab &symtab) override; + + bool IsStripped() override { return false; } + + void CreateSections(SectionList &unified_section_list) override; + + void Dump(Stream *s) override {} + + ArchSpec GetArchitecture() override { return m_arch; } + + UUID GetUUID() override { return m_uuid; } + + uint32_t GetDependentModules(FileSpecList &files) override { return 0; } + + Type CalculateType() override { return eTypeDebugInfo; } + + Strata CalculateStrata() override { return eStrataUser; } + + static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset, + lldb::addr_t length); + + struct Header { + std::string triple; + std::string uuid; + }; + + struct Body { + std::vector symbols; + }; + +private: + ArchSpec m_arch; + UUID m_uuid; + std::vector m_symbols; + + ObjectFileJSON(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t offset, lldb::offset_t length, ArchSpec arch, + UUID uuid, std::vector symbols); +}; + +bool fromJSON(const llvm::json::Value &value, ObjectFileJSON::Header &header, + llvm::json::Path path); + +bool fromJSON(const llvm::json::Value &value, ObjectFileJSON::Body &body, + llvm::json::Path path); + +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp @@ -0,0 +1,176 @@ +//===-- ObjectFileJSON.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 "Plugins/ObjectFile/JSON/ObjectFileJSON.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/DenseSet.h" +#include + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileJSON) + +char ObjectFileJSON::ID; + +void ObjectFileJSON::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileJSON::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectFile * +ObjectFileJSON::CreateInstance(const ModuleSP &module_sp, DataBufferSP data_sp, + offset_t data_offset, const FileSpec *file, + offset_t file_offset, offset_t length) { + if (!data_sp) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + + if (!MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + return nullptr; + + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + + auto text = + llvm::StringRef(reinterpret_cast(data_sp->GetBytes())); + + Expected json = json::parse(text); + if (!json) { + llvm::consumeError(json.takeError()); + return nullptr; + } + + json::Path::Root root; + Header header; + if (!fromJSON(*json, header, root)) + return nullptr; + + ArchSpec arch(header.triple); + UUID uuid; + uuid.SetFromStringRef(header.uuid); + + Body body; + fromJSON(*json, body, root); + + return new ObjectFileJSON(module_sp, data_sp, data_offset, file, file_offset, + length, std::move(arch), std::move(uuid), + std::move(body.symbols)); +} + +ObjectFile *ObjectFileJSON::CreateMemoryInstance(const ModuleSP &module_sp, + WritableDataBufferSP data_sp, + const ProcessSP &process_sp, + addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileJSON::GetModuleSpecifications( + const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset, + offset_t file_offset, offset_t length, ModuleSpecList &specs) { + + if (!MagicBytesMatch(data_sp, data_offset, data_sp->GetByteSize())) + return 0; + + auto text = + llvm::StringRef(reinterpret_cast(data_sp->GetBytes())); + + Expected json = json::parse(text); + if (!json) { + llvm::consumeError(json.takeError()); + return 0; + } + + json::Path::Root root; + Header header; + if (!fromJSON(*json, header, root)) + return 0; + + ArchSpec arch(header.triple); + UUID uuid; + uuid.SetFromStringRef(header.uuid); + + ModuleSpec spec(file, std::move(arch)); + spec.GetUUID() = std::move(uuid); + specs.Append(spec); + return 1; +} + +ObjectFileJSON::ObjectFileJSON(const ModuleSP &module_sp, DataBufferSP &data_sp, + offset_t data_offset, const FileSpec *file, + offset_t offset, offset_t length, ArchSpec arch, + UUID uuid, std::vector symbols) + : ObjectFile(module_sp, file, offset, length, data_sp, data_offset), + m_arch(std::move(arch)), m_uuid(std::move(uuid)), + m_symbols(std::move(symbols)) {} + +bool ObjectFileJSON::ParseHeader() { + // We already parsed the header during initialization. + return true; +} + +void ObjectFileJSON::ParseSymtab(Symtab &symtab) { + Log *log = GetLog(LLDBLog::Symbols); + SectionList *section_list = GetModule()->GetSectionList(); + for (JSONSymbol json_symbol : m_symbols) { + llvm::Expected symbol = Symbol::FromJSON(json_symbol, section_list); + if (!symbol) { + LLDB_LOG_ERROR(log, symbol.takeError(), "invalid symbol"); + continue; + } + symtab.AddSymbol(*symbol); + } + symtab.Finalize(); +} + +void ObjectFileJSON::CreateSections(SectionList &unified_section_list) {} + +bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + uint32_t magic = data.GetU8(&offset); + return magic == '{'; +} + +namespace lldb_private { + +bool fromJSON(const json::Value &value, ObjectFileJSON::Header &header, + json::Path path) { + json::ObjectMapper o(value, path); + return o && o.map("triple", header.triple) && o.map("uuid", header.uuid); +} + +bool fromJSON(const json::Value &value, ObjectFileJSON::Body &body, + json::Path path) { + json::ObjectMapper o(value, path); + return o && o.map("symbols", body.symbols); +} + +} // namespace lldb_private diff --git a/lldb/source/Plugins/SymbolFile/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/CMakeLists.txt --- a/lldb/source/Plugins/SymbolFile/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolFile/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(Breakpad) add_subdirectory(DWARF) +add_subdirectory(JSON) add_subdirectory(NativePDB) add_subdirectory(PDB) add_subdirectory(Symtab) diff --git a/lldb/source/Plugins/SymbolFile/JSON/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/JSON/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/JSON/CMakeLists.txt @@ -0,0 +1,7 @@ +add_lldb_library(lldbPluginSymbolFileJSON PLUGIN + SymbolFileJSON.cpp + + LINK_LIBS + lldbCore + lldbSymbol + ) diff --git a/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h b/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h @@ -0,0 +1,110 @@ +//===-- SymbolFileJSON.h ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_JSON_SYMBOLFILETEXT_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_JSON_SYMBOLFILETEXT_H + +#include +#include +#include + +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolFile.h" + +namespace lldb_private { + +class SymbolFileJSON : public lldb_private::SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + SymbolFileJSON(lldb::ObjectFileSP objfile_sp); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "text"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb::ObjectFileSP objfile_sp); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + uint32_t CalculateAbilities() override; + + lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override { + return lldb::eLanguageTypeUnknown; + } + + size_t ParseFunctions(CompileUnit &comp_unit) override { return 0; } + + bool ParseLineTable(CompileUnit &comp_unit) override { return false; } + + bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; } + + bool ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) override { + return false; + } + + size_t ParseTypes(CompileUnit &cu) override { return 0; } + + bool ParseImportedModules( + const SymbolContext &sc, + std::vector &imported_modules) override { + return false; + } + + size_t ParseBlocksRecursive(Function &func) override { return 0; } + + size_t ParseVariablesForContext(const SymbolContext &sc) override { + return 0; + } + + uint32_t CalculateNumCompileUnits() override { return 0; } + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + Type *ResolveTypeUID(lldb::user_id_t type_uid) override { return nullptr; } + std::optional GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override { + return std::nullopt; + } + + bool CompleteType(CompilerType &compiler_type) override { return false; } + + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) override; + + void GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + + void AddSymbols(Symtab &symtab) override; + +private: + lldb::addr_t GetBaseFileAddress(); + + std::vector> m_symbols; +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_JSON_SYMBOLFILETEXT_H diff --git a/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp b/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp @@ -0,0 +1,105 @@ +//===-- SymbolFileJSON.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 "SymbolFileJSON.h" + +#include "Plugins/ObjectFile/JSON/ObjectFileJSON.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.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/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Timer.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(SymbolFileJSON) + +char SymbolFileJSON::ID; + +SymbolFileJSON::SymbolFileJSON(lldb::ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)) {} + +void SymbolFileJSON::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolFileJSON::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef SymbolFileJSON::GetPluginDescriptionStatic() { + return "Reads debug symbols from a textual symbol table."; +} + +SymbolFile *SymbolFileJSON::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileJSON(std::move(objfile_sp)); +} + +uint32_t SymbolFileJSON::CalculateAbilities() { + if (!m_objfile_sp || !llvm::isa(*m_objfile_sp)) + return 0; + + return GlobalVariables | Functions; +} + +uint32_t SymbolFileJSON::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; + if (resolve_scope & eSymbolContextSymbol) { + sc.symbol = m_objfile_sp->GetSymtab()->FindSymbolContainingFileAddress( + so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + return resolved_flags; +} + +CompUnitSP SymbolFileJSON::ParseCompileUnitAtIndex(uint32_t idx) { return {}; } + +void SymbolFileJSON::GetTypes(SymbolContextScope *sc_scope, TypeClass type_mask, + lldb_private::TypeList &type_list) {} + +void SymbolFileJSON::AddSymbols(Symtab &symtab) { + if (!m_objfile_sp) + return; + + Symtab *json_symtab = m_objfile_sp->GetSymtab(); + if (!json_symtab) + return; + + if (&symtab == json_symtab) + return; + + // Merge the two symbol tables. + const size_t num_new_symbols = json_symtab->GetNumSymbols(); + for (size_t i = 0; i < num_new_symbols; ++i) { + Symbol *s = json_symtab->SymbolAtIndex(i); + symtab.AddSymbol(*s); + } + symtab.Finalize(); +} diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp --- a/lldb/source/Symbol/Symbol.cpp +++ b/lldb/source/Symbol/Symbol.cpp @@ -19,6 +19,7 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/DataEncoder.h" #include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringSwitch.h" using namespace lldb; using namespace lldb_private; @@ -95,6 +96,55 @@ return *this; } +llvm::Expected Symbol::FromJSON(const JSONSymbol &symbol, + SectionList *section_list) { + if (!section_list) + return llvm::make_error("no section list provided", + llvm::inconvertibleErrorCode()); + + if (!symbol.value && !symbol.address) + return llvm::make_error( + "symbol must contain either a value or an address", + llvm::inconvertibleErrorCode()); + + if (symbol.value && symbol.address) + return llvm::make_error( + "symbol cannot contain both a value and an address", + llvm::inconvertibleErrorCode()); + + const uint64_t size = symbol.size.value_or(0); + const bool is_artificial = false; + const bool is_trampoline = false; + const bool is_debug = false; + const bool external = false; + const bool size_is_valid = symbol.size.has_value(); + const bool contains_linker_annotations = false; + const uint32_t flags = 0; + + if (symbol.address) { + if (SectionSP section_sp = + section_list->FindSectionContainingFileAddress(*symbol.address)) { + const uint64_t offset = *symbol.address - section_sp->GetFileAddress(); + return Symbol(symbol.id.value_or(0), Mangled(symbol.name), + symbol.type.value_or(eSymbolTypeAny), external, is_debug, + is_trampoline, is_artificial, + AddressRange(section_sp, offset, size), size_is_valid, + contains_linker_annotations, flags); + } + return llvm::make_error( + llvm::formatv("no section found for address: {0:x}", *symbol.address), + llvm::inconvertibleErrorCode()); + } + + // Absolute symbols encode the integer value in the m_offset of the + // AddressRange object and the section is set to nothing. + return Symbol(symbol.id.value_or(0), Mangled(symbol.name), + symbol.type.value_or(eSymbolTypeAny), external, is_debug, + is_trampoline, is_artificial, + AddressRange(SectionSP(), *symbol.value, size), size_is_valid, + contains_linker_annotations, flags); +} + void Symbol::Clear() { m_uid = UINT32_MAX; m_mangled.Clear(); @@ -622,14 +672,14 @@ const bool is_addr = data.GetU8(offset_ptr) != 0; const uint64_t value = data.GetU64(offset_ptr); if (is_addr) { - m_addr_range.GetBaseAddress().ResolveAddressUsingFileSections( - value, section_list); + m_addr_range.GetBaseAddress().ResolveAddressUsingFileSections(value, + section_list); } else { m_addr_range.GetBaseAddress().Clear(); m_addr_range.GetBaseAddress().SetOffset(value); } m_addr_range.SetByteSize(data.GetU64(offset_ptr)); - m_flags = data.GetU32(offset_ptr); + m_flags = data.GetU32(offset_ptr); return true; } @@ -723,3 +773,77 @@ return false; return true; } + +namespace llvm { +namespace json { + +bool fromJSON(const llvm::json::Value &value, lldb_private::JSONSymbol &symbol, + llvm::json::Path path) { + llvm::json::ObjectMapper o(value, path); + const bool mapped = o && o.map("value", symbol.value) && + o.map("address", symbol.address) && + o.map("size", symbol.size) && o.map("id", symbol.id) && + o.map("type", symbol.type) && o.map("name", symbol.name); + + if (!mapped) + return false; + + if (!symbol.value && !symbol.address) { + path.report("symbol must have either a value or an address"); + return false; + } + + if (symbol.value && symbol.address) { + path.report("symbol cannot have both a value and an address"); + return false; + } + + return true; +} + +bool fromJSON(const llvm::json::Value &value, lldb::SymbolType &type, + llvm::json::Path path) { + if (auto str = value.getAsString()) { + type = llvm::StringSwitch(*str) + .Case("absolute", eSymbolTypeAbsolute) + .Case("code", eSymbolTypeCode) + .Case("resolver", eSymbolTypeResolver) + .Case("data", eSymbolTypeData) + .Case("trampoline", eSymbolTypeTrampoline) + .Case("runtime", eSymbolTypeRuntime) + .Case("exception", eSymbolTypeException) + .Case("sourcefile", eSymbolTypeSourceFile) + .Case("headerfile", eSymbolTypeHeaderFile) + .Case("objectfile", eSymbolTypeObjectFile) + .Case("commonblock", eSymbolTypeCommonBlock) + .Case("block", eSymbolTypeBlock) + .Case("local", eSymbolTypeLocal) + .Case("param", eSymbolTypeParam) + .Case("variable", eSymbolTypeVariable) + .Case("variableType", eSymbolTypeVariableType) + .Case("lineentry", eSymbolTypeLineEntry) + .Case("lineheader", eSymbolTypeLineHeader) + .Case("scopebegin", eSymbolTypeScopeBegin) + .Case("scopeend", eSymbolTypeScopeEnd) + .Case("additional,", eSymbolTypeAdditional) + .Case("compiler", eSymbolTypeCompiler) + .Case("instrumentation", eSymbolTypeInstrumentation) + .Case("undefined", eSymbolTypeUndefined) + .Case("objcclass", eSymbolTypeObjCClass) + .Case("objcmetaClass", eSymbolTypeObjCMetaClass) + .Case("objcivar", eSymbolTypeObjCIVar) + .Case("reexporte", eSymbolTypeReExported) + .Default(eSymbolTypeInvalid); + + if (type == eSymbolTypeInvalid) { + path.report("invalid symbol type"); + return false; + } + + return true; + } + path.report("expected string"); + return false; +} +} // namespace json +} // namespace llvm diff --git a/lldb/test/API/macosx/symbols/Makefile b/lldb/test/API/macosx/symbols/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/symbols/Makefile @@ -0,0 +1,8 @@ +C_SOURCES := main.c + +all: stripped.out + +stripped.out : a.out + strip a.out -o stripped.out + +include Makefile.rules diff --git a/lldb/test/API/macosx/symbols/TestSymbolFileJSON.py b/lldb/test/API/macosx/symbols/TestSymbolFileJSON.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/symbols/TestSymbolFileJSON.py @@ -0,0 +1,103 @@ +""" Testing symbol loading via JSON file. """ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TargetSymbolsFileJSON(TestBase): + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.c' + + @no_debug_info_test + def test_symbol_file_json_address(self): + """Test that 'target symbols add' can load the symbols from a JSON file using file addresses.""" + + self.build() + stripped = self.getBuildArtifact("stripped.out") + unstripped = self.getBuildArtifact("a.out") + + # Create a JSON symbol file from the unstripped target. + unstripped_target = self.dbg.CreateTarget(unstripped) + self.assertTrue(unstripped_target, VALID_TARGET) + + unstripped_module = unstripped_target.GetModuleAtIndex(0) + main_symbol = unstripped_module.FindSymbol("main") + foo_symbol = unstripped_module.FindSymbol("foo") + + data = { + "triple": unstripped_module.GetTriple(), + "uuid": unstripped_module.GetUUIDString(), + "symbols": list() + } + data['symbols'].append({ + "name": "main", + "type": "code", + "size": main_symbol.GetSize(), + "address": main_symbol.addr.GetFileAddress(), + }) + data['symbols'].append({ + "name": "foo", + "type": "code", + "size": foo_symbol.GetSize(), + "address": foo_symbol.addr.GetFileAddress(), + }) + data['symbols'].append({ + "name": "bar", + "type": "code", + "size": 0, + "value": 0xFF, + }) + + json_object = json.dumps(data, indent=4) + json_symbol_file = self.getBuildArtifact("a.json") + with open(json_symbol_file, "w") as outfile: + outfile.write(json_object) + + # Create a stripped target. + stripped_target = self.dbg.CreateTarget(stripped) + self.assertTrue(stripped_target, VALID_TARGET) + + # Ensure there's no symbol for main and foo. + stripped_module = stripped_target.GetModuleAtIndex(0) + self.assertFalse(stripped_module.FindSymbol("main").IsValid()) + self.assertFalse(stripped_module.FindSymbol("foo").IsValid()) + self.assertFalse(stripped_module.FindSymbol("bar").IsValid()) + + main_bp = stripped_target.BreakpointCreateByName( + "main", "stripped.out") + self.assertTrue(main_bp, VALID_BREAKPOINT) + self.assertEqual(main_bp.num_locations, 0) + + # Load the JSON symbol file. + self.runCmd("target symbols add -s %s %s" % + (stripped, self.getBuildArtifact("a.json"))) + + stripped_main_symbol = stripped_module.FindSymbol("main") + stripped_foo_symbol = stripped_module.FindSymbol("foo") + stripped_bar_symbol = stripped_module.FindSymbol("bar") + + # Ensure main and foo are available now. + self.assertTrue(stripped_main_symbol.IsValid()) + self.assertTrue(stripped_foo_symbol.IsValid()) + self.assertTrue(stripped_bar_symbol.IsValid()) + self.assertEqual(main_bp.num_locations, 1) + + # Ensure the file address matches between the stripped and unstripped target. + self.assertEqual(stripped_main_symbol.addr.GetFileAddress(), + main_symbol.addr.GetFileAddress()) + self.assertEqual(stripped_main_symbol.addr.GetFileAddress(), + main_symbol.addr.GetFileAddress()) + + # Ensure the size matches. + self.assertEqual(stripped_main_symbol.GetSize(), main_symbol.GetSize()) + self.assertEqual(stripped_main_symbol.GetSize(), main_symbol.GetSize()) + + # Ensure the type matches. + self.assertEqual(stripped_main_symbol.GetType(), main_symbol.GetType()) + self.assertEqual(stripped_main_symbol.GetType(), main_symbol.GetType()) + + # Ensure the bar symbol has a fixed value of 10. + self.assertEqual(stripped_bar_symbol.GetValue(), 0xFF); diff --git a/lldb/test/API/macosx/symbols/main.c b/lldb/test/API/macosx/symbols/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/symbols/main.c @@ -0,0 +1,2 @@ +int foo() { return 1; } +int main() { return foo(); } diff --git a/lldb/unittests/Symbol/CMakeLists.txt b/lldb/unittests/Symbol/CMakeLists.txt --- a/lldb/unittests/Symbol/CMakeLists.txt +++ b/lldb/unittests/Symbol/CMakeLists.txt @@ -1,4 +1,5 @@ add_lldb_unittest(SymbolTests + JSONSymbolTest.cpp LocateSymbolFileTest.cpp MangledTest.cpp PostfixExpressionTest.cpp diff --git a/lldb/unittests/Symbol/JSONSymbolTest.cpp b/lldb/unittests/Symbol/JSONSymbolTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Symbol/JSONSymbolTest.cpp @@ -0,0 +1,192 @@ +//===-- JSONSymbolTest.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 "lldb/Core/Section.h" +#include "lldb/Symbol/Symbol.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace lldb; +using namespace llvm; +using namespace lldb_private; + +static std::string g_error_no_section_list = "no section list provided"; +static std::string g_error_both_value_and_address = + "symbol cannot contain both a value and an address"; +static std::string g_error_neither_value_or_address = + "symbol must contain either a value or an address"; + +TEST(JSONSymbolTest, DeserializeCodeAddress) { + std::string text = R"( +{ + "name": "foo", + "type": "code", + "size": 32, + "address": 4096 +})"; + + Expected json = json::parse(text); + ASSERT_TRUE(static_cast(json)); + + json::Path::Root root; + JSONSymbol json_symbol; + ASSERT_TRUE(fromJSON(*json, json_symbol, root)); + + SectionSP sect_sp(new Section( + /*module_sp=*/ModuleSP(), + /*obj_file=*/nullptr, + /*sect_id=*/1, + /*name=*/ConstString(".text"), + /*sect_type=*/eSectionTypeCode, + /*file_vm_addr=*/0x1000, + /*vm_size=*/0x1000, + /*file_offset=*/0, + /*file_size=*/0, + /*log2align=*/5, + /*flags=*/0x10203040)); + SectionList sect_list; + sect_list.AddSection(sect_sp); + + Expected symbol = Symbol::FromJSON(json_symbol, §_list); + EXPECT_THAT_EXPECTED(symbol, llvm::Succeeded()); + EXPECT_EQ(symbol->GetName(), ConstString("foo")); + EXPECT_EQ(symbol->GetFileAddress(), static_cast(0x1000)); + EXPECT_EQ(symbol->GetType(), eSymbolTypeCode); +} + +TEST(JSONSymbolTest, DeserializeCodeValue) { + std::string text = R"( +{ + "name": "foo", + "type": "code", + "size": 32, + "value": 4096 +})"; + + Expected json = json::parse(text); + EXPECT_THAT_EXPECTED(json, llvm::Succeeded()); + + json::Path::Root root; + JSONSymbol json_symbol; + ASSERT_TRUE(fromJSON(*json, json_symbol, root)); + + SectionList sect_list; + + Expected symbol = Symbol::FromJSON(json_symbol, §_list); + EXPECT_THAT_EXPECTED(symbol, llvm::Succeeded()); + EXPECT_EQ(symbol->GetName(), ConstString("foo")); + EXPECT_EQ(symbol->GetRawValue(), static_cast(0x1000)); + EXPECT_EQ(symbol->GetType(), eSymbolTypeCode); +} + +TEST(JSONSymbolTest, JSONInvalidValueAndAddress) { + std::string text = R"( +{ + "name": "foo", + "type": "code", + "size": 32, + "value": 4096, + "address": 4096 +})"; + + Expected json = json::parse(text); + EXPECT_THAT_EXPECTED(json, llvm::Succeeded()); + + json::Path::Root root; + JSONSymbol json_symbol; + ASSERT_FALSE(fromJSON(*json, json_symbol, root)); +} + +TEST(JSONSymbolTest, JSONInvalidNoValueOrAddress) { + std::string text = R"( +{ + "name": "foo", + "type": "code", + "size": 32 +})"; + + Expected json = json::parse(text); + EXPECT_THAT_EXPECTED(json, llvm::Succeeded()); + + json::Path::Root root; + JSONSymbol json_symbol; + ASSERT_FALSE(fromJSON(*json, json_symbol, root)); +} + +TEST(JSONSymbolTest, JSONInvalidType) { + std::string text = R"( +{ + "name": "foo", + "type": "bogus", + "value": 4096, + "size": 32 +})"; + + Expected json = json::parse(text); + EXPECT_THAT_EXPECTED(json, llvm::Succeeded()); + + json::Path::Root root; + JSONSymbol json_symbol; + ASSERT_FALSE(fromJSON(*json, json_symbol, root)); +} + +TEST(JSONSymbolTest, SymbolInvalidNoSectionList) { + JSONSymbol json_symbol; + json_symbol.value = 0x1; + + Expected symbol = Symbol::FromJSON(json_symbol, nullptr); + EXPECT_THAT_EXPECTED(symbol, + llvm::FailedWithMessage(g_error_no_section_list)); +} + +TEST(JSONSymbolTest, SymbolInvalidValueAndAddress) { + JSONSymbol json_symbol; + json_symbol.value = 0x1; + json_symbol.address = 0x2; + + SectionList sect_list; + + Expected symbol = Symbol::FromJSON(json_symbol, §_list); + EXPECT_THAT_EXPECTED(symbol, + llvm::FailedWithMessage(g_error_both_value_and_address)); +} + +TEST(JSONSymbolTest, SymbolInvalidNoValueOrAddress) { + JSONSymbol json_symbol; + + SectionList sect_list; + + Expected symbol = Symbol::FromJSON(json_symbol, §_list); + EXPECT_THAT_EXPECTED( + symbol, llvm::FailedWithMessage(g_error_neither_value_or_address)); +} + +TEST(JSONSymbolTest, SymbolInvalidAddressNotInSection) { + JSONSymbol json_symbol; + json_symbol.address = 0x0fff; + + SectionSP sect_sp(new Section( + /*module_sp=*/ModuleSP(), + /*obj_file=*/nullptr, + /*sect_id=*/1, + /*name=*/ConstString(".text"), + /*sect_type=*/eSectionTypeCode, + /*file_vm_addr=*/0x1000, + /*vm_size=*/0x1000, + /*file_offset=*/0, + /*file_size=*/0, + /*log2align=*/5, + /*flags=*/0x10203040)); + SectionList sect_list; + sect_list.AddSection(sect_sp); + + Expected symbol = Symbol::FromJSON(json_symbol, §_list); + EXPECT_THAT_EXPECTED( + symbol, llvm::FailedWithMessage("no section found for address: 0xfff")); +}