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