1 //===-- ObjectContainerUniversalMachO.cpp -----------------------*- C++ -*-===//
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 
23 void ObjectContainerUniversalMachO::Initialize() {
24   PluginManager::RegisterPlugin(GetPluginNameStatic(),
25                                 GetPluginDescriptionStatic(), CreateInstance,
26                                 GetModuleSpecifications);
27 }
28 
29 void ObjectContainerUniversalMachO::Terminate() {
30   PluginManager::UnregisterPlugin(CreateInstance);
31 }
32 
33 lldb_private::ConstString ObjectContainerUniversalMachO::GetPluginNameStatic() {
34   static ConstString g_name("mach-o");
35   return g_name;
36 }
37 
38 const char *ObjectContainerUniversalMachO::GetPluginDescriptionStatic() {
39   return "Universal mach-o object container reader.";
40 }
41 
42 ObjectContainer *ObjectContainerUniversalMachO::CreateInstance(
43     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
44     lldb::offset_t data_offset, const FileSpec *file,
45     lldb::offset_t file_offset, lldb::offset_t length) {
46   // We get data when we aren't trying to look for cached container
47   // information, so only try and look for an architecture slice if we get data
48   if (data_sp) {
49     DataExtractor data;
50     data.SetData(data_sp, data_offset, length);
51     if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) {
52       std::unique_ptr<ObjectContainerUniversalMachO> container_up(
53           new ObjectContainerUniversalMachO(module_sp, data_sp, data_offset,
54                                             file, file_offset, length));
55       if (container_up->ParseHeader()) {
56         return container_up.release();
57       }
58     }
59   }
60   return nullptr;
61 }
62 
63 bool ObjectContainerUniversalMachO::MagicBytesMatch(const DataExtractor &data) {
64   lldb::offset_t offset = 0;
65   uint32_t magic = data.GetU32(&offset);
66   return magic == FAT_MAGIC || magic == FAT_CIGAM;
67 }
68 
69 ObjectContainerUniversalMachO::ObjectContainerUniversalMachO(
70     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
71     lldb::offset_t data_offset, const FileSpec *file,
72     lldb::offset_t file_offset, lldb::offset_t length)
73     : ObjectContainer(module_sp, file, file_offset, length, data_sp,
74                       data_offset),
75       m_header(), m_fat_archs() {
76   memset(&m_header, 0, sizeof(m_header));
77 }
78 
79 ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() {}
80 
81 bool ObjectContainerUniversalMachO::ParseHeader() {
82   bool success = ParseHeader(m_data, m_header, m_fat_archs);
83   // We no longer need any data, we parsed all we needed to parse and cached it
84   // in m_header and m_fat_archs
85   m_data.Clear();
86   return success;
87 }
88 
89 bool ObjectContainerUniversalMachO::ParseHeader(
90     lldb_private::DataExtractor &data, llvm::MachO::fat_header &header,
91     std::vector<llvm::MachO::fat_arch> &fat_archs) {
92   bool success = false;
93   // Store the file offset for this universal file as we could have a universal
94   // .o file in a BSD archive, or be contained in another kind of object.
95   // Universal mach-o files always have their headers in big endian.
96   lldb::offset_t offset = 0;
97   data.SetByteOrder(eByteOrderBig);
98   header.magic = data.GetU32(&offset);
99   fat_archs.clear();
100 
101   if (header.magic == FAT_MAGIC) {
102 
103     data.SetAddressByteSize(4);
104 
105     header.nfat_arch = data.GetU32(&offset);
106 
107     // Now we should have enough data for all of the fat headers, so lets index
108     // them so we know how many architectures that this universal binary
109     // contains.
110     uint32_t arch_idx = 0;
111     for (arch_idx = 0; arch_idx < header.nfat_arch; ++arch_idx) {
112       if (data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch))) {
113         fat_arch arch;
114         if (data.GetU32(&offset, &arch, sizeof(fat_arch) / sizeof(uint32_t)))
115           fat_archs.push_back(arch);
116       }
117     }
118     success = true;
119   } else {
120     memset(&header, 0, sizeof(header));
121   }
122   return success;
123 }
124 
125 void ObjectContainerUniversalMachO::Dump(Stream *s) const {
126   s->Printf("%p: ", static_cast<const void *>(this));
127   s->Indent();
128   const size_t num_archs = GetNumArchitectures();
129   const size_t num_objects = GetNumObjects();
130   s->Printf("ObjectContainerUniversalMachO, num_archs = %zu, num_objects = %zu",
131             num_archs, num_objects);
132   uint32_t i;
133   ArchSpec arch;
134   s->IndentMore();
135   for (i = 0; i < num_archs; i++) {
136     s->Indent();
137     GetArchitectureAtIndex(i, arch);
138     s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName());
139   }
140   for (i = 0; i < num_objects; i++) {
141     s->Indent();
142     s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex(i));
143   }
144   s->IndentLess();
145   s->EOL();
146 }
147 
148 size_t ObjectContainerUniversalMachO::GetNumArchitectures() const {
149   return m_header.nfat_arch;
150 }
151 
152 bool ObjectContainerUniversalMachO::GetArchitectureAtIndex(
153     uint32_t idx, ArchSpec &arch) const {
154   if (idx < m_header.nfat_arch) {
155     arch.SetArchitecture(eArchTypeMachO, m_fat_archs[idx].cputype,
156                          m_fat_archs[idx].cpusubtype);
157     return true;
158   }
159   return false;
160 }
161 
162 ObjectFileSP
163 ObjectContainerUniversalMachO::GetObjectFile(const FileSpec *file) {
164   uint32_t arch_idx = 0;
165   ArchSpec arch;
166   // If the module hasn't specified an architecture yet, set it to the default
167   // architecture:
168   ModuleSP module_sp(GetModule());
169   if (module_sp) {
170     if (!module_sp->GetArchitecture().IsValid()) {
171       arch = Target::GetDefaultArchitecture();
172       if (!arch.IsValid())
173         arch.SetTriple(LLDB_ARCH_DEFAULT);
174     } else
175       arch = module_sp->GetArchitecture();
176 
177     ArchSpec curr_arch;
178     // First, try to find an exact match for the Arch of the Target.
179     for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) {
180       if (GetArchitectureAtIndex(arch_idx, curr_arch) &&
181           arch.IsExactMatch(curr_arch))
182         break;
183     }
184 
185     // Failing an exact match, try to find a compatible Arch of the Target.
186     if (arch_idx >= m_header.nfat_arch) {
187       for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) {
188         if (GetArchitectureAtIndex(arch_idx, curr_arch) &&
189             arch.IsCompatibleMatch(curr_arch))
190           break;
191       }
192     }
193 
194     if (arch_idx < m_header.nfat_arch) {
195       DataBufferSP data_sp;
196       lldb::offset_t data_offset = 0;
197       return ObjectFile::FindPlugin(
198           module_sp, file, m_offset + m_fat_archs[arch_idx].offset,
199           m_fat_archs[arch_idx].size, data_sp, data_offset);
200     }
201   }
202   return ObjectFileSP();
203 }
204 
205 // PluginInterface protocol
206 lldb_private::ConstString ObjectContainerUniversalMachO::GetPluginName() {
207   return GetPluginNameStatic();
208 }
209 
210 uint32_t ObjectContainerUniversalMachO::GetPluginVersion() { return 1; }
211 
212 size_t ObjectContainerUniversalMachO::GetModuleSpecifications(
213     const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
214     lldb::offset_t data_offset, lldb::offset_t file_offset,
215     lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
216   const size_t initial_count = specs.GetSize();
217 
218   DataExtractor data;
219   data.SetData(data_sp, data_offset, data_sp->GetByteSize());
220 
221   if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) {
222     llvm::MachO::fat_header header;
223     std::vector<llvm::MachO::fat_arch> fat_archs;
224     if (ParseHeader(data, header, fat_archs)) {
225       for (const llvm::MachO::fat_arch &fat_arch : fat_archs) {
226         const lldb::offset_t slice_file_offset = fat_arch.offset + file_offset;
227         if (fat_arch.offset < file_size && file_size > slice_file_offset) {
228           ObjectFile::GetModuleSpecifications(
229               file, slice_file_offset, file_size - slice_file_offset, specs);
230         }
231       }
232     }
233   }
234   return specs.GetSize() - initial_count;
235 }
236