Index: lldb/source/Plugins/ObjectFile/CMakeLists.txt =================================================================== --- lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ 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) Index: lldb/source/Plugins/ObjectFile/JSON/CMakeLists.txt =================================================================== --- /dev/null +++ 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 + ) Index: lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h @@ -0,0 +1,129 @@ +//===-- 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 Symbol { + uint64_t addr; + std::string name; + }; + + struct Symbols { + std::vector symbols; + }; + + std::vector GetSymbols() { return m_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::Symbol &symbol, + llvm::json::Path path); + +bool fromJSON(const llvm::json::Value &value, ObjectFileJSON::Symbols &symbols, + llvm::json::Path path); + +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H Index: lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp @@ -0,0 +1,168 @@ +//===-- 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/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.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); + + json::Path::Root symbols_path("header"); + Symbols symbols; + fromJSON(*json, symbols, symbols_path); + + return new ObjectFileJSON(module_sp, data_sp, data_offset, file, file_offset, + length, std::move(arch), std::move(uuid), + std::move(symbols.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) { + // Nothing to do for JSON files, all information is parsed as debug info. +} + +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::Symbol &symbol, + json::Path path) { + json::ObjectMapper o(value, path); + return o && o.map("addr", symbol.addr) && o.map("name", symbol.name); +} + +bool fromJSON(const json::Value &value, ObjectFileJSON::Symbols &symbols, + json::Path path) { + json::ObjectMapper o(value, path); + return o && o.map("symbols", symbols.symbols); +} + +} // namespace lldb_private Index: lldb/source/Plugins/SymbolFile/CMakeLists.txt =================================================================== --- lldb/source/Plugins/SymbolFile/CMakeLists.txt +++ 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) Index: lldb/source/Plugins/SymbolFile/JSON/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/JSON/CMakeLists.txt @@ -0,0 +1,7 @@ +add_lldb_library(lldbPluginSymbolFileJSON PLUGIN + SymbolFileJSON.cpp + + LINK_LIBS + lldbCore + lldbSymbol + ) Index: lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h =================================================================== --- /dev/null +++ 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 Index: lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp @@ -0,0 +1,120 @@ +//===-- 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) { + auto json_object_file = dyn_cast_or_null(m_objfile_sp.get()); + if (!json_object_file) + return; + + Log *log = GetLog(LLDBLog::Symbols); + Module &module = *m_objfile_sp->GetModule(); + const SectionList &list = *module.GetSectionList(); + + llvm::DenseSet found_symbol_addresses; + + for (ObjectFileJSON::Symbol symbol : json_object_file->GetSymbols()) { + SectionSP section_sp = list.FindSectionContainingFileAddress(symbol.addr); + if (!section_sp) { + LLDB_LOG(log, + "Ignoring symbol '{0}', whose address ({1:x}) is outside of the " + "object file. Mismatched symbol file?", + symbol.name, symbol.addr); + continue; + } + // Keep track of what addresses were already added so far and only add + // the symbol with the first address. + if (!found_symbol_addresses.insert(symbol.addr).second) + continue; + symtab.AddSymbol(Symbol( + /*symID*/ 0, Mangled(symbol.name), eSymbolTypeCode, + /*is_global*/ true, /*is_debug*/ false, + /*is_trampoline*/ false, /*is_artificial*/ false, + AddressRange(section_sp, symbol.addr - section_sp->GetFileAddress(), 0), + false, /*contains_linker_annotations*/ false, /*flags*/ 0)); + } + + symtab.Finalize(); +} Index: lldb/test/API/macosx/symbols/Makefile =================================================================== --- /dev/null +++ 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 Index: lldb/test/API/macosx/symbols/TestSymbolFileJSON.py =================================================================== --- /dev/null +++ lldb/test/API/macosx/symbols/TestSymbolFileJSON.py @@ -0,0 +1,80 @@ +""" 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 # Prevent the genaration of the dwarf version of this test + def test_symbol_file_json(self): + """Test that 'target symbols add' can load the symbols from a JSON file.""" + + 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) + + data = { + "triple": unstripped_module.GetTriple(), + "uuid": unstripped_module.GetUUIDString(), + "symbols": list() + } + data['symbols'].append({ + "name": + "main", + "addr": + unstripped_module.FindSymbol("main").addr.GetFileAddress() + }) + data['symbols'].append({ + "name": + "foo", + "addr": + unstripped_module.FindSymbol("foo").addr.GetFileAddress() + }) + + 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()) + + 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"))) + + # Ensure main and foo are available now. + self.assertTrue(stripped_module.FindSymbol("main").IsValid()) + self.assertTrue(stripped_module.FindSymbol("foo").IsValid()) + self.assertEqual(main_bp.num_locations, 1) + + # Ensure the file address matches between the stripped and unstripped target. + self.assertEqual( + stripped_module.FindSymbol("main").addr.GetFileAddress(), + unstripped_module.FindSymbol("main").addr.GetFileAddress()) + self.assertEqual( + stripped_module.FindSymbol("foo").addr.GetFileAddress(), + unstripped_module.FindSymbol("foo").addr.GetFileAddress()) Index: lldb/test/API/macosx/symbols/main.c =================================================================== --- /dev/null +++ lldb/test/API/macosx/symbols/main.c @@ -0,0 +1,2 @@ +int foo() { return 1; } +int main() { return foo(); }