diff --git a/lldb/include/lldb/Core/Section.h b/lldb/include/lldb/Core/Section.h --- a/lldb/include/lldb/Core/Section.h +++ b/lldb/include/lldb/Core/Section.h @@ -17,6 +17,7 @@ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" +#include "llvm/Support/JSON.h" #include #include @@ -99,6 +100,13 @@ collection m_sections; }; +struct JSONSection { + std::string name; + std::optional type; + std::optional address; + std::optional size; +}; + class Section : public std::enable_shared_from_this
, public ModuleChild, public UserID, @@ -287,4 +295,16 @@ } // namespace lldb_private +namespace llvm { +namespace json { + +bool fromJSON(const llvm::json::Value &value, + lldb_private::JSONSection §ion, llvm::json::Path path); + +bool fromJSON(const llvm::json::Value &value, lldb::SectionType &type, + llvm::json::Path path); + +} // namespace json +} // namespace llvm + #endif // LLDB_CORE_SECTION_H diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -768,6 +768,11 @@ static void format(const lldb_private::ObjectFile::Strata &strata, raw_ostream &OS, StringRef Style); }; + +namespace json { +bool fromJSON(const llvm::json::Value &value, lldb_private::ObjectFile::Type &, + llvm::json::Path path); +} // namespace json } // namespace llvm #endif // LLDB_SYMBOL_OBJECTFILE_H diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -673,3 +673,36 @@ } return debug_info_size; } + +namespace llvm { +namespace json { + +bool fromJSON(const llvm::json::Value &value, + lldb_private::JSONSection §ion, llvm::json::Path path) { + llvm::json::ObjectMapper o(value, path); + return o && o.map("name", section.name) && o.map("type", section.type) && + o.map("size", section.address) && o.map("size", section.size); +} + +bool fromJSON(const llvm::json::Value &value, lldb::SectionType &type, + llvm::json::Path path) { + if (auto str = value.getAsString()) { + type = llvm::StringSwitch(*str) + .Case("code", eSectionTypeCode) + .Case("container", eSectionTypeContainer) + .Case("data", eSectionTypeData) + .Case("debug", eSectionTypeDebug) + .Default(eSectionTypeInvalid); + + if (type == eSectionTypeInvalid) { + path.report("invalid section type"); + return false; + } + + return true; + } + path.report("expected string"); + return false; +} +} // namespace json +} // namespace llvm diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h --- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h @@ -82,7 +82,7 @@ uint32_t GetDependentModules(FileSpecList &files) override { return 0; } - Type CalculateType() override { return eTypeDebugInfo; } + Type CalculateType() override { return m_type; } Strata CalculateStrata() override { return eStrataUser; } @@ -92,21 +92,27 @@ struct Header { std::string triple; std::string uuid; + std::optional type; }; struct Body { + std::vector sections; std::vector symbols; }; private: ArchSpec m_arch; UUID m_uuid; + ObjectFile::Type m_type; + std::optional m_size; std::vector m_symbols; + std::vector m_sections; 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); + UUID uuid, Type type, std::vector symbols, + std::vector sections); }; bool fromJSON(const llvm::json::Value &value, ObjectFileJSON::Header &header, diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp --- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp @@ -49,6 +49,7 @@ if (!MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) return nullptr; + // Update the data to contain the entire file if it doesn't already. if (data_sp->GetByteSize() < length) { data_sp = MapFileData(*file, length, file_offset); if (!data_sp) @@ -56,30 +57,41 @@ data_offset = 0; } + Log *log = GetLog(LLDBLog::Symbols); + auto text = llvm::StringRef(reinterpret_cast(data_sp->GetBytes())); Expected json = json::parse(text); if (!json) { - llvm::consumeError(json.takeError()); + LLDB_LOG_ERROR(log, json.takeError(), + "failed to parse JSON object file: {0}"); return nullptr; } json::Path::Root root; Header header; - if (!fromJSON(*json, header, root)) + if (!fromJSON(*json, header, root)) { + LLDB_LOG_ERROR(log, root.getError(), + "failed to parse JSON object file header: {0}"); return nullptr; + } ArchSpec arch(header.triple); UUID uuid; uuid.SetFromStringRef(header.uuid); + Type type = header.type.value_or(eTypeDebugInfo); Body body; - fromJSON(*json, body, root); + if (!fromJSON(*json, body, root)) { + LLDB_LOG_ERROR(log, root.getError(), + "failed to parse JSON object file body: {0}"); + return nullptr; + } return new ObjectFileJSON(module_sp, data_sp, data_offset, file, file_offset, - length, std::move(arch), std::move(uuid), - std::move(body.symbols)); + length, std::move(arch), std::move(uuid), type, + std::move(body.symbols), std::move(body.sections)); } ObjectFile *ObjectFileJSON::CreateMemoryInstance(const ModuleSP &module_sp, @@ -92,23 +104,36 @@ 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; + // Update the data to contain the entire file if it doesn't already. + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(file, length, file_offset); + if (!data_sp) + return 0; + data_offset = 0; + } + + Log *log = GetLog(LLDBLog::Symbols); + auto text = llvm::StringRef(reinterpret_cast(data_sp->GetBytes())); Expected json = json::parse(text); if (!json) { - llvm::consumeError(json.takeError()); + LLDB_LOG_ERROR(log, json.takeError(), + "failed to parse JSON object file: {0}"); return 0; } json::Path::Root root; Header header; - if (!fromJSON(*json, header, root)) + if (!fromJSON(*json, header, root)) { + LLDB_LOG_ERROR(log, root.getError(), + "failed to parse JSON object file header: {0}"); return 0; + } ArchSpec arch(header.triple); UUID uuid; @@ -123,10 +148,12 @@ 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) + UUID uuid, Type type, + std::vector symbols, + std::vector sections) : 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)) {} + m_arch(std::move(arch)), m_uuid(std::move(uuid)), m_type(type), + m_symbols(std::move(symbols)), m_sections(std::move(sections)) {} bool ObjectFileJSON::ParseHeader() { // We already parsed the header during initialization. @@ -139,7 +166,7 @@ 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"); + LLDB_LOG_ERROR(log, symbol.takeError(), "invalid symbol: {0}"); continue; } symtab.AddSymbol(*symbol); @@ -147,7 +174,21 @@ symtab.Finalize(); } -void ObjectFileJSON::CreateSections(SectionList &unified_section_list) {} +void ObjectFileJSON::CreateSections(SectionList &unified_section_list) { + if (m_sections_up) + return; + m_sections_up = std::make_unique(); + + lldb::user_id_t id = 1; + for (const auto §ion : m_sections) { + auto section_sp = std::make_shared
( + GetModule(), this, id++, ConstString(section.name), + section.type.value_or(eSectionTypeCode), 0, section.size.value_or(0), 0, + section.size.value_or(0), /*log2align*/ 0, /*flags*/ 0); + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); + } +} bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp, lldb::addr_t data_offset, @@ -164,13 +205,15 @@ 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); + return o && o.map("triple", header.triple) && o.map("uuid", header.uuid) && + o.map("type", header.type); } bool fromJSON(const json::Value &value, ObjectFileJSON::Body &body, json::Path path) { json::ObjectMapper o(value, path); - return o && o.map("symbols", body.symbols); + return o && o.mapOptional("symbols", body.symbols) && + o.mapOptional("sections", body.sections); } } // namespace lldb_private diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp --- a/lldb/source/Symbol/ObjectFile.cpp +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -761,3 +761,34 @@ m_cache_hash = llvm::djbHash(strm.GetString()); return *m_cache_hash; } + +namespace llvm { +namespace json { + +bool fromJSON(const llvm::json::Value &value, + lldb_private::ObjectFile::Type &type, llvm::json::Path path) { + if (auto str = value.getAsString()) { + type = llvm::StringSwitch(*str) + .Case("corefile", ObjectFile::eTypeCoreFile) + .Case("executable", ObjectFile::eTypeExecutable) + .Case("debuginfo", ObjectFile::eTypeDebugInfo) + .Case("dynamiclinker", ObjectFile::eTypeDynamicLinker) + .Case("objectfile", ObjectFile::eTypeObjectFile) + .Case("sharedlibrary", ObjectFile::eTypeSharedLibrary) + .Case("stublibrary", ObjectFile::eTypeStubLibrary) + .Case("jit", ObjectFile::eTypeJIT) + .Case("unknown", ObjectFile::eTypeUnknown) + .Default(ObjectFile::eTypeInvalid); + + if (type == ObjectFile::eTypeInvalid) { + path.report("invalid object type"); + return false; + } + + return true; + } + path.report("expected string"); + return false; +} +} // namespace json +} // namespace llvm diff --git a/lldb/test/API/macosx/symbols/TestObjectFileJSON.py b/lldb/test/API/macosx/symbols/TestObjectFileJSON.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/symbols/TestObjectFileJSON.py @@ -0,0 +1,100 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +import json +import uuid +import os +import shutil + + +class TestObjectFileJSON(TestBase): + TRIPLE = "arm64-apple-macosx13.0.0" + + def setUp(self): + TestBase.setUp(self) + self.source = "main.c" + + def emitJSON(self, data, path): + json_object = json.dumps(data, indent=4) + json_object_file = self.getBuildArtifact("a.json") + with open(json_object_file, "w") as outfile: + outfile.write(json_object) + + def toModuleSpec(self, path): + module_spec = lldb.SBModuleSpec() + module_spec.SetFileSpec(lldb.SBFileSpec(path)) + return module_spec + + @no_debug_info_test + def test_target(self): + data = { + "triple": self.TRIPLE, + "uuid": str(uuid.uuid4()), + "type": "executable", + } + + json_object_file = self.getBuildArtifact("a.json") + self.emitJSON(data, json_object_file) + + target = self.dbg.CreateTarget(json_object_file) + self.assertTrue(target.IsValid()) + self.assertEqual(target.GetTriple(), self.TRIPLE) + + @no_debug_info_test + def test_module(self): + self.build() + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + + data = { + "triple": self.TRIPLE, + "uuid": str(uuid.uuid4()), + } + + json_object_file = self.getBuildArtifact("a.json") + self.emitJSON(data, json_object_file) + + module = target.AddModule(self.toModuleSpec(json_object_file)) + self.assertFalse(module.IsValid()) + + data = { + "triple": self.TRIPLE, + "uuid": str(uuid.uuid4()), + "type": "sharedlibrary", + "sections": [ + { + "name": "__TEXT", + "type": "code", + "address": 0, + "size": 0x222, + } + ], + "symbols": [ + { + "name": "foo", + "address": 0x100, + "size": 0x11, + } + ], + } + self.emitJSON(data, json_object_file) + + module = target.AddModule(self.toModuleSpec(json_object_file)) + self.assertTrue(module.IsValid()) + + section = module.GetSectionAtIndex(0) + self.assertTrue(section.IsValid()) + self.assertEqual(section.GetName(), "__TEXT") + self.assertEqual(section.file_addr, 0x0) + self.assertEqual(section.size, 0x222) + + symbol = module.FindSymbol("foo") + self.assertTrue(symbol.IsValid()) + self.assertEqual(symbol.addr.GetFileAddress(), 0x100) + self.assertEqual(symbol.GetSize(), 0x11) + + error = target.SetSectionLoadAddress(section, 0x1000) + self.assertSuccess(error) + self.assertEqual(symbol.addr.GetLoadAddress(target), 0x1100)