1//===-- ObjectContainerMachOFileset.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 "ObjectContainerMachOFileset.h"
10#include "lldb/Core/Module.h"
11#include "lldb/Core/ModuleSpec.h"
12#include "lldb/Core/PluginManager.h"
13#include "lldb/Symbol/ObjectFile.h"
14#include "lldb/Target/Target.h"
15#include "lldb/Utility/ArchSpec.h"
16#include "lldb/Utility/DataBuffer.h"
17#include "lldb/Utility/Stream.h"
18#include <optional>
19
20using namespace lldb;
21using namespace lldb_private;
22using namespace llvm::MachO;
23
24LLDB_PLUGIN_DEFINE(ObjectContainerMachOFileset)
25
26void ObjectContainerMachOFileset::Initialize() {
27 PluginManager::RegisterPlugin(name: GetPluginNameStatic(),
28 description: GetPluginDescriptionStatic(), create_callback: CreateInstance,
29 get_module_specifications: GetModuleSpecifications, create_memory_callback: CreateMemoryInstance);
30}
31
32void ObjectContainerMachOFileset::Terminate() {
33 PluginManager::UnregisterPlugin(create_callback: CreateInstance);
34}
35
36ObjectContainerMachOFileset::ObjectContainerMachOFileset(
37 const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
38 lldb::offset_t data_offset, const lldb_private::FileSpec *file,
39 lldb::offset_t offset, lldb::offset_t length)
40 : ObjectContainer(module_sp, file, offset, length, data_sp, data_offset),
41 m_memory_addr(LLDB_INVALID_ADDRESS) {}
42
43ObjectContainerMachOFileset::ObjectContainerMachOFileset(
44 const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp,
45 const lldb::ProcessSP &process_sp, lldb::addr_t header_addr)
46 : ObjectContainer(module_sp, nullptr, 0, data_sp->GetByteSize(), data_sp,
47 0),
48 m_process_wp(process_sp), m_memory_addr(header_addr) {}
49
50ObjectContainer *ObjectContainerMachOFileset::CreateInstance(
51 const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
52 lldb::offset_t data_offset, const FileSpec *file,
53 lldb::offset_t file_offset, lldb::offset_t length) {
54 if (!data_sp)
55 return {};
56
57 DataExtractor data;
58 data.SetData(data_sp, offset: data_offset, length);
59 if (!MagicBytesMatch(data))
60 return {};
61
62 auto container_up = std::make_unique<ObjectContainerMachOFileset>(
63 args: module_sp, args&: data_sp, args&: data_offset, args&: file, args&: file_offset, args&: length);
64 if (!container_up->ParseHeader())
65 return {};
66
67 return container_up.release();
68}
69
70ObjectContainer *ObjectContainerMachOFileset::CreateMemoryInstance(
71 const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp,
72 const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
73 if (!MagicBytesMatch(data_sp, data_offset: 0, data_length: data_sp->GetByteSize()))
74 return {};
75
76 auto container_up = std::make_unique<ObjectContainerMachOFileset>(
77 args: module_sp, args&: data_sp, args: process_sp, args&: header_addr);
78 if (!container_up->ParseHeader())
79 return {};
80
81 return container_up.release();
82}
83
84ObjectContainerMachOFileset::~ObjectContainerMachOFileset() = default;
85
86static uint32_t MachHeaderSizeFromMagic(uint32_t magic) {
87 switch (magic) {
88 case MH_MAGIC:
89 case MH_CIGAM:
90 return sizeof(struct mach_header);
91 case MH_MAGIC_64:
92 case MH_CIGAM_64:
93 return sizeof(struct mach_header_64);
94 default:
95 return 0;
96 }
97}
98
99static std::optional<mach_header> ParseMachOHeader(DataExtractor &data) {
100 lldb::offset_t offset = 0;
101 mach_header header;
102 header.magic = data.GetU32(offset_ptr: &offset);
103 switch (header.magic) {
104 case MH_MAGIC:
105 data.SetByteOrder(endian::InlHostByteOrder());
106 data.SetAddressByteSize(4);
107 break;
108 case MH_MAGIC_64:
109 data.SetByteOrder(endian::InlHostByteOrder());
110 data.SetAddressByteSize(8);
111 break;
112 case MH_CIGAM:
113 data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
114 ? eByteOrderLittle
115 : eByteOrderBig);
116 data.SetAddressByteSize(4);
117 break;
118 case MH_CIGAM_64:
119 data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
120 ? eByteOrderLittle
121 : eByteOrderBig);
122 data.SetAddressByteSize(8);
123 break;
124 default:
125 return {};
126 }
127
128 header.cputype = data.GetU32(offset_ptr: &offset);
129 header.cpusubtype = data.GetU32(offset_ptr: &offset);
130 header.filetype = data.GetU32(offset_ptr: &offset);
131 header.ncmds = data.GetU32(offset_ptr: &offset);
132 header.sizeofcmds = data.GetU32(offset_ptr: &offset);
133 return header;
134}
135
136static bool
137ParseFileset(DataExtractor &data, mach_header header,
138 std::vector<ObjectContainerMachOFileset::Entry> &entries,
139 std::optional<lldb::addr_t> load_addr = std::nullopt) {
140 lldb::offset_t offset = MachHeaderSizeFromMagic(magic: header.magic);
141 lldb::offset_t slide = 0;
142 for (uint32_t i = 0; i < header.ncmds; ++i) {
143 const lldb::offset_t load_cmd_offset = offset;
144 load_command lc = {};
145 if (data.GetU32(offset_ptr: &offset, dst: &lc.cmd, count: 2) == nullptr)
146 break;
147
148 // If we know the load address we can compute the slide.
149 if (load_addr) {
150 if (lc.cmd == llvm::MachO::LC_SEGMENT_64) {
151 segment_command_64 segment;
152 data.CopyData(offset: load_cmd_offset, length: sizeof(segment_command_64), dst: &segment);
153 if (llvm::StringRef(segment.segname) == "__TEXT")
154 slide = *load_addr - segment.vmaddr;
155 }
156 }
157
158 if (lc.cmd == LC_FILESET_ENTRY) {
159 fileset_entry_command entry;
160 data.CopyData(offset: load_cmd_offset, length: sizeof(fileset_entry_command), dst: &entry);
161 lldb::offset_t entry_id_offset = load_cmd_offset + entry.entry_id.offset;
162 const char *id = data.GetCStr(offset_ptr: &entry_id_offset);
163 entries.emplace_back(args: entry.vmaddr + slide, args&: entry.fileoff,
164 args: std::string(id));
165 }
166
167 offset = load_cmd_offset + lc.cmdsize;
168 }
169
170 return true;
171}
172
173bool ObjectContainerMachOFileset::ParseHeader(
174 DataExtractor &data, const lldb_private::FileSpec &file,
175 lldb::offset_t file_offset, std::vector<Entry> &entries) {
176 std::optional<mach_header> header = ParseMachOHeader(data);
177
178 if (!header)
179 return false;
180
181 const size_t header_size = MachHeaderSizeFromMagic(magic: header->magic);
182 const size_t header_and_lc_size = header_size + header->sizeofcmds;
183
184 if (data.GetByteSize() < header_and_lc_size) {
185 DataBufferSP data_sp =
186 ObjectFile::MapFileData(file, Size: header_and_lc_size, Offset: file_offset);
187 data.SetData(data_sp);
188 }
189
190 return ParseFileset(data, header: *header, entries);
191}
192
193bool ObjectContainerMachOFileset::ParseHeader() {
194 ModuleSP module_sp(GetModule());
195 if (!module_sp)
196 return false;
197
198 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
199
200 std::optional<mach_header> header = ParseMachOHeader(data&: m_data);
201 if (!header)
202 return false;
203
204 const size_t header_size = MachHeaderSizeFromMagic(magic: header->magic);
205 const size_t header_and_lc_size = header_size + header->sizeofcmds;
206
207 if (m_data.GetByteSize() < header_and_lc_size) {
208 ProcessSP process_sp(m_process_wp.lock());
209 DataBufferSP data_sp =
210 process_sp
211 ? ObjectFile::ReadMemory(process_sp, addr: m_memory_addr,
212 byte_size: header_and_lc_size)
213 : ObjectFile::MapFileData(file: m_file, Size: header_and_lc_size, Offset: m_offset);
214 m_data.SetData(data_sp);
215 }
216
217 return ParseFileset(data&: m_data, header: *header, entries&: m_entries, load_addr: m_memory_addr);
218}
219
220size_t ObjectContainerMachOFileset::GetModuleSpecifications(
221 const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
222 lldb::offset_t data_offset, lldb::offset_t file_offset,
223 lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
224 const size_t initial_count = specs.GetSize();
225
226 DataExtractor data;
227 data.SetData(data_sp, offset: data_offset, length: data_sp->GetByteSize());
228
229 if (MagicBytesMatch(data)) {
230 std::vector<Entry> entries;
231 if (ParseHeader(data, file, file_offset, entries)) {
232 for (const Entry &entry : entries) {
233 const lldb::offset_t entry_offset = entry.fileoff + file_offset;
234 if (ObjectFile::GetModuleSpecifications(
235 file, file_offset: entry_offset, file_size: file_size - entry_offset, specs)) {
236 ModuleSpec &spec = specs.GetModuleSpecRefAtIndex(i: specs.GetSize() - 1);
237 spec.GetObjectName() = ConstString(entry.id);
238 }
239 }
240 }
241 }
242 return specs.GetSize() - initial_count;
243}
244
245bool ObjectContainerMachOFileset::MagicBytesMatch(DataBufferSP data_sp,
246 lldb::addr_t data_offset,
247 lldb::addr_t data_length) {
248 DataExtractor data;
249 data.SetData(data_sp, offset: data_offset, length: data_length);
250 return MagicBytesMatch(data);
251}
252
253bool ObjectContainerMachOFileset::MagicBytesMatch(const DataExtractor &data) {
254 lldb::offset_t offset = 0;
255 uint32_t magic = data.GetU32(offset_ptr: &offset);
256 switch (magic) {
257 case MH_MAGIC:
258 case MH_CIGAM:
259 case MH_MAGIC_64:
260 case MH_CIGAM_64:
261 break;
262 default:
263 return false;
264 }
265 offset += 4; // cputype
266 offset += 4; // cpusubtype
267 uint32_t filetype = data.GetU32(offset_ptr: &offset);
268 return filetype == MH_FILESET;
269}
270
271ObjectFileSP
272ObjectContainerMachOFileset::GetObjectFile(const lldb_private::FileSpec *file) {
273 ModuleSP module_sp(GetModule());
274 if (!module_sp)
275 return {};
276
277 ConstString object_name = module_sp->GetObjectName();
278 if (!object_name)
279 return {};
280
281 Entry *entry = FindEntry(id: object_name.GetCString());
282 if (!entry)
283 return {};
284
285 DataBufferSP data_sp;
286 lldb::offset_t data_offset = 0;
287 return ObjectFile::FindPlugin(module_sp, file_spec: file, file_offset: m_offset + entry->fileoff,
288 file_size: m_data.GetByteSize() - entry->fileoff, data_sp,
289 data_offset);
290}
291
292ObjectContainerMachOFileset::Entry *
293ObjectContainerMachOFileset::FindEntry(llvm::StringRef id) {
294 for (Entry &entry : m_entries) {
295 if (entry.id == id)
296 return &entry;
297 }
298 return nullptr;
299}
300

source code of lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.cpp