1 //===-- ObjectContainerUniversalMachO.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 "ObjectContainerUniversalMachO.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"
20 using namespace lldb_private
;
21 using namespace llvm::MachO
;
23 LLDB_PLUGIN_DEFINE_ADV(ObjectContainerUniversalMachO
,
24 ObjectContainerMachOArchive
)
26 void ObjectContainerUniversalMachO::Initialize() {
27 PluginManager::RegisterPlugin(GetPluginNameStatic(),
28 GetPluginDescriptionStatic(), CreateInstance
,
29 GetModuleSpecifications
);
32 void ObjectContainerUniversalMachO::Terminate() {
33 PluginManager::UnregisterPlugin(CreateInstance
);
36 ObjectContainer
*ObjectContainerUniversalMachO::CreateInstance(
37 const lldb::ModuleSP
&module_sp
, DataBufferSP
&data_sp
,
38 lldb::offset_t data_offset
, const FileSpec
*file
,
39 lldb::offset_t file_offset
, lldb::offset_t length
) {
40 // We get data when we aren't trying to look for cached container
41 // information, so only try and look for an architecture slice if we get data
44 data
.SetData(data_sp
, data_offset
, length
);
45 if (ObjectContainerUniversalMachO::MagicBytesMatch(data
)) {
46 std::unique_ptr
<ObjectContainerUniversalMachO
> container_up(
47 new ObjectContainerUniversalMachO(module_sp
, data_sp
, data_offset
,
48 file
, file_offset
, length
));
49 if (container_up
->ParseHeader()) {
50 return container_up
.release();
57 bool ObjectContainerUniversalMachO::MagicBytesMatch(const DataExtractor
&data
) {
58 lldb::offset_t offset
= 0;
59 uint32_t magic
= data
.GetU32(&offset
);
60 return magic
== FAT_MAGIC
|| magic
== FAT_CIGAM
|| magic
== FAT_MAGIC_64
||
61 magic
== FAT_CIGAM_64
;
64 ObjectContainerUniversalMachO::ObjectContainerUniversalMachO(
65 const lldb::ModuleSP
&module_sp
, DataBufferSP
&data_sp
,
66 lldb::offset_t data_offset
, const FileSpec
*file
,
67 lldb::offset_t file_offset
, lldb::offset_t length
)
68 : ObjectContainer(module_sp
, file
, file_offset
, length
, data_sp
,
70 m_header(), m_fat_archs() {
71 memset(&m_header
, 0, sizeof(m_header
));
74 ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() = default;
76 bool ObjectContainerUniversalMachO::ParseHeader() {
77 bool success
= ParseHeader(m_data
, m_header
, m_fat_archs
);
78 // We no longer need any data, we parsed all we needed to parse and cached it
79 // in m_header and m_fat_archs
84 bool ObjectContainerUniversalMachO::ParseHeader(
85 lldb_private::DataExtractor
&data
, llvm::MachO::fat_header
&header
,
86 std::vector
<FatArch
> &fat_archs
) {
87 // Store the file offset for this universal file as we could have a universal
88 // .o file in a BSD archive, or be contained in another kind of object.
89 lldb::offset_t offset
= 0;
90 data
.SetByteOrder(eByteOrderBig
);
91 header
.magic
= data
.GetU32(&offset
);
94 // Universal mach-o files always have their headers in big endian.
95 if (header
.magic
== FAT_MAGIC
|| header
.magic
== FAT_MAGIC_64
) {
96 const bool is_fat64
= header
.magic
== FAT_MAGIC_64
;
97 data
.SetAddressByteSize(is_fat64
? 8 : 4);
99 header
.nfat_arch
= data
.GetU32(&offset
);
101 // Now we should have enough data for all of the fat headers, so lets index
102 // them so we know how many architectures that this universal binary
104 for (uint32_t arch_idx
= 0; arch_idx
< header
.nfat_arch
; ++arch_idx
) {
105 if (data
.ValidOffsetForDataOfSize(offset
, sizeof(fat_arch
))) {
108 arch
.cputype
= data
.GetU32(&offset
);
109 arch
.cpusubtype
= data
.GetU32(&offset
);
110 arch
.offset
= data
.GetU64(&offset
);
111 arch
.size
= data
.GetU64(&offset
);
112 arch
.align
= data
.GetU32(&offset
);
113 arch
.reserved
= data
.GetU32(&offset
);
114 fat_archs
.emplace_back(arch
);
117 arch
.cputype
= data
.GetU32(&offset
);
118 arch
.cpusubtype
= data
.GetU32(&offset
);
119 arch
.offset
= data
.GetU32(&offset
);
120 arch
.size
= data
.GetU32(&offset
);
121 arch
.align
= data
.GetU32(&offset
);
122 fat_archs
.emplace_back(arch
);
129 memset(&header
, 0, sizeof(header
));
133 size_t ObjectContainerUniversalMachO::GetNumArchitectures() const {
134 return m_header
.nfat_arch
;
137 bool ObjectContainerUniversalMachO::GetArchitectureAtIndex(
138 uint32_t idx
, ArchSpec
&arch
) const {
139 if (idx
< m_header
.nfat_arch
) {
140 arch
.SetArchitecture(eArchTypeMachO
, m_fat_archs
[idx
].GetCPUType(),
141 m_fat_archs
[idx
].GetCPUSubType());
148 ObjectContainerUniversalMachO::GetObjectFile(const FileSpec
*file
) {
149 uint32_t arch_idx
= 0;
151 // If the module hasn't specified an architecture yet, set it to the default
153 ModuleSP
module_sp(GetModule());
155 if (!module_sp
->GetArchitecture().IsValid()) {
156 arch
= Target::GetDefaultArchitecture();
158 arch
.SetTriple(LLDB_ARCH_DEFAULT
);
160 arch
= module_sp
->GetArchitecture();
163 // First, try to find an exact match for the Arch of the Target.
164 for (arch_idx
= 0; arch_idx
< m_header
.nfat_arch
; ++arch_idx
) {
165 if (GetArchitectureAtIndex(arch_idx
, curr_arch
) &&
166 arch
.IsExactMatch(curr_arch
))
170 // Failing an exact match, try to find a compatible Arch of the Target.
171 if (arch_idx
>= m_header
.nfat_arch
) {
172 for (arch_idx
= 0; arch_idx
< m_header
.nfat_arch
; ++arch_idx
) {
173 if (GetArchitectureAtIndex(arch_idx
, curr_arch
) &&
174 arch
.IsCompatibleMatch(curr_arch
))
179 if (arch_idx
< m_header
.nfat_arch
) {
180 DataBufferSP data_sp
;
181 lldb::offset_t data_offset
= 0;
182 return ObjectFile::FindPlugin(
183 module_sp
, file
, m_offset
+ m_fat_archs
[arch_idx
].GetOffset(),
184 m_fat_archs
[arch_idx
].GetSize(), data_sp
, data_offset
);
187 return ObjectFileSP();
190 size_t ObjectContainerUniversalMachO::GetModuleSpecifications(
191 const lldb_private::FileSpec
&file
, lldb::DataBufferSP
&data_sp
,
192 lldb::offset_t data_offset
, lldb::offset_t file_offset
,
193 lldb::offset_t file_size
, lldb_private::ModuleSpecList
&specs
) {
194 const size_t initial_count
= specs
.GetSize();
197 data
.SetData(data_sp
, data_offset
, data_sp
->GetByteSize());
199 if (ObjectContainerUniversalMachO::MagicBytesMatch(data
)) {
200 llvm::MachO::fat_header header
;
201 std::vector
<FatArch
> fat_archs
;
202 if (ParseHeader(data
, header
, fat_archs
)) {
203 for (const FatArch
&fat_arch
: fat_archs
) {
204 const lldb::offset_t slice_file_offset
=
205 fat_arch
.GetOffset() + file_offset
;
206 if (fat_arch
.GetOffset() < file_size
&& file_size
> slice_file_offset
) {
207 ObjectFile::GetModuleSpecifications(
208 file
, slice_file_offset
, file_size
- slice_file_offset
, specs
);
213 return specs
.GetSize() - initial_count
;