[Go to site: main page, start]

LLDB mainline
ObjectContainerMachOFileset.cpp
Go to the documentation of this file.
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
10#include "lldb/Core/Module.h"
14#include "lldb/Target/Target.h"
17#include "lldb/Utility/Stream.h"
18#include <optional>
19
20using namespace lldb;
21using namespace lldb_private;
22using namespace llvm::MachO;
23
24/// Read a Mach-O load-command header (cmd + cmdsize) from \p data at
25/// \p offset into \p cmd, advancing \p offset by 8 bytes. \p T may be
26/// \c llvm::MachO::load_command or any of its richer variants
27/// (\c thread_command, \c dylib_command, \c encryption_info_command, ...);
28/// only the leading cmd/cmdsize fields are touched by this read. Returns
29/// false on EOF or on a cmdsize smaller than sizeof(load_command), in which
30/// case callers should break out of their load-command loop to avoid spinning
31/// on malformed input.
32template <typename T>
34 T &cmd) {
35 static_assert(offsetof(T, cmd) == 0, "T::cmd must be the first field");
36 static_assert(offsetof(T, cmdsize) == sizeof(uint32_t),
37 "T::cmdsize must immediately follow T::cmd");
38 static_assert(std::is_same<decltype(T::cmd), uint32_t>::value,
39 "T::cmd must be uint32_t");
40 static_assert(std::is_same<decltype(T::cmdsize), uint32_t>::value,
41 "T::cmdsize must be uint32_t");
42 if (data.GetU32(&offset, &cmd, 2) == nullptr)
43 return false;
44 if (cmd.cmdsize < sizeof(load_command))
45 return false;
46 return true;
47}
48
50
56
60
62 const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
63 lldb::offset_t data_offset, const lldb_private::FileSpec *file,
64 lldb::offset_t offset, lldb::offset_t length)
65 : ObjectContainer(module_sp, file, offset, length, data_sp, data_offset),
67
69 const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp,
70 const lldb::ProcessSP &process_sp, lldb::addr_t header_addr)
71 : ObjectContainer(module_sp, nullptr, 0, data_sp->GetByteSize(), data_sp,
72 0),
73 m_process_wp(process_sp), m_memory_addr(header_addr) {}
74
76 const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
77 lldb::offset_t data_offset, const FileSpec *file,
78 lldb::offset_t file_offset, lldb::offset_t length) {
79 if (!data_sp)
80 return {};
81
82 DataExtractor extractor;
83 extractor.SetData(data_sp, data_offset, length);
84 if (!MagicBytesMatch(extractor))
85 return {};
86
87 auto container_up = std::make_unique<ObjectContainerMachOFileset>(
88 module_sp, data_sp, data_offset, file, file_offset, length);
89 if (!container_up->ParseHeader())
90 return {};
91
92 return container_up.release();
93}
94
96 const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp,
97 const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
98 if (!MagicBytesMatch(data_sp, 0, data_sp->GetByteSize()))
99 return {};
100
101 auto container_up = std::make_unique<ObjectContainerMachOFileset>(
102 module_sp, data_sp, process_sp, header_addr);
103 if (!container_up->ParseHeader())
104 return {};
105
106 return container_up.release();
107}
108
110
111static uint32_t MachHeaderSizeFromMagic(uint32_t magic) {
112 switch (magic) {
113 case MH_MAGIC:
114 case MH_CIGAM:
115 return sizeof(struct mach_header);
116 case MH_MAGIC_64:
117 case MH_CIGAM_64:
118 return sizeof(struct mach_header_64);
119 default:
120 return 0;
121 }
122}
123
124static std::optional<mach_header> ParseMachOHeader(DataExtractor &extractor) {
125 lldb::offset_t offset = 0;
126 mach_header header;
127 header.magic = extractor.GetU32(&offset);
128 switch (header.magic) {
129 case MH_MAGIC:
131 extractor.SetAddressByteSize(4);
132 break;
133 case MH_MAGIC_64:
135 extractor.SetAddressByteSize(8);
136 break;
137 case MH_CIGAM:
140 : eByteOrderBig);
141 extractor.SetAddressByteSize(4);
142 break;
143 case MH_CIGAM_64:
146 : eByteOrderBig);
147 extractor.SetAddressByteSize(8);
148 break;
149 default:
150 return {};
151 }
152
153 header.cputype = extractor.GetU32(&offset);
154 header.cpusubtype = extractor.GetU32(&offset);
155 header.filetype = extractor.GetU32(&offset);
156 header.ncmds = extractor.GetU32(&offset);
157 header.sizeofcmds = extractor.GetU32(&offset);
158 return header;
159}
160
161static bool
162ParseFileset(DataExtractor &extractor, mach_header header,
163 std::vector<ObjectContainerMachOFileset::Entry> &entries,
164 std::optional<lldb::addr_t> load_addr = std::nullopt) {
165 lldb::offset_t offset = MachHeaderSizeFromMagic(header.magic);
166 lldb::offset_t slide = 0;
167 for (uint32_t i = 0; i < header.ncmds; ++i) {
168 const lldb::offset_t load_cmd_offset = offset;
169 load_command lc = {};
170 if (!ReadMachOCommand(extractor, offset, lc))
171 break;
172
173 // If we know the load address we can compute the slide.
174 if (load_addr) {
175 if (lc.cmd == llvm::MachO::LC_SEGMENT_64) {
176 segment_command_64 segment;
177 extractor.CopyData(load_cmd_offset, sizeof(segment_command_64),
178 &segment);
179 if (llvm::StringRef(segment.segname) == "__TEXT")
180 slide = *load_addr - segment.vmaddr;
181 }
182 }
183
184 if (lc.cmd == LC_FILESET_ENTRY) {
185 fileset_entry_command entry;
186 extractor.CopyData(load_cmd_offset, sizeof(fileset_entry_command),
187 &entry);
188 lldb::offset_t entry_id_offset = load_cmd_offset + entry.entry_id.offset;
189 if (const char *id = extractor.GetCStr(&entry_id_offset))
190 entries.emplace_back(entry.vmaddr + slide, entry.fileoff,
191 std::string(id));
192 }
193
194 offset = load_cmd_offset + lc.cmdsize;
195 }
196
197 return true;
198}
199
201 DataExtractor &extractor, const lldb_private::FileSpec &file,
202 lldb::offset_t file_offset, std::vector<Entry> &entries) {
203 std::optional<mach_header> header = ParseMachOHeader(extractor);
204
205 if (!header)
206 return false;
207
208 const size_t header_size = MachHeaderSizeFromMagic(header->magic);
209 const size_t header_and_lc_size = header_size + header->sizeofcmds;
210
211 if (extractor.GetByteSize() < header_and_lc_size) {
212 DataBufferSP data_sp =
213 ObjectFile::MapFileData(file, header_and_lc_size, file_offset);
214 extractor.SetData(data_sp);
215 }
216
217 return ParseFileset(extractor, *header, entries);
218}
219
221 ModuleSP module_sp(GetModule());
222 if (!module_sp)
223 return false;
224
225 std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
226
227 std::optional<mach_header> header = ParseMachOHeader(*m_extractor_sp);
228 if (!header)
229 return false;
230
231 const size_t header_size = MachHeaderSizeFromMagic(header->magic);
232 const size_t header_and_lc_size = header_size + header->sizeofcmds;
233
234 if (m_extractor_sp->GetByteSize() < header_and_lc_size) {
235 ProcessSP process_sp(m_process_wp.lock());
236 DataBufferSP data_sp =
237 process_sp
239 header_and_lc_size)
240 : ObjectFile::MapFileData(m_file, header_and_lc_size, m_offset);
241 m_extractor_sp->SetData(data_sp);
242 }
243
245}
246
248 const lldb_private::FileSpec &file, lldb::DataExtractorSP &extractor_sp,
249 lldb::offset_t file_offset, lldb::offset_t file_size) {
250 if (!extractor_sp)
251 return {};
252
253 ModuleSpecList specs;
254 if (MagicBytesMatch(*extractor_sp)) {
255 std::vector<Entry> entries;
256 if (ParseHeader(*extractor_sp, file, file_offset, entries)) {
257 for (const Entry &entry : entries) {
258 const lldb::offset_t entry_offset = entry.fileoff + file_offset;
260 file, entry_offset, file_size - entry_offset);
261 if (entry_specs.GetSize() > 0) {
262 ModuleSpec &spec =
263 entry_specs.GetModuleSpecRefAtIndex(entry_specs.GetSize() - 1);
264 spec.GetObjectName() = ConstString(entry.id);
265 specs.Append(spec);
266 }
267 }
268 }
269 }
270 return specs;
271}
272
274 lldb::addr_t data_offset,
275 lldb::addr_t data_length) {
276 DataExtractor extractor;
277 extractor.SetData(data_sp, data_offset, data_length);
278 return MagicBytesMatch(extractor);
279}
280
282 const DataExtractor &extractor) {
283 lldb::offset_t offset = 0;
284 uint32_t magic = extractor.GetU32(&offset);
285 switch (magic) {
286 case MH_MAGIC:
287 case MH_CIGAM:
288 case MH_MAGIC_64:
289 case MH_CIGAM_64:
290 break;
291 default:
292 return false;
293 }
294 offset += 4; // cputype
295 offset += 4; // cpusubtype
296 uint32_t filetype = extractor.GetU32(&offset);
297 return filetype == MH_FILESET;
298}
299
302 ModuleSP module_sp(GetModule());
303 if (!module_sp)
304 return {};
305
306 ConstString object_name = module_sp->GetObjectName();
307 if (!object_name)
308 return {};
309
310 Entry *entry = FindEntry(object_name.GetCString());
311 if (!entry)
312 return {};
313
314 DataExtractorSP extractor_sp;
315 lldb::offset_t data_offset = 0;
316 return ObjectFile::FindPlugin(module_sp, file, m_offset + entry->fileoff,
317 m_extractor_sp->GetByteSize() - entry->fileoff,
318 extractor_sp, data_offset);
319}
320
323 for (Entry &entry : m_entries) {
324 if (entry.id == id)
325 return &entry;
326 }
327 return nullptr;
328}
static bool ReadMachOCommand(DataExtractor &data, lldb::offset_t &offset, T &cmd)
Read a Mach-O load-command header (cmd + cmdsize) from data at offset into cmd, advancing offset by 8...
static bool ParseFileset(DataExtractor &extractor, mach_header header, std::vector< ObjectContainerMachOFileset::Entry > &entries, std::optional< lldb::addr_t > load_addr=std::nullopt)
static std::optional< mach_header > ParseMachOHeader(DataExtractor &extractor)
static uint32_t MachHeaderSizeFromMagic(uint32_t magic)
#define LLDB_PLUGIN_DEFINE(PluginName)
A uniqued constant string class.
Definition ConstString.h:40
const char * GetCString() const
Get the string value as a C string.
An data extractor class.
const char * GetCStr(lldb::offset_t *offset_ptr) const
Extract a C string from *offset_ptr.
virtual uint64_t GetByteSize() const
Get the number of bytes contained in this object.
lldb::offset_t CopyData(lldb::offset_t offset, lldb::offset_t length, void *dst) const
Copy length bytes from *offset, without swapping bytes.
void SetByteOrder(lldb::ByteOrder byte_order)
Set the byte_order value.
uint32_t GetU32(lldb::offset_t *offset_ptr) const
Extract a uint32_t value from *offset_ptr.
virtual lldb::offset_t SetData(const void *bytes, lldb::offset_t length, lldb::ByteOrder byte_order)
Set data with a buffer that is caller owned.
void SetAddressByteSize(uint32_t addr_size)
Set the address byte size.
A file utility class.
Definition FileSpec.h:56
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
void Append(const ModuleSpec &spec)
Definition ModuleSpec.h:371
ModuleSpec & GetModuleSpecRefAtIndex(size_t i)
Definition ModuleSpec.h:384
ConstString & GetObjectName()
Definition ModuleSpec.h:107
static bool MagicBytesMatch(const lldb_private::DataExtractor &data)
static lldb_private::ObjectContainer * CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, lldb::offset_t offset, lldb::offset_t length)
ObjectContainerMachOFileset(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, lldb::offset_t offset, lldb::offset_t length)
static lldb_private::ObjectContainer * CreateMemoryInstance(const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr)
static ModuleSpecList GetModuleSpecifications(const lldb_private::FileSpec &file, lldb::DataExtractorSP &extractor_sp, lldb::offset_t file_offset, lldb::offset_t length)
bool ParseHeader() override
Attempts to parse the object header.
lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override
Selects an architecture in an object file.
virtual lldb::addr_t GetByteSize() const
lldb::DataExtractorSP m_extractor_sp
The data for this object file so things can be parsed lazily.
FileSpec m_file
The file that represents this container objects (which can be different from the module's file).
lldb::addr_t m_offset
The offset in bytes into the file, or the address in memory.
ObjectContainer(const lldb::ModuleSP &module_sp, const FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length, lldb::DataBufferSP data_sp, lldb::offset_t data_offset)
Construct with a parent module, offset, and header data.
static lldb::ObjectFileSP FindPlugin(const lldb::ModuleSP &module_sp, const FileSpec *file_spec, lldb::offset_t file_offset, lldb::offset_t file_size, lldb::DataExtractorSP extractor_sp, lldb::offset_t &data_offset)
Find a ObjectFile plug-in that can parse file_spec.
static lldb::DataBufferSP MapFileData(const FileSpec &file, uint64_t Size, uint64_t Offset)
static lldb::WritableDataBufferSP ReadMemory(const lldb::ProcessSP &process_sp, lldb::addr_t addr, size_t byte_size)
static ModuleSpecList GetModuleSpecifications(const FileSpec &file, lldb::offset_t file_offset, lldb::offset_t file_size, lldb::DataExtractorSP=lldb::DataExtractorSP())
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
#define LLDB_INVALID_ADDRESS
lldb::ByteOrder InlHostByteOrder()
Definition Endian.h:25
A class that represents a running process on the host machine.
uint64_t offset_t
Definition lldb-types.h:86
std::shared_ptr< lldb_private::ObjectFile > ObjectFileSP
std::shared_ptr< lldb_private::Process > ProcessSP
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::DataExtractor > DataExtractorSP
std::shared_ptr< lldb_private::Module > ModuleSP
uint64_t vmaddr