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 | |