| //===-- 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/Target/Target.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include <optional> | |
| using namespace llvm; | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| LLDB_PLUGIN_DEFINE(ObjectFileJSON) | |
| char ObjectFileJSON::ID; | |
| /// Returns the JSON text in the buffer, which is not null-terminated and may | |
| /// be zero padded past the end of the file. | |
| static StringRef GetText(const lldb_private::DataExtractor &data) { | |
| StringRef text = toStringRef(data.GetData()); | |
| return text.substr(0, text.find('\0')); | |
| } | |
| void ObjectFileJSON::Initialize() { | |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), | |
| GetPluginDescriptionStatic(), CreateInstance, | |
| CreateMemoryInstance, GetModuleSpecifications); | |
| } | |
| void ObjectFileJSON::Terminate() { | |
| PluginManager::UnregisterPlugin(CreateInstance); | |
| } | |
| ObjectFile *ObjectFileJSON::CreateInstance(const ModuleSP &module_sp, | |
| DataExtractorSP extractor_sp, | |
| offset_t data_offset, | |
| const FileSpec *file, | |
| offset_t file_offset, | |
| offset_t length) { | |
| if (!extractor_sp || !extractor_sp->HasData()) { | |
| DataBufferSP data_sp = MapFileData(*file, length, file_offset); | |
| if (!data_sp) | |
| return nullptr; | |
| extractor_sp = std::make_shared<DataExtractor>(data_sp); | |
| data_offset = 0; | |
| } | |
| if (!extractor_sp->HasData()) | |
| return nullptr; | |
| if (!MagicBytesMatch(extractor_sp->GetSubsetExtractorSP(data_offset))) | |
| return nullptr; | |
| // Update the data to contain the entire file if it doesn't already. | |
| if (extractor_sp->GetByteSize() < length) { | |
| DataBufferSP data_sp = MapFileData(*file, length, file_offset); | |
| if (!data_sp) | |
| return nullptr; | |
| extractor_sp->SetData(data_sp); | |
| data_offset = 0; | |
| } | |
| Log *log = GetLog(LLDBLog::Symbols); | |
| StringRef text = GetText(*extractor_sp); | |
| Expected<json::Value> json = json::parse(text); | |
| if (!json) { | |
| 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)) { | |
| 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; | |
| 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, extractor_sp, data_offset, file, | |
| file_offset, length, std::move(arch), | |
| std::move(uuid), type, std::move(body.symbols), | |
| std::move(body.sections)); | |
| } | |
| ObjectFile *ObjectFileJSON::CreateMemoryInstance(const ModuleSP &module_sp, | |
| WritableDataBufferSP data_sp, | |
| const ProcessSP &process_sp, | |
| addr_t header_addr) { | |
| return nullptr; | |
| } | |
| ModuleSpecList | |
| ObjectFileJSON::GetModuleSpecifications(const FileSpec &file, | |
| DataExtractorSP &extractor_sp, | |
| offset_t file_offset, offset_t length) { | |
| if (!extractor_sp || !MagicBytesMatch(extractor_sp)) | |
| return {}; | |
| // Update the data to contain the entire file if it doesn't already. | |
| if (extractor_sp->GetByteSize() < length) { | |
| if (DataBufferSP file_data_sp = MapFileData(file, length, file_offset)) | |
| extractor_sp->SetData(file_data_sp); | |
| if (!extractor_sp->HasData()) | |
| return {}; | |
| } | |
| Log *log = GetLog(LLDBLog::Symbols); | |
| StringRef text = GetText(*extractor_sp); | |
| Expected<json::Value> json = json::parse(text); | |
| if (!json) { | |
| LLDB_LOG_ERROR(log, json.takeError(), | |
| "failed to parse JSON object file: {0}"); | |
| return {}; | |
| } | |
| json::Path::Root root; | |
| Header header; | |
| if (!fromJSON(*json, header, root)) { | |
| LLDB_LOG_ERROR(log, root.getError(), | |
| "failed to parse JSON object file header: {0}"); | |
| return {}; | |
| } | |
| ArchSpec arch(header.triple); | |
| UUID uuid; | |
| uuid.SetFromStringRef(header.uuid); | |
| ModuleSpec spec(file, std::move(arch)); | |
| spec.GetUUID() = std::move(uuid); | |
| ModuleSpecList specs; | |
| specs.Append(spec); | |
| return specs; | |
| } | |
| ObjectFileJSON::ObjectFileJSON(const ModuleSP &module_sp, | |
| DataExtractorSP extractor_sp, | |
| offset_t data_offset, const FileSpec *file, | |
| offset_t offset, offset_t length, ArchSpec arch, | |
| UUID uuid, Type type, | |
| std::vector<JSONSymbol> symbols, | |
| std::vector<JSONSection> sections) | |
| : ObjectFile(module_sp, file, offset, length, extractor_sp, data_offset), | |
| 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. | |
| 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 = Symbol::FromJSON(json_symbol, section_list); | |
| if (!symbol) { | |
| LLDB_LOG_ERROR(log, symbol.takeError(), "invalid symbol: {0}"); | |
| continue; | |
| } | |
| symtab.AddSymbol(*symbol); | |
| } | |
| symtab.Finalize(); | |
| } | |
| void ObjectFileJSON::CreateSections(SectionList &unified_section_list) { | |
| if (m_sections_up) | |
| return; | |
| m_sections_up = std::make_unique<SectionList>(); | |
| lldb::user_id_t id = 0; | |
| for (const auto &json_section : m_sections) { | |
| auto make_section = [this, &id](const JSONSection §ion, | |
| SectionSP parent_section_sp = | |
| nullptr) -> SectionSP { | |
| SectionSP section_sp; | |
| auto sect_id = section.user_id.value_or(id + 1); | |
| if (!section.user_id.has_value()) | |
| ++id; | |
| const auto name = section.name; | |
| const auto sect_type = section.type.value_or(eSectionTypeCode); | |
| const auto vm_addr = section.address.value_or(0); | |
| const auto vm_size = section.size.value_or(0); | |
| const auto file_offset = section.file_offset.value_or(0); | |
| const auto file_size = section.file_size.value_or(0); | |
| const auto log2align = section.log2align.value_or(0); | |
| const auto flags = section.flags.value_or(0); | |
| if (parent_section_sp) { | |
| section_sp = std::make_shared<Section>( | |
| parent_section_sp, GetModule(), this, sect_id, name, sect_type, | |
| vm_addr - parent_section_sp->GetFileAddress(), vm_size, file_offset, | |
| file_size, log2align, flags); | |
| } else { | |
| section_sp = std::make_shared<Section>( | |
| GetModule(), this, sect_id, name, sect_type, vm_addr, vm_size, | |
| file_offset, file_size, log2align, flags); | |
| } | |
| // Set permissions | |
| uint32_t permissions = 0; | |
| if (section.read.value_or(0)) | |
| permissions |= lldb::ePermissionsReadable; | |
| if (section.write.value_or(0)) | |
| permissions |= lldb::ePermissionsWritable; | |
| if (section.execute.value_or(0)) | |
| permissions |= lldb::ePermissionsExecutable; | |
| if (permissions) | |
| section_sp->SetPermissions(permissions); | |
| section_sp->SetIsFake(section.fake.value_or(false)); | |
| section_sp->SetIsEncrypted(section.encrypted.value_or(false)); | |
| section_sp->SetIsThreadSpecific(section.thread_specific.value_or(false)); | |
| return section_sp; | |
| }; | |
| auto section_sp = make_section(json_section); | |
| for (const auto &subsection : json_section.subsections) { | |
| SectionSP subsection_sp = make_section(subsection, section_sp); | |
| section_sp->GetChildren().AddSection(subsection_sp); | |
| } | |
| m_sections_up->AddSection(section_sp); | |
| unified_section_list.AddSection(section_sp); | |
| } | |
| } | |
| bool ObjectFileJSON::SetLoadAddress(Target &target, lldb::addr_t value, | |
| bool value_is_offset) { | |
| Log *log(GetLog(LLDBLog::DynamicLoader)); | |
| if (!m_sections_up) | |
| return true; | |
| addr_t slide = value; | |
| if (!value_is_offset) { | |
| addr_t lowest_addr = LLDB_INVALID_ADDRESS; | |
| for (const SectionSP §ion_sp : *m_sections_up) { | |
| addr_t section_load_addr = section_sp->GetFileAddress(); | |
| lowest_addr = std::min(lowest_addr, section_load_addr); | |
| } | |
| if (lowest_addr == LLDB_INVALID_ADDRESS) | |
| return false; | |
| slide = value - lowest_addr; | |
| } | |
| // Apply slide to each section's file address. | |
| for (const SectionSP §ion_sp : *m_sections_up) { | |
| addr_t section_load_addr = section_sp->GetFileAddress(); | |
| if (section_load_addr != LLDB_INVALID_ADDRESS) { | |
| LLDB_LOG(log, | |
| "ObjectFileJSON::SetLoadAddress section {0} to load addr {1:x}", | |
| section_sp->GetName(), section_load_addr + slide); | |
| target.SetSectionLoadAddress(section_sp, section_load_addr + slide, | |
| /*warn_multiple=*/true); | |
| } | |
| } | |
| return true; | |
| } | |
| bool ObjectFileJSON::MagicBytesMatch(DataExtractorSP extractor_sp) { | |
| lldb::offset_t offset = 0; | |
| char magic = extractor_sp->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) && | |
| 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.mapOptional("symbols", body.symbols) && | |
| o.mapOptional("sections", body.sections); | |
| } | |
| } // namespace lldb_private |