1 //===-- ObjectContainerUniversalMachO.cpp -----------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "ObjectContainerUniversalMachO.h"
11 #include "lldb/Core/ArchSpec.h"
12 #include "lldb/Core/DataBuffer.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/ModuleSpec.h"
15 #include "lldb/Core/PluginManager.h"
16 #include "lldb/Core/Stream.h"
17 #include "lldb/Symbol/ObjectFile.h"
18 #include "lldb/Target/Target.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace llvm::MachO;
23 
24 void
25 ObjectContainerUniversalMachO::Initialize()
26 {
27     PluginManager::RegisterPlugin (GetPluginNameStatic(),
28                                    GetPluginDescriptionStatic(),
29                                    CreateInstance,
30                                    GetModuleSpecifications);
31 }
32 
33 void
34 ObjectContainerUniversalMachO::Terminate()
35 {
36     PluginManager::UnregisterPlugin (CreateInstance);
37 }
38 
39 
40 const char *
41 ObjectContainerUniversalMachO::GetPluginNameStatic()
42 {
43     return "object-container.mach-o";
44 }
45 
46 const char *
47 ObjectContainerUniversalMachO::GetPluginDescriptionStatic()
48 {
49     return "Universal mach-o object container reader.";
50 }
51 
52 
53 ObjectContainer *
54 ObjectContainerUniversalMachO::CreateInstance
55 (
56     const lldb::ModuleSP &module_sp,
57     DataBufferSP& data_sp,
58     lldb::offset_t data_offset,
59     const FileSpec *file,
60     lldb::offset_t file_offset,
61     lldb::offset_t length
62 )
63 {
64     // We get data when we aren't trying to look for cached container information,
65     // so only try and look for an architecture slice if we get data
66     if (data_sp)
67     {
68         DataExtractor data;
69         data.SetData (data_sp, data_offset, length);
70         if (ObjectContainerUniversalMachO::MagicBytesMatch(data))
71         {
72             std::unique_ptr<ObjectContainerUniversalMachO> container_ap(new ObjectContainerUniversalMachO (module_sp, data_sp, data_offset, file, file_offset, length));
73             if (container_ap->ParseHeader())
74             {
75                 return container_ap.release();
76             }
77         }
78     }
79     return NULL;
80 }
81 
82 bool
83 ObjectContainerUniversalMachO::MagicBytesMatch (const DataExtractor &data)
84 {
85     lldb::offset_t offset = 0;
86     uint32_t magic = data.GetU32(&offset);
87     return magic == UniversalMagic || magic == UniversalMagicSwapped;
88 }
89 
90 ObjectContainerUniversalMachO::ObjectContainerUniversalMachO
91 (
92     const lldb::ModuleSP &module_sp,
93     DataBufferSP& data_sp,
94     lldb::offset_t data_offset,
95     const FileSpec *file,
96     lldb::offset_t file_offset,
97     lldb::offset_t length
98 ) :
99     ObjectContainer (module_sp, file, file_offset, length, data_sp, data_offset),
100     m_header(),
101     m_fat_archs()
102 {
103     memset(&m_header, 0, sizeof(m_header));
104 }
105 
106 
107 ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO()
108 {
109 }
110 
111 bool
112 ObjectContainerUniversalMachO::ParseHeader ()
113 {
114     bool success = ParseHeader (m_data, m_header, m_fat_archs);
115     // We no longer need any data, we parsed all we needed to parse
116     // and cached it in m_header and m_fat_archs
117     m_data.Clear();
118     return success;
119 }
120 
121 bool
122 ObjectContainerUniversalMachO::ParseHeader (lldb_private::DataExtractor &data,
123                                             llvm::MachO::fat_header &header,
124                                             std::vector<llvm::MachO::fat_arch> &fat_archs)
125 {
126     bool success = false;
127     // Store the file offset for this universal file as we could have a universal .o file
128     // in a BSD archive, or be contained in another kind of object.
129     // Universal mach-o files always have their headers in big endian.
130     lldb::offset_t offset = 0;
131     data.SetByteOrder (eByteOrderBig);
132     header.magic = data.GetU32(&offset);
133     fat_archs.clear();
134 
135     if (header.magic == UniversalMagic)
136     {
137 
138         data.SetAddressByteSize(4);
139 
140         header.nfat_arch = data.GetU32(&offset);
141 
142         // Now we should have enough data for all of the fat headers, so lets index
143         // them so we know how many architectures that this universal binary contains.
144         uint32_t arch_idx = 0;
145         for (arch_idx = 0; arch_idx < header.nfat_arch; ++arch_idx)
146         {
147             if (data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch)))
148             {
149                 fat_arch arch;
150                 if (data.GetU32(&offset, &arch, sizeof(fat_arch)/sizeof(uint32_t)))
151                     fat_archs.push_back(arch);
152             }
153         }
154         success = true;
155     }
156     else
157     {
158         memset(&header, 0, sizeof(header));
159     }
160     return success;
161 }
162 
163 void
164 ObjectContainerUniversalMachO::Dump (Stream *s) const
165 {
166     s->Printf("%p: ", this);
167     s->Indent();
168     const size_t num_archs = GetNumArchitectures();
169     const size_t num_objects = GetNumObjects();
170     s->Printf("ObjectContainerUniversalMachO, num_archs = %lu, num_objects = %lu", num_archs, num_objects);
171     uint32_t i;
172     ArchSpec arch;
173     s->IndentMore();
174     for (i=0; i<num_archs; i++)
175     {
176         s->Indent();
177         GetArchitectureAtIndex(i, arch);
178         s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName());
179     }
180     for (i=0; i<num_objects; i++)
181     {
182         s->Indent();
183         s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i));
184     }
185     s->IndentLess();
186     s->EOL();
187 }
188 
189 size_t
190 ObjectContainerUniversalMachO::GetNumArchitectures () const
191 {
192     return m_header.nfat_arch;
193 }
194 
195 bool
196 ObjectContainerUniversalMachO::GetArchitectureAtIndex (uint32_t idx, ArchSpec& arch) const
197 {
198     if (idx < m_header.nfat_arch)
199     {
200         arch.SetArchitecture (eArchTypeMachO, m_fat_archs[idx].cputype, m_fat_archs[idx].cpusubtype);
201         return true;
202     }
203     return false;
204 }
205 
206 ObjectFileSP
207 ObjectContainerUniversalMachO::GetObjectFile (const FileSpec *file)
208 {
209     uint32_t arch_idx = 0;
210     ArchSpec arch;
211     // If the module hasn't specified an architecture yet, set it to the default
212     // architecture:
213     ModuleSP module_sp (GetModule());
214     if (module_sp)
215     {
216         if (!module_sp->GetArchitecture().IsValid())
217         {
218             arch = Target::GetDefaultArchitecture ();
219             if (!arch.IsValid())
220                 arch.SetTriple (LLDB_ARCH_DEFAULT);
221         }
222         else
223             arch = module_sp->GetArchitecture();
224 
225         ArchSpec curr_arch;
226         // First, try to find an exact match for the Arch of the Target.
227         for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx)
228         {
229             if (GetArchitectureAtIndex (arch_idx, curr_arch) && arch.IsExactMatch(curr_arch))
230                 break;
231         }
232 
233         // Failing an exact match, try to find a compatible Arch of the Target.
234         if (arch_idx >= m_header.nfat_arch)
235         {
236             for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx)
237             {
238                 if (GetArchitectureAtIndex (arch_idx, curr_arch) && arch.IsCompatibleMatch(curr_arch))
239                     break;
240             }
241         }
242 
243         if (arch_idx < m_header.nfat_arch)
244         {
245             DataBufferSP data_sp;
246             lldb::offset_t data_offset = 0;
247             return ObjectFile::FindPlugin (module_sp,
248                                            file,
249                                            m_offset + m_fat_archs[arch_idx].offset,
250                                            m_fat_archs[arch_idx].size,
251                                            data_sp,
252                                            data_offset);
253         }
254     }
255     return ObjectFileSP();
256 }
257 
258 
259 //------------------------------------------------------------------
260 // PluginInterface protocol
261 //------------------------------------------------------------------
262 const char *
263 ObjectContainerUniversalMachO::GetPluginName()
264 {
265     return "ObjectContainerUniversalMachO";
266 }
267 
268 const char *
269 ObjectContainerUniversalMachO::GetShortPluginName()
270 {
271     return GetPluginNameStatic();
272 }
273 
274 uint32_t
275 ObjectContainerUniversalMachO::GetPluginVersion()
276 {
277     return 1;
278 }
279 
280 
281 size_t
282 ObjectContainerUniversalMachO::GetModuleSpecifications (const lldb_private::FileSpec& file,
283                                                         lldb::DataBufferSP& data_sp,
284                                                         lldb::offset_t data_offset,
285                                                         lldb::offset_t file_offset,
286                                                         lldb::offset_t length,
287                                                         lldb_private::ModuleSpecList &specs)
288 {
289     const size_t initial_count = specs.GetSize();
290 
291     DataExtractor data;
292     data.SetData (data_sp, data_offset, length);
293 
294     if (ObjectContainerUniversalMachO::MagicBytesMatch(data))
295     {
296         llvm::MachO::fat_header header;
297         std::vector<llvm::MachO::fat_arch> fat_archs;
298         if (ParseHeader (data, header, fat_archs))
299         {
300             for (const llvm::MachO::fat_arch &fat_arch : fat_archs)
301             {
302                 ObjectFile::GetModuleSpecifications (file,
303                                                      fat_arch.offset + file_offset,
304                                                      specs);
305             }
306         }
307     }
308     return specs.GetSize() - initial_count;
309 }
310 
311