| 1 | //===-- ObjectFileJSON.cpp ------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "Plugins/ObjectFile/JSON/ObjectFileJSON.h" |
| 10 | #include "lldb/Core/Module.h" |
| 11 | #include "lldb/Core/ModuleSpec.h" |
| 12 | #include "lldb/Core/PluginManager.h" |
| 13 | #include "lldb/Core/Section.h" |
| 14 | #include "lldb/Symbol/Symbol.h" |
| 15 | #include "lldb/Utility/LLDBLog.h" |
| 16 | #include "lldb/Utility/Log.h" |
| 17 | #include "llvm/ADT/DenseSet.h" |
| 18 | #include <optional> |
| 19 | |
| 20 | using namespace llvm; |
| 21 | using namespace lldb; |
| 22 | using namespace lldb_private; |
| 23 | |
| 24 | LLDB_PLUGIN_DEFINE(ObjectFileJSON) |
| 25 | |
| 26 | char ObjectFileJSON::ID; |
| 27 | |
| 28 | void ObjectFileJSON::Initialize() { |
| 29 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
| 30 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance, |
| 31 | create_memory_callback: CreateMemoryInstance, get_module_specifications: GetModuleSpecifications); |
| 32 | } |
| 33 | |
| 34 | void ObjectFileJSON::Terminate() { |
| 35 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
| 36 | } |
| 37 | |
| 38 | ObjectFile * |
| 39 | ObjectFileJSON::CreateInstance(const ModuleSP &module_sp, DataBufferSP data_sp, |
| 40 | offset_t data_offset, const FileSpec *file, |
| 41 | offset_t file_offset, offset_t length) { |
| 42 | if (!data_sp) { |
| 43 | data_sp = MapFileData(file: *file, Size: length, Offset: file_offset); |
| 44 | if (!data_sp) |
| 45 | return nullptr; |
| 46 | data_offset = 0; |
| 47 | } |
| 48 | |
| 49 | if (!MagicBytesMatch(data_sp, offset: 0, length: data_sp->GetByteSize())) |
| 50 | return nullptr; |
| 51 | |
| 52 | // Update the data to contain the entire file if it doesn't already. |
| 53 | if (data_sp->GetByteSize() < length) { |
| 54 | data_sp = MapFileData(file: *file, Size: length, Offset: file_offset); |
| 55 | if (!data_sp) |
| 56 | return nullptr; |
| 57 | data_offset = 0; |
| 58 | } |
| 59 | |
| 60 | Log *log = GetLog(mask: LLDBLog::Symbols); |
| 61 | |
| 62 | auto text = |
| 63 | llvm::StringRef(reinterpret_cast<const char *>(data_sp->GetBytes())); |
| 64 | |
| 65 | Expected<json::Value> json = json::parse(JSON: text); |
| 66 | if (!json) { |
| 67 | LLDB_LOG_ERROR(log, json.takeError(), |
| 68 | "failed to parse JSON object file: {0}" ); |
| 69 | return nullptr; |
| 70 | } |
| 71 | |
| 72 | json::Path::Root root; |
| 73 | Header ; |
| 74 | if (!fromJSON(value: *json, header, path: root)) { |
| 75 | LLDB_LOG_ERROR(log, root.getError(), |
| 76 | "failed to parse JSON object file header: {0}" ); |
| 77 | return nullptr; |
| 78 | } |
| 79 | |
| 80 | ArchSpec arch(header.triple); |
| 81 | UUID uuid; |
| 82 | uuid.SetFromStringRef(header.uuid); |
| 83 | Type type = header.type.value_or(u: eTypeDebugInfo); |
| 84 | |
| 85 | Body body; |
| 86 | if (!fromJSON(value: *json, body, path: root)) { |
| 87 | LLDB_LOG_ERROR(log, root.getError(), |
| 88 | "failed to parse JSON object file body: {0}" ); |
| 89 | return nullptr; |
| 90 | } |
| 91 | |
| 92 | return new ObjectFileJSON(module_sp, data_sp, data_offset, file, file_offset, |
| 93 | length, std::move(arch), std::move(uuid), type, |
| 94 | std::move(body.symbols), std::move(body.sections)); |
| 95 | } |
| 96 | |
| 97 | ObjectFile *ObjectFileJSON::CreateMemoryInstance(const ModuleSP &module_sp, |
| 98 | WritableDataBufferSP data_sp, |
| 99 | const ProcessSP &process_sp, |
| 100 | addr_t ) { |
| 101 | return nullptr; |
| 102 | } |
| 103 | |
| 104 | size_t ObjectFileJSON::GetModuleSpecifications( |
| 105 | const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset, |
| 106 | offset_t file_offset, offset_t length, ModuleSpecList &specs) { |
| 107 | if (!MagicBytesMatch(data_sp, offset: data_offset, length: data_sp->GetByteSize())) |
| 108 | return 0; |
| 109 | |
| 110 | // Update the data to contain the entire file if it doesn't already. |
| 111 | if (data_sp->GetByteSize() < length) { |
| 112 | data_sp = MapFileData(file, Size: length, Offset: file_offset); |
| 113 | if (!data_sp) |
| 114 | return 0; |
| 115 | data_offset = 0; |
| 116 | } |
| 117 | |
| 118 | Log *log = GetLog(mask: LLDBLog::Symbols); |
| 119 | |
| 120 | auto text = |
| 121 | llvm::StringRef(reinterpret_cast<const char *>(data_sp->GetBytes())); |
| 122 | |
| 123 | Expected<json::Value> json = json::parse(JSON: text); |
| 124 | if (!json) { |
| 125 | LLDB_LOG_ERROR(log, json.takeError(), |
| 126 | "failed to parse JSON object file: {0}" ); |
| 127 | return 0; |
| 128 | } |
| 129 | |
| 130 | json::Path::Root root; |
| 131 | Header ; |
| 132 | if (!fromJSON(value: *json, header, path: root)) { |
| 133 | LLDB_LOG_ERROR(log, root.getError(), |
| 134 | "failed to parse JSON object file header: {0}" ); |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | ArchSpec arch(header.triple); |
| 139 | UUID uuid; |
| 140 | uuid.SetFromStringRef(header.uuid); |
| 141 | |
| 142 | ModuleSpec spec(file, std::move(arch)); |
| 143 | spec.GetUUID() = std::move(uuid); |
| 144 | specs.Append(spec); |
| 145 | return 1; |
| 146 | } |
| 147 | |
| 148 | ObjectFileJSON::ObjectFileJSON(const ModuleSP &module_sp, DataBufferSP &data_sp, |
| 149 | offset_t data_offset, const FileSpec *file, |
| 150 | offset_t offset, offset_t length, ArchSpec arch, |
| 151 | UUID uuid, Type type, |
| 152 | std::vector<JSONSymbol> symbols, |
| 153 | std::vector<JSONSection> sections) |
| 154 | : ObjectFile(module_sp, file, offset, length, data_sp, data_offset), |
| 155 | m_arch(std::move(arch)), m_uuid(std::move(uuid)), m_type(type), |
| 156 | m_symbols(std::move(symbols)), m_sections(std::move(sections)) {} |
| 157 | |
| 158 | bool ObjectFileJSON::() { |
| 159 | // We already parsed the header during initialization. |
| 160 | return true; |
| 161 | } |
| 162 | |
| 163 | void ObjectFileJSON::ParseSymtab(Symtab &symtab) { |
| 164 | Log *log = GetLog(mask: LLDBLog::Symbols); |
| 165 | SectionList *section_list = GetModule()->GetSectionList(); |
| 166 | for (JSONSymbol json_symbol : m_symbols) { |
| 167 | llvm::Expected<Symbol> symbol = Symbol::FromJSON(symbol: json_symbol, section_list); |
| 168 | if (!symbol) { |
| 169 | LLDB_LOG_ERROR(log, symbol.takeError(), "invalid symbol: {0}" ); |
| 170 | continue; |
| 171 | } |
| 172 | symtab.AddSymbol(symbol: *symbol); |
| 173 | } |
| 174 | symtab.Finalize(); |
| 175 | } |
| 176 | |
| 177 | void ObjectFileJSON::CreateSections(SectionList &unified_section_list) { |
| 178 | if (m_sections_up) |
| 179 | return; |
| 180 | m_sections_up = std::make_unique<SectionList>(); |
| 181 | |
| 182 | lldb::user_id_t id = 0; |
| 183 | for (const auto &json_section : m_sections) { |
| 184 | auto make_section = [this, &id](const JSONSection §ion, |
| 185 | SectionSP parent_section_sp = |
| 186 | nullptr) -> SectionSP { |
| 187 | SectionSP section_sp; |
| 188 | auto sect_id = section.user_id.value_or(u: id + 1); |
| 189 | if (!section.user_id.has_value()) |
| 190 | ++id; |
| 191 | const auto name = ConstString(section.name); |
| 192 | const auto sect_type = section.type.value_or(u: eSectionTypeCode); |
| 193 | const auto vm_addr = section.address.value_or(u: 0); |
| 194 | const auto vm_size = section.size.value_or(u: 0); |
| 195 | const auto file_offset = section.file_offset.value_or(u: 0); |
| 196 | const auto file_size = section.file_size.value_or(u: 0); |
| 197 | const auto log2align = section.log2align.value_or(u: 0); |
| 198 | const auto flags = section.flags.value_or(u: 0); |
| 199 | if (parent_section_sp) { |
| 200 | section_sp = std::make_shared<Section>( |
| 201 | args&: parent_section_sp, args: GetModule(), args: this, args&: sect_id, args: name, args: sect_type, |
| 202 | args: vm_addr - parent_section_sp->GetFileAddress(), args: vm_size, args: file_offset, |
| 203 | args: file_size, args: log2align, args: flags); |
| 204 | |
| 205 | } else { |
| 206 | section_sp = std::make_shared<Section>( |
| 207 | args: GetModule(), args: this, args&: sect_id, args: name, args: sect_type, args: vm_addr, args: vm_size, |
| 208 | args: file_offset, args: file_size, args: log2align, args: flags); |
| 209 | } |
| 210 | // Set permissions |
| 211 | uint32_t permissions = 0; |
| 212 | if (section.read.value_or(u: 0)) |
| 213 | permissions |= lldb::ePermissionsReadable; |
| 214 | if (section.write.value_or(u: 0)) |
| 215 | permissions |= lldb::ePermissionsWritable; |
| 216 | if (section.execute.value_or(u: 0)) |
| 217 | permissions |= lldb::ePermissionsExecutable; |
| 218 | if (permissions) |
| 219 | section_sp->SetPermissions(permissions); |
| 220 | section_sp->SetIsFake(section.fake.value_or(u: false)); |
| 221 | section_sp->SetIsEncrypted(section.encrypted.value_or(u: false)); |
| 222 | section_sp->SetIsThreadSpecific(section.thread_specific.value_or(u: false)); |
| 223 | return section_sp; |
| 224 | }; |
| 225 | auto section_sp = make_section(json_section); |
| 226 | for (const auto &subsection : json_section.subsections) { |
| 227 | SectionSP subsection_sp = make_section(subsection, section_sp); |
| 228 | section_sp->GetChildren().AddSection(section_sp: subsection_sp); |
| 229 | } |
| 230 | |
| 231 | m_sections_up->AddSection(section_sp); |
| 232 | unified_section_list.AddSection(section_sp); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp, |
| 237 | lldb::addr_t data_offset, |
| 238 | lldb::addr_t data_length) { |
| 239 | DataExtractor data; |
| 240 | data.SetData(data_sp, offset: data_offset, length: data_length); |
| 241 | lldb::offset_t offset = 0; |
| 242 | uint32_t magic = data.GetU8(offset_ptr: &offset); |
| 243 | return magic == '{'; |
| 244 | } |
| 245 | |
| 246 | namespace lldb_private { |
| 247 | |
| 248 | bool (const json::Value &value, ObjectFileJSON::Header &, |
| 249 | json::Path path) { |
| 250 | json::ObjectMapper o(value, path); |
| 251 | return o && o.map(Prop: "triple" , Out&: header.triple) && o.map(Prop: "uuid" , Out&: header.uuid) && |
| 252 | o.map(Prop: "type" , Out&: header.type); |
| 253 | } |
| 254 | |
| 255 | bool fromJSON(const json::Value &value, ObjectFileJSON::Body &body, |
| 256 | json::Path path) { |
| 257 | json::ObjectMapper o(value, path); |
| 258 | return o && o.mapOptional(Prop: "symbols" , Out&: body.symbols) && |
| 259 | o.mapOptional(Prop: "sections" , Out&: body.sections); |
| 260 | } |
| 261 | |
| 262 | } // namespace lldb_private |
| 263 | |