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
20using namespace llvm;
21using namespace lldb;
22using namespace lldb_private;
23
24LLDB_PLUGIN_DEFINE(ObjectFileJSON)
25
26char ObjectFileJSON::ID;
27
28void ObjectFileJSON::Initialize() {
29  PluginManager::RegisterPlugin(GetPluginNameStatic(),
30                                GetPluginDescriptionStatic(), CreateInstance,
31                                CreateMemoryInstance, GetModuleSpecifications);
32}
33
34void ObjectFileJSON::Terminate() {
35  PluginManager::UnregisterPlugin(CreateInstance);
36}
37
38ObjectFile *
39ObjectFileJSON::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, length, file_offset);
44    if (!data_sp)
45      return nullptr;
46    data_offset = 0;
47  }
48
49  if (!MagicBytesMatch(data_sp, 0, 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, length, file_offset);
55    if (!data_sp)
56      return nullptr;
57    data_offset = 0;
58  }
59
60  Log *log = GetLog(LLDBLog::Symbols);
61
62  auto text =
63      llvm::StringRef(reinterpret_cast<const char *>(data_sp->GetBytes()));
64
65  Expected<json::Value> json = json::parse(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 header;
74  if (!fromJSON(*json, header, 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(eTypeDebugInfo);
84
85  Body body;
86  if (!fromJSON(*json, body, 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
97ObjectFile *ObjectFileJSON::CreateMemoryInstance(const ModuleSP &module_sp,
98                                                 WritableDataBufferSP data_sp,
99                                                 const ProcessSP &process_sp,
100                                                 addr_t header_addr) {
101  return nullptr;
102}
103
104size_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, data_offset, 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, length, file_offset);
113    if (!data_sp)
114      return 0;
115    data_offset = 0;
116  }
117
118  Log *log = GetLog(LLDBLog::Symbols);
119
120  auto text =
121      llvm::StringRef(reinterpret_cast<const char *>(data_sp->GetBytes()));
122
123  Expected<json::Value> json = json::parse(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 header;
132  if (!fromJSON(*json, header, 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
148ObjectFileJSON::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
158bool ObjectFileJSON::ParseHeader() {
159  // We already parsed the header during initialization.
160  return true;
161}
162
163void ObjectFileJSON::ParseSymtab(Symtab &symtab) {
164  Log *log = GetLog(LLDBLog::Symbols);
165  SectionList *section_list = GetModule()->GetSectionList();
166  for (JSONSymbol json_symbol : m_symbols) {
167    llvm::Expected<Symbol> symbol = Symbol::FromJSON(json_symbol, section_list);
168    if (!symbol) {
169      LLDB_LOG_ERROR(log, symbol.takeError(), "invalid symbol: {0}");
170      continue;
171    }
172    symtab.AddSymbol(*symbol);
173  }
174  symtab.Finalize();
175}
176
177void 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 = 1;
183  for (const auto &section : m_sections) {
184    auto section_sp = std::make_shared<Section>(
185        GetModule(), this, id++, ConstString(section.name),
186        section.type.value_or(eSectionTypeCode), 0, section.size.value_or(0), 0,
187        section.size.value_or(0), /*log2align*/ 0, /*flags*/ 0);
188    m_sections_up->AddSection(section_sp);
189    unified_section_list.AddSection(section_sp);
190  }
191}
192
193bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp,
194                                     lldb::addr_t data_offset,
195                                     lldb::addr_t data_length) {
196  DataExtractor data;
197  data.SetData(data_sp, data_offset, data_length);
198  lldb::offset_t offset = 0;
199  uint32_t magic = data.GetU8(&offset);
200  return magic == '{';
201}
202
203namespace lldb_private {
204
205bool fromJSON(const json::Value &value, ObjectFileJSON::Header &header,
206              json::Path path) {
207  json::ObjectMapper o(value, path);
208  return o && o.map("triple", header.triple) && o.map("uuid", header.uuid) &&
209         o.map("type", header.type);
210}
211
212bool fromJSON(const json::Value &value, ObjectFileJSON::Body &body,
213              json::Path path) {
214  json::ObjectMapper o(value, path);
215  return o && o.mapOptional("symbols", body.symbols) &&
216         o.mapOptional("sections", body.sections);
217}
218
219} // namespace lldb_private
220