Symbols.cpp revision 1674b12bbc3dae7b9543b8c5f958e90ddc767fa4
1//===-- Symbols.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 "lldb/Host/Symbols.h"
11
12// C Includes
13#include <dirent.h>
14#include "llvm/Support/MachO.h"
15
16// C++ Includes
17// Other libraries and framework includes
18#include <CoreFoundation/CoreFoundation.h>
19
20// Project includes
21#include "lldb/Core/ArchSpec.h"
22#include "lldb/Core/DataBuffer.h"
23#include "lldb/Core/DataExtractor.h"
24#include "lldb/Core/Timer.h"
25#include "lldb/Core/UUID.h"
26
27#include "Host/macosx/cfcpp/CFCReleaser.h"
28
29using namespace lldb;
30using namespace lldb_private;
31using namespace llvm::MachO;
32
33extern "C" {
34
35CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
36CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
37
38}
39
40static bool
41SkinnyMachOFileContainsArchAndUUID
42(
43    const FileSpec &file_spec,
44    const ArchSpec *arch,
45    const UUID *uuid,   // the UUID we are looking for
46    off_t file_offset,
47    DataExtractor& data,
48    uint32_t data_offset,
49    const uint32_t magic
50)
51{
52    assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped);
53    if (magic == HeaderMagic32 || magic == HeaderMagic64)
54        data.SetByteOrder (eByteOrderHost);
55    else if (eByteOrderHost == eByteOrderBig)
56        data.SetByteOrder (eByteOrderLittle);
57    else
58        data.SetByteOrder (eByteOrderBig);
59
60    uint32_t i;
61    const uint32_t cputype      = data.GetU32(&data_offset);    // cpu specifier
62    const uint32_t cpusubtype   = data.GetU32(&data_offset);    // machine specifier
63    data_offset+=4; // Skip mach file type
64    const uint32_t ncmds        = data.GetU32(&data_offset);    // number of load commands
65    const uint32_t sizeofcmds   = data.GetU32(&data_offset);    // the size of all the load commands
66    data_offset+=4; // Skip flags
67
68    // Check the architecture if we have a valid arch pointer
69    if (arch)
70    {
71        ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype);
72
73        if (file_arch != *arch)
74            return false;
75    }
76
77    // The file exists, and if a valid arch pointer was passed in we know
78    // if already matches, so we can return if we aren't looking for a specific
79    // UUID
80    if (uuid == NULL)
81        return true;
82
83    if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
84        data_offset += 4;   // Skip reserved field for in mach_header_64
85
86    // Make sure we have enough data for all the load commands
87    if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
88    {
89        if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds)
90        {
91            DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds));
92            data.SetData (data_buffer_sp);
93        }
94    }
95    else
96    {
97        if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds)
98        {
99            DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds));
100            data.SetData (data_buffer_sp);
101        }
102    }
103
104    for (i=0; i<ncmds; i++)
105    {
106        const uint32_t cmd_offset = data_offset;    // Save this data_offset in case parsing of the segment goes awry!
107        uint32_t cmd        = data.GetU32(&data_offset);
108        uint32_t cmd_size   = data.GetU32(&data_offset);
109        if (cmd == LoadCommandUUID)
110        {
111            UUID file_uuid (data.GetData(&data_offset, 16), 16);
112            return file_uuid == *uuid;
113        }
114        data_offset = cmd_offset + cmd_size;
115    }
116    return false;
117}
118
119bool
120UniversalMachOFileContainsArchAndUUID
121(
122    const FileSpec &file_spec,
123    const ArchSpec *arch,
124    const UUID *uuid,
125    off_t file_offset,
126    DataExtractor& data,
127    uint32_t data_offset,
128    const uint32_t magic
129)
130{
131    assert(magic == UniversalMagic || magic == UniversalMagicSwapped);
132
133    // Universal mach-o files always have their headers encoded as BIG endian
134    data.SetByteOrder(eByteOrderBig);
135
136    uint32_t i;
137    const uint32_t nfat_arch = data.GetU32(&data_offset);   // number of structs that follow
138    const uint32_t fat_header_and_arch_size = sizeof(struct fat_header) + nfat_arch * sizeof(struct fat_arch);
139    if (data.GetByteSize() < fat_header_and_arch_size)
140    {
141        DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, fat_header_and_arch_size));
142        data.SetData (data_buffer_sp);
143    }
144
145    for (i=0; i<nfat_arch; i++)
146    {
147        cpu_type_t      arch_cputype        = data.GetU32(&data_offset);    // cpu specifier (int)
148        cpu_subtype_t   arch_cpusubtype     = data.GetU32(&data_offset);    // machine specifier (int)
149        uint32_t        arch_offset         = data.GetU32(&data_offset);    // file offset to this object file
150    //  uint32_t        arch_size           = data.GetU32(&data_offset);    // size of this object file
151    //  uint32_t        arch_align          = data.GetU32(&data_offset);    // alignment as a power of 2
152        data_offset += 8;   // Skip size and align as we don't need those
153        // Only process this slice if the cpu type/subtype matches
154        if (arch)
155        {
156            ArchSpec fat_arch(eArchTypeMachO, arch_cputype, arch_cpusubtype);
157            if (fat_arch != *arch)
158                continue;
159        }
160
161        // Create a buffer with only the arch slice date in it
162        DataExtractor arch_data;
163        DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset + arch_offset, 0x1000));
164        arch_data.SetData(data_buffer_sp);
165        uint32_t arch_data_offset = 0;
166        uint32_t arch_magic = arch_data.GetU32(&arch_data_offset);
167
168        switch (arch_magic)
169        {
170        case HeaderMagic32:
171        case HeaderMagic32Swapped:
172        case HeaderMagic64:
173        case HeaderMagic64Swapped:
174            if (SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset + arch_offset, arch_data, arch_data_offset, arch_magic))
175                return true;
176            break;
177        }
178    }
179    return false;
180}
181
182static bool
183FileAtPathContainsArchAndUUID
184(
185    const FileSpec &file_spec,
186    const ArchSpec *arch,
187    const UUID *uuid
188)
189{
190    DataExtractor data;
191    off_t file_offset = 0;
192    DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, 0x1000));
193
194    if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0)
195    {
196        data.SetData(data_buffer_sp);
197
198        uint32_t data_offset = 0;
199        uint32_t magic = data.GetU32(&data_offset);
200
201        switch (magic)
202        {
203        // 32 bit mach-o file
204        case HeaderMagic32:
205        case HeaderMagic32Swapped:
206        case HeaderMagic64:
207        case HeaderMagic64Swapped:
208            return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
209
210        // fat mach-o file
211        case UniversalMagic:
212        case UniversalMagicSwapped:
213            return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
214
215        default:
216            break;
217        }
218    }
219    return false;
220}
221
222static FileSpec
223LocateDSYMMachFileInDSYMBundle
224(
225    const FileSpec& dsym_bundle_fspec,
226    const UUID *uuid,
227    const ArchSpec *arch)
228{
229    char path[PATH_MAX];
230
231    FileSpec dsym_fspec;
232
233    if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
234    {
235        ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
236
237        DIR* dirp = ::opendir(path);
238        if (dirp != NULL)
239        {
240            const size_t path_len = strlen(path);
241            const int bytes_left = sizeof(path) - path_len - 1;
242            struct dirent* dp;
243            while ((dp = readdir(dirp)) != NULL)
244            {
245                // Only search directories
246                if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
247                {
248                    if (dp->d_namlen == 1 && dp->d_name[0] == '.')
249                        continue;
250
251                    if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
252                        continue;
253                }
254
255                if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
256                {
257                    ::strncpy (&path[path_len], dp->d_name, bytes_left);
258
259                    dsym_fspec.SetFile(path);
260                    if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid))
261                        return dsym_fspec;
262                }
263            }
264        }
265    }
266    dsym_fspec.Clear();
267    return dsym_fspec;
268}
269
270static int
271LocateMacOSXFilesUsingDebugSymbols
272(
273    const FileSpec *exec_fspec, // An executable path that may or may not be correct if UUID is specified
274    const ArchSpec* arch,       // Limit the search to files with this architecture if non-NULL
275    const UUID *uuid,           // Match the UUID value if non-NULL,
276    FileSpec *out_exec_fspec,   // If non-NULL, try and find the executable
277    FileSpec *out_dsym_fspec    // If non-NULL try and find the debug symbol file
278)
279{
280    int items_found = 0;
281
282    if (out_exec_fspec)
283        out_exec_fspec->Clear();
284
285    if (out_dsym_fspec)
286        out_dsym_fspec->Clear();
287
288    if (uuid && uuid->IsValid())
289    {
290        // Try and locate the dSYM file using DebugSymbols first
291        const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes();
292        if (module_uuid != NULL)
293        {
294            CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes ( NULL,
295                                                                            module_uuid[0],
296                                                                            module_uuid[1],
297                                                                            module_uuid[2],
298                                                                            module_uuid[3],
299                                                                            module_uuid[4],
300                                                                            module_uuid[5],
301                                                                            module_uuid[6],
302                                                                            module_uuid[7],
303                                                                            module_uuid[8],
304                                                                            module_uuid[9],
305                                                                            module_uuid[10],
306                                                                            module_uuid[11],
307                                                                            module_uuid[12],
308                                                                            module_uuid[13],
309                                                                            module_uuid[14],
310                                                                            module_uuid[15]));
311
312            if (module_uuid_ref.get())
313            {
314                CFCReleaser<CFURLRef> exec_url;
315
316                if (exec_fspec)
317                {
318                    char exec_cf_path[PATH_MAX];
319                    if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
320                        exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL,
321                                                                                  (const UInt8 *)exec_cf_path,
322                                                                                  strlen(exec_cf_path),
323                                                                                  FALSE));
324                }
325
326                CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
327                char path[PATH_MAX];
328
329                if (dsym_url.get())
330                {
331                    if (out_dsym_fspec)
332                    {
333                        if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
334                        {
335                            out_dsym_fspec->SetFile(path);
336
337                            if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory)
338                            {
339                                *out_dsym_fspec = LocateDSYMMachFileInDSYMBundle (*out_dsym_fspec, uuid, arch);
340                                if (*out_dsym_fspec)
341                                    ++items_found;
342                            }
343                            else
344                            {
345                                ++items_found;
346                            }
347                        }
348                    }
349
350                    if (out_exec_fspec)
351                    {
352                        CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));;
353                        if (dict.get())
354                        {
355                            CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (dict.get(), CFSTR("DBGSymbolRichExecutable")));
356                            if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path)))
357                            {
358                                ++items_found;
359                                out_dsym_fspec->SetFile(path);
360                            }
361                        }
362                    }
363                }
364            }
365        }
366    }
367    return items_found;
368}
369
370static bool
371LocateDSYMInVincinityOfExecutable (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid, FileSpec &dsym_fspec)
372{
373    if (exec_fspec)
374    {
375        char path[PATH_MAX];
376        if (exec_fspec->GetPath(path, sizeof(path)))
377        {
378            // Make sure the module isn't already just a dSYM file...
379            if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL)
380            {
381                size_t obj_file_path_length = strlen(path);
382                strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path));
383                strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path));
384
385                dsym_fspec.SetFile(path);
386
387                if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid))
388                {
389                    return true;
390                }
391                else
392                {
393                    path[obj_file_path_length] = '\0';
394
395                    char *last_dot = strrchr(path, '.');
396                    while (last_dot != NULL && last_dot[0])
397                    {
398                        char *next_slash = strchr(last_dot, '/');
399                        if (next_slash != NULL)
400                        {
401                            *next_slash = '\0';
402                            strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path));
403                            strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path));
404                            dsym_fspec.SetFile(path);
405                            if (dsym_fspec.Exists())
406                                return true;
407                            else
408                            {
409                                *last_dot = '\0';
410                                char *prev_slash = strrchr(path, '/');
411                                if (prev_slash != NULL)
412                                    *prev_slash = '\0';
413                                else
414                                    break;
415                            }
416                        }
417                        else
418                        {
419                            break;
420                        }
421                    }
422                }
423            }
424        }
425    }
426    dsym_fspec.Clear();
427    return false;
428}
429
430FileSpec
431Symbols::LocateExecutableObjectFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid)
432{
433    Timer scoped_timer (__PRETTY_FUNCTION__,
434                        "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
435                        exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>",
436                        arch ? arch->AsCString() : "<NULL>",
437                        uuid);
438
439    FileSpec objfile_fspec;
440    if (exec_fspec && FileAtPathContainsArchAndUUID (*exec_fspec, arch, uuid))
441        objfile_fspec = *exec_fspec;
442    else
443        LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, &objfile_fspec, NULL);
444    return objfile_fspec;
445}
446
447FileSpec
448Symbols::LocateExecutableSymbolFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid)
449{
450    Timer scoped_timer (__PRETTY_FUNCTION__,
451                        "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)",
452                        exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>",
453                        arch ? arch->AsCString() : "<NULL>",
454                        uuid);
455
456    FileSpec symbol_fspec;
457    // First try and find the dSYM in the same directory as the executable or in
458    // an appropriate parent directory
459    if (LocateDSYMInVincinityOfExecutable (exec_fspec, arch, uuid, symbol_fspec) == false)
460    {
461        // We failed to easily find the dSYM above, so use DebugSymbols
462        LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, NULL, &symbol_fspec);
463    }
464    return symbol_fspec;
465}
466