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 = 1; |
183 | for (const auto §ion : m_sections) { |
184 | auto section_sp = std::make_shared<Section>( |
185 | args: GetModule(), args: this, args: id++, args: ConstString(section.name), |
186 | args: section.type.value_or(u: eSectionTypeCode), args: 0, args: section.size.value_or(u: 0), args: 0, |
187 | args: section.size.value_or(u: 0), /*log2align*/ args: 0, /*flags*/ args: 0); |
188 | m_sections_up->AddSection(section_sp); |
189 | unified_section_list.AddSection(section_sp); |
190 | } |
191 | } |
192 | |
193 | bool 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, offset: data_offset, length: data_length); |
198 | lldb::offset_t offset = 0; |
199 | uint32_t magic = data.GetU8(offset_ptr: &offset); |
200 | return magic == '{'; |
201 | } |
202 | |
203 | namespace lldb_private { |
204 | |
205 | bool (const json::Value &value, ObjectFileJSON::Header &, |
206 | json::Path path) { |
207 | json::ObjectMapper o(value, path); |
208 | return o && o.map(Prop: "triple" , Out&: header.triple) && o.map(Prop: "uuid" , Out&: header.uuid) && |
209 | o.map(Prop: "type" , Out&: header.type); |
210 | } |
211 | |
212 | bool fromJSON(const json::Value &value, ObjectFileJSON::Body &body, |
213 | json::Path path) { |
214 | json::ObjectMapper o(value, path); |
215 | return o && o.mapOptional(Prop: "symbols" , Out&: body.symbols) && |
216 | o.mapOptional(Prop: "sections" , Out&: body.sections); |
217 | } |
218 | |
219 | } // namespace lldb_private |
220 | |