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;
61 }
62 
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)63 ObjectContainerUniversalMachO::ObjectContainerUniversalMachO(
64     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
65     lldb::offset_t data_offset, const FileSpec *file,
66     lldb::offset_t file_offset, lldb::offset_t length)
67     : ObjectContainer(module_sp, file, file_offset, length, data_sp,
68                       data_offset),
69       m_header(), m_fat_archs() {
70   memset(&m_header, 0, sizeof(m_header));
71 }
72 
73 ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() = default;
74 
ParseHeader()75 bool ObjectContainerUniversalMachO::ParseHeader() {
76   bool success = ParseHeader(m_data, m_header, m_fat_archs);
77   // We no longer need any data, we parsed all we needed to parse and cached it
78   // in m_header and m_fat_archs
79   m_data.Clear();
80   return success;
81 }
82 
ParseHeader(lldb_private::DataExtractor & data,llvm::MachO::fat_header & header,std::vector<llvm::MachO::fat_arch> & fat_archs)83 bool ObjectContainerUniversalMachO::ParseHeader(
84     lldb_private::DataExtractor &data, llvm::MachO::fat_header &header,
85     std::vector<llvm::MachO::fat_arch> &fat_archs) {
86   bool success = false;
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   // Universal mach-o files always have their headers in big endian.
90   lldb::offset_t offset = 0;
91   data.SetByteOrder(eByteOrderBig);
92   header.magic = data.GetU32(&offset);
93   fat_archs.clear();
94 
95   if (header.magic == FAT_MAGIC) {
96 
97     data.SetAddressByteSize(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     uint32_t arch_idx = 0;
105     for (arch_idx = 0; arch_idx < header.nfat_arch; ++arch_idx) {
106       if (data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch))) {
107         fat_arch arch;
108         if (data.GetU32(&offset, &arch, sizeof(fat_arch) / sizeof(uint32_t)))
109           fat_archs.push_back(arch);
110       }
111     }
112     success = true;
113   } else {
114     memset(&header, 0, sizeof(header));
115   }
116   return success;
117 }
118 
GetNumArchitectures() const119 size_t ObjectContainerUniversalMachO::GetNumArchitectures() const {
120   return m_header.nfat_arch;
121 }
122 
GetArchitectureAtIndex(uint32_t idx,ArchSpec & arch) const123 bool ObjectContainerUniversalMachO::GetArchitectureAtIndex(
124     uint32_t idx, ArchSpec &arch) const {
125   if (idx < m_header.nfat_arch) {
126     arch.SetArchitecture(eArchTypeMachO, m_fat_archs[idx].cputype,
127                          m_fat_archs[idx].cpusubtype);
128     return true;
129   }
130   return false;
131 }
132 
133 ObjectFileSP
GetObjectFile(const FileSpec * file)134 ObjectContainerUniversalMachO::GetObjectFile(const FileSpec *file) {
135   uint32_t arch_idx = 0;
136   ArchSpec arch;
137   // If the module hasn't specified an architecture yet, set it to the default
138   // architecture:
139   ModuleSP module_sp(GetModule());
140   if (module_sp) {
141     if (!module_sp->GetArchitecture().IsValid()) {
142       arch = Target::GetDefaultArchitecture();
143       if (!arch.IsValid())
144         arch.SetTriple(LLDB_ARCH_DEFAULT);
145     } else
146       arch = module_sp->GetArchitecture();
147 
148     ArchSpec curr_arch;
149     // First, try to find an exact match for the Arch of the Target.
150     for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) {
151       if (GetArchitectureAtIndex(arch_idx, curr_arch) &&
152           arch.IsExactMatch(curr_arch))
153         break;
154     }
155 
156     // Failing an exact match, try to find a compatible Arch of the Target.
157     if (arch_idx >= m_header.nfat_arch) {
158       for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) {
159         if (GetArchitectureAtIndex(arch_idx, curr_arch) &&
160             arch.IsCompatibleMatch(curr_arch))
161           break;
162       }
163     }
164 
165     if (arch_idx < m_header.nfat_arch) {
166       DataBufferSP data_sp;
167       lldb::offset_t data_offset = 0;
168       return ObjectFile::FindPlugin(
169           module_sp, file, m_offset + m_fat_archs[arch_idx].offset,
170           m_fat_archs[arch_idx].size, data_sp, data_offset);
171     }
172   }
173   return ObjectFileSP();
174 }
175 
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)176 size_t ObjectContainerUniversalMachO::GetModuleSpecifications(
177     const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
178     lldb::offset_t data_offset, lldb::offset_t file_offset,
179     lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
180   const size_t initial_count = specs.GetSize();
181 
182   DataExtractor data;
183   data.SetData(data_sp, data_offset, data_sp->GetByteSize());
184 
185   if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) {
186     llvm::MachO::fat_header header;
187     std::vector<llvm::MachO::fat_arch> fat_archs;
188     if (ParseHeader(data, header, fat_archs)) {
189       for (const llvm::MachO::fat_arch &fat_arch : fat_archs) {
190         const lldb::offset_t slice_file_offset = fat_arch.offset + file_offset;
191         if (fat_arch.offset < file_size && file_size > slice_file_offset) {
192           ObjectFile::GetModuleSpecifications(
193               file, slice_file_offset, file_size - slice_file_offset, specs);
194         }
195       }
196     }
197   }
198   return specs.GetSize() - initial_count;
199 }
200