1 //===-- ObjectContainerMachOFileset.cpp -----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace lldb_private
;
22 using namespace llvm::MachO
;
24 LLDB_PLUGIN_DEFINE(ObjectContainerMachOFileset
)
26 void ObjectContainerMachOFileset::Initialize() {
27 PluginManager::RegisterPlugin(GetPluginNameStatic(),
28 GetPluginDescriptionStatic(), CreateInstance
,
29 GetModuleSpecifications
, CreateMemoryInstance
);
32 void ObjectContainerMachOFileset::Terminate() {
33 PluginManager::UnregisterPlugin(CreateInstance
);
36 ObjectContainerMachOFileset::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
) {}
43 ObjectContainerMachOFileset::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
,
48 m_process_wp(process_sp
), m_memory_addr(header_addr
) {}
50 ObjectContainer
*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
) {
58 data
.SetData(data_sp
, data_offset
, length
);
59 if (!MagicBytesMatch(data
))
62 auto container_up
= std::make_unique
<ObjectContainerMachOFileset
>(
63 module_sp
, data_sp
, data_offset
, file
, file_offset
, length
);
64 if (!container_up
->ParseHeader())
67 return container_up
.release();
70 ObjectContainer
*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
, 0, data_sp
->GetByteSize()))
76 auto container_up
= std::make_unique
<ObjectContainerMachOFileset
>(
77 module_sp
, data_sp
, process_sp
, header_addr
);
78 if (!container_up
->ParseHeader())
81 return container_up
.release();
84 ObjectContainerMachOFileset::~ObjectContainerMachOFileset() = default;
86 static uint32_t MachHeaderSizeFromMagic(uint32_t magic
) {
90 return sizeof(struct mach_header
);
93 return sizeof(struct mach_header_64
);
99 static std::optional
<mach_header
> ParseMachOHeader(DataExtractor
&data
) {
100 lldb::offset_t offset
= 0;
102 header
.magic
= data
.GetU32(&offset
);
103 switch (header
.magic
) {
105 data
.SetByteOrder(endian::InlHostByteOrder());
106 data
.SetAddressByteSize(4);
109 data
.SetByteOrder(endian::InlHostByteOrder());
110 data
.SetAddressByteSize(8);
113 data
.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
116 data
.SetAddressByteSize(4);
119 data
.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
122 data
.SetAddressByteSize(8);
128 header
.cputype
= data
.GetU32(&offset
);
129 header
.cpusubtype
= data
.GetU32(&offset
);
130 header
.filetype
= data
.GetU32(&offset
);
131 header
.ncmds
= data
.GetU32(&offset
);
132 header
.sizeofcmds
= data
.GetU32(&offset
);
137 ParseFileset(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(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
, &lc
.cmd
, 2) == nullptr)
148 // If we know the load address we can compute the slide.
150 if (lc
.cmd
== llvm::MachO::LC_SEGMENT_64
) {
151 segment_command_64 segment
;
152 data
.CopyData(load_cmd_offset
, sizeof(segment_command_64
), &segment
);
153 if (llvm::StringRef(segment
.segname
) == "__TEXT")
154 slide
= *load_addr
- segment
.vmaddr
;
158 if (lc
.cmd
== LC_FILESET_ENTRY
) {
159 fileset_entry_command entry
;
160 data
.CopyData(load_cmd_offset
, sizeof(fileset_entry_command
), &entry
);
161 lldb::offset_t entry_id_offset
= load_cmd_offset
+ entry
.entry_id
.offset
;
162 const char *id
= data
.GetCStr(&entry_id_offset
);
163 entries
.emplace_back(entry
.vmaddr
+ slide
, entry
.fileoff
,
167 offset
= load_cmd_offset
+ lc
.cmdsize
;
173 bool 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
);
181 const size_t header_size
= MachHeaderSizeFromMagic(header
->magic
);
182 const size_t header_and_lc_size
= header_size
+ header
->sizeofcmds
;
184 if (data
.GetByteSize() < header_and_lc_size
) {
185 DataBufferSP data_sp
=
186 ObjectFile::MapFileData(file
, header_and_lc_size
, file_offset
);
187 data
.SetData(data_sp
);
190 return ParseFileset(data
, *header
, entries
);
193 bool ObjectContainerMachOFileset::ParseHeader() {
194 ModuleSP
module_sp(GetModule());
198 std::lock_guard
<std::recursive_mutex
> guard(module_sp
->GetMutex());
200 std::optional
<mach_header
> header
= ParseMachOHeader(m_data
);
204 const size_t header_size
= MachHeaderSizeFromMagic(header
->magic
);
205 const size_t header_and_lc_size
= header_size
+ header
->sizeofcmds
;
207 if (m_data
.GetByteSize() < header_and_lc_size
) {
208 ProcessSP
process_sp(m_process_wp
.lock());
209 DataBufferSP data_sp
=
211 ? ObjectFile::ReadMemory(process_sp
, m_memory_addr
,
213 : ObjectFile::MapFileData(m_file
, header_and_lc_size
, m_offset
);
214 m_data
.SetData(data_sp
);
217 return ParseFileset(m_data
, *header
, m_entries
, m_memory_addr
);
220 size_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();
227 data
.SetData(data_sp
, data_offset
, data_sp
->GetByteSize());
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
, entry_offset
, file_size
- entry_offset
, specs
)) {
236 ModuleSpec
&spec
= specs
.GetModuleSpecRefAtIndex(specs
.GetSize() - 1);
237 spec
.GetObjectName() = ConstString(entry
.id
);
242 return specs
.GetSize() - initial_count
;
245 bool ObjectContainerMachOFileset::MagicBytesMatch(DataBufferSP data_sp
,
246 lldb::addr_t data_offset
,
247 lldb::addr_t data_length
) {
249 data
.SetData(data_sp
, data_offset
, data_length
);
250 return MagicBytesMatch(data
);
253 bool ObjectContainerMachOFileset::MagicBytesMatch(const DataExtractor
&data
) {
254 lldb::offset_t offset
= 0;
255 uint32_t magic
= data
.GetU32(&offset
);
265 offset
+= 4; // cputype
266 offset
+= 4; // cpusubtype
267 uint32_t filetype
= data
.GetU32(&offset
);
268 return filetype
== MH_FILESET
;
272 ObjectContainerMachOFileset::GetObjectFile(const lldb_private::FileSpec
*file
) {
273 ModuleSP
module_sp(GetModule());
277 ConstString object_name
= module_sp
->GetObjectName();
281 Entry
*entry
= FindEntry(object_name
.GetCString());
285 DataBufferSP data_sp
;
286 lldb::offset_t data_offset
= 0;
287 return ObjectFile::FindPlugin(module_sp
, file
, m_offset
+ entry
->fileoff
,
288 m_data
.GetByteSize() - entry
->fileoff
, data_sp
,
292 ObjectContainerMachOFileset::Entry
*
293 ObjectContainerMachOFileset::FindEntry(llvm::StringRef id
) {
294 for (Entry
&entry
: m_entries
) {