1 //===-- ObjectContainerUniversalMachO.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 "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"
18 
19 using namespace lldb;
20 using namespace lldb_private;
21 using namespace llvm::MachO;
22 
LLDB_PLUGIN_DEFINE_ADV(ObjectContainerUniversalMachO,ObjectContainerMachOArchive)23 LLDB_PLUGIN_DEFINE_ADV(ObjectContainerUniversalMachO,
24                        ObjectContainerMachOArchive)
25 
26 void ObjectContainerUniversalMachO::Initialize() {
27   PluginManager::RegisterPlugin(GetPluginNameStatic(),
28                                 GetPluginDescriptionStatic(), CreateInstance,
29                                 GetModuleSpecifications);
30 }
31 
Terminate()32 void ObjectContainerUniversalMachO::Terminate() {
33   PluginManager::UnregisterPlugin(CreateInstance);
34 }
35 
CreateInstance(const lldb::ModuleSP & module_sp,DataBufferSP & data_sp,lldb::offset_t data_offset,const FileSpec * file,lldb::offset_t file_offset,lldb::offset_t length)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
42   if (data_sp) {
43     DataExtractor 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();
51       }
52     }
53   }
54   return nullptr;
55 }
56 
MagicBytesMatch(const DataExtractor & data)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;
62 }
63 
ObjectContainerUniversalMachO(const lldb::ModuleSP & module_sp,DataBufferSP & data_sp,lldb::offset_t data_offset,const FileSpec * file,lldb::offset_t file_offset,lldb::offset_t length)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,
69                       data_offset),
70       m_header(), m_fat_archs() {
71   memset(&m_header, 0, sizeof(m_header));
72 }
73 
74 ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() = default;
75 
ParseHeader()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
80   m_data.Clear();
81   return success;
82 }
83 
ParseHeader(lldb_private::DataExtractor & data,llvm::MachO::fat_header & header,std::vector<FatArch> & 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);
92   fat_archs.clear();
93 
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);
98 
99     header.nfat_arch = data.GetU32(&offset);
100 
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
103     // contains.
104     for (uint32_t arch_idx = 0; arch_idx < header.nfat_arch; ++arch_idx) {
105       if (data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch))) {
106         if (is_fat64) {
107           fat_arch_64 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);
115         } else {
116           fat_arch 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);
123         }
124       }
125     }
126     return true;
127   }
128 
129   memset(&header, 0, sizeof(header));
130   return true;
131 }
132 
GetNumArchitectures() const133 size_t ObjectContainerUniversalMachO::GetNumArchitectures() const {
134   return m_header.nfat_arch;
135 }
136 
GetArchitectureAtIndex(uint32_t idx,ArchSpec & arch) const137 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());
142     return true;
143   }
144   return false;
145 }
146 
147 ObjectFileSP
GetObjectFile(const FileSpec * file)148 ObjectContainerUniversalMachO::GetObjectFile(const FileSpec *file) {
149   uint32_t arch_idx = 0;
150   ArchSpec arch;
151   // If the module hasn't specified an architecture yet, set it to the default
152   // architecture:
153   ModuleSP module_sp(GetModule());
154   if (module_sp) {
155     if (!module_sp->GetArchitecture().IsValid()) {
156       arch = Target::GetDefaultArchitecture();
157       if (!arch.IsValid())
158         arch.SetTriple(LLDB_ARCH_DEFAULT);
159     } else
160       arch = module_sp->GetArchitecture();
161 
162     ArchSpec curr_arch;
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))
167         break;
168     }
169 
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))
175           break;
176       }
177     }
178 
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);
185     }
186   }
187   return ObjectFileSP();
188 }
189 
GetModuleSpecifications(const lldb_private::FileSpec & file,lldb::DataBufferSP & data_sp,lldb::offset_t data_offset,lldb::offset_t file_offset,lldb::offset_t file_size,lldb_private::ModuleSpecList & specs)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();
195 
196   DataExtractor data;
197   data.SetData(data_sp, data_offset, data_sp->GetByteSize());
198 
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);
209         }
210       }
211     }
212   }
213   return specs.GetSize() - initial_count;
214 }
215