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 <pwd.h>
15#include "llvm/Support/MachO.h"
16
17// C++ Includes
18// Other libraries and framework includes
19#include <CoreFoundation/CoreFoundation.h>
20
21// Project includes
22#include "lldb/Core/ArchSpec.h"
23#include "lldb/Core/DataBuffer.h"
24#include "lldb/Core/DataExtractor.h"
25#include "lldb/Core/Module.h"
26#include "lldb/Core/ModuleSpec.h"
27#include "lldb/Core/StreamString.h"
28#include "lldb/Core/Timer.h"
29#include "lldb/Core/UUID.h"
30#include "lldb/Host/Endian.h"
31#include "lldb/Host/Host.h"
32#include "lldb/Utility/CleanUp.h"
33#include "Host/macosx/cfcpp/CFCBundle.h"
34#include "Host/macosx/cfcpp/CFCData.h"
35#include "Host/macosx/cfcpp/CFCReleaser.h"
36#include "Host/macosx/cfcpp/CFCString.h"
37#include "mach/machine.h"
38
39
40using namespace lldb;
41using namespace lldb_private;
42using namespace llvm::MachO;
43
44#if !defined (__arm__) // No DebugSymbols on the iOS devices
45extern "C" {
46
47CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
48CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
49
50}
51#endif
52
53static bool
54SkinnyMachOFileContainsArchAndUUID
55(
56    const FileSpec &file_spec,
57    const ArchSpec *arch,
58    const lldb_private::UUID *uuid,   // the UUID we are looking for
59    off_t file_offset,
60    DataExtractor& data,
61    lldb::offset_t data_offset,
62    const uint32_t magic
63)
64{
65    assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped);
66    if (magic == HeaderMagic32 || magic == HeaderMagic64)
67        data.SetByteOrder (lldb::endian::InlHostByteOrder());
68    else if (lldb::endian::InlHostByteOrder() == eByteOrderBig)
69        data.SetByteOrder (eByteOrderLittle);
70    else
71        data.SetByteOrder (eByteOrderBig);
72
73    uint32_t i;
74    const uint32_t cputype      = data.GetU32(&data_offset);    // cpu specifier
75    const uint32_t cpusubtype   = data.GetU32(&data_offset);    // machine specifier
76    data_offset+=4; // Skip mach file type
77    const uint32_t ncmds        = data.GetU32(&data_offset);    // number of load commands
78    const uint32_t sizeofcmds   = data.GetU32(&data_offset);    // the size of all the load commands
79    data_offset+=4; // Skip flags
80
81    // Check the architecture if we have a valid arch pointer
82    if (arch)
83    {
84        ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype);
85
86        if (!file_arch.IsCompatibleMatch(*arch))
87            return false;
88    }
89
90    // The file exists, and if a valid arch pointer was passed in we know
91    // if already matches, so we can return if we aren't looking for a specific
92    // UUID
93    if (uuid == NULL)
94        return true;
95
96    if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
97        data_offset += 4;   // Skip reserved field for in mach_header_64
98
99    // Make sure we have enough data for all the load commands
100    if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
101    {
102        if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds)
103        {
104            DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds));
105            data.SetData (data_buffer_sp);
106        }
107    }
108    else
109    {
110        if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds)
111        {
112            DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds));
113            data.SetData (data_buffer_sp);
114        }
115    }
116
117    for (i=0; i<ncmds; i++)
118    {
119        const lldb::offset_t cmd_offset = data_offset;    // Save this data_offset in case parsing of the segment goes awry!
120        uint32_t cmd        = data.GetU32(&data_offset);
121        uint32_t cmd_size   = data.GetU32(&data_offset);
122        if (cmd == LoadCommandUUID)
123        {
124            lldb_private::UUID file_uuid (data.GetData(&data_offset, 16), 16);
125            if (file_uuid == *uuid)
126                return true;
127            return false;
128        }
129        data_offset = cmd_offset + cmd_size;
130    }
131    return false;
132}
133
134bool
135UniversalMachOFileContainsArchAndUUID
136(
137    const FileSpec &file_spec,
138    const ArchSpec *arch,
139    const lldb_private::UUID *uuid,
140    off_t file_offset,
141    DataExtractor& data,
142    lldb::offset_t data_offset,
143    const uint32_t magic
144)
145{
146    assert(magic == UniversalMagic || magic == UniversalMagicSwapped);
147
148    // Universal mach-o files always have their headers encoded as BIG endian
149    data.SetByteOrder(eByteOrderBig);
150
151    uint32_t i;
152    const uint32_t nfat_arch = data.GetU32(&data_offset);   // number of structs that follow
153    const uint32_t fat_header_and_arch_size = sizeof(struct fat_header) + nfat_arch * sizeof(struct fat_arch);
154    if (data.GetByteSize() < fat_header_and_arch_size)
155    {
156        DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, fat_header_and_arch_size));
157        data.SetData (data_buffer_sp);
158    }
159
160    for (i=0; i<nfat_arch; i++)
161    {
162        cpu_type_t      arch_cputype        = data.GetU32(&data_offset);    // cpu specifier (int)
163        cpu_subtype_t   arch_cpusubtype     = data.GetU32(&data_offset);    // machine specifier (int)
164        uint32_t        arch_offset         = data.GetU32(&data_offset);    // file offset to this object file
165    //  uint32_t        arch_size           = data.GetU32(&data_offset);    // size of this object file
166    //  uint32_t        arch_align          = data.GetU32(&data_offset);    // alignment as a power of 2
167        data_offset += 8;   // Skip size and align as we don't need those
168        // Only process this slice if the cpu type/subtype matches
169        if (arch)
170        {
171            ArchSpec fat_arch(eArchTypeMachO, arch_cputype, arch_cpusubtype);
172            if (!fat_arch.IsExactMatch(*arch))
173                continue;
174        }
175
176        // Create a buffer with only the arch slice date in it
177        DataExtractor arch_data;
178        DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset + arch_offset, 0x1000));
179        arch_data.SetData(data_buffer_sp);
180        lldb::offset_t arch_data_offset = 0;
181        uint32_t arch_magic = arch_data.GetU32(&arch_data_offset);
182
183        switch (arch_magic)
184        {
185        case HeaderMagic32:
186        case HeaderMagic32Swapped:
187        case HeaderMagic64:
188        case HeaderMagic64Swapped:
189            if (SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset + arch_offset, arch_data, arch_data_offset, arch_magic))
190                return true;
191            break;
192        }
193    }
194    return false;
195}
196
197static bool
198FileAtPathContainsArchAndUUID
199(
200    const FileSpec &file_spec,
201    const ArchSpec *arch,
202    const lldb_private::UUID *uuid
203)
204{
205    DataExtractor data;
206    off_t file_offset = 0;
207    DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, 0x1000));
208
209    if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0)
210    {
211        data.SetData(data_buffer_sp);
212
213        lldb::offset_t data_offset = 0;
214        uint32_t magic = data.GetU32(&data_offset);
215
216        switch (magic)
217        {
218        // 32 bit mach-o file
219        case HeaderMagic32:
220        case HeaderMagic32Swapped:
221        case HeaderMagic64:
222        case HeaderMagic64Swapped:
223            return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
224
225        // fat mach-o file
226        case UniversalMagic:
227        case UniversalMagicSwapped:
228            return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
229
230        default:
231            break;
232        }
233    }
234    return false;
235}
236
237FileSpec
238Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec,
239                                 const lldb_private::UUID *uuid,
240                                 const ArchSpec *arch)
241{
242    char path[PATH_MAX];
243
244    FileSpec dsym_fspec;
245
246    if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
247    {
248        ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
249
250        lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir);
251        if (dirp.is_valid())
252        {
253            dsym_fspec.GetDirectory().SetCString(path);
254            struct dirent* dp;
255            while ((dp = readdir(dirp.get())) != NULL)
256            {
257                // Only search directories
258                if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
259                {
260                    if (dp->d_namlen == 1 && dp->d_name[0] == '.')
261                        continue;
262
263                    if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
264                        continue;
265                }
266
267                if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
268                {
269                    dsym_fspec.GetFilename().SetCString(dp->d_name);
270                    if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid))
271                        return dsym_fspec;
272                }
273            }
274        }
275    }
276    dsym_fspec.Clear();
277    return dsym_fspec;
278}
279
280static int
281LocateMacOSXFilesUsingDebugSymbols
282(
283    const ModuleSpec &module_spec,
284    FileSpec *out_exec_fspec,   // If non-NULL, try and find the executable
285    FileSpec *out_dsym_fspec    // If non-NULL try and find the debug symbol file
286)
287{
288    int items_found = 0;
289
290    if (out_exec_fspec)
291        out_exec_fspec->Clear();
292
293    if (out_dsym_fspec)
294        out_dsym_fspec->Clear();
295
296#if !defined (__arm__) // No DebugSymbols on the iOS devices
297
298    const UUID *uuid = module_spec.GetUUIDPtr();
299    const ArchSpec *arch = module_spec.GetArchitecturePtr();
300
301    if (uuid && uuid->IsValid())
302    {
303        // Try and locate the dSYM file using DebugSymbols first
304        const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes();
305        if (module_uuid != NULL)
306        {
307            CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL,
308                                                                            module_uuid[0],
309                                                                            module_uuid[1],
310                                                                            module_uuid[2],
311                                                                            module_uuid[3],
312                                                                            module_uuid[4],
313                                                                            module_uuid[5],
314                                                                            module_uuid[6],
315                                                                            module_uuid[7],
316                                                                            module_uuid[8],
317                                                                            module_uuid[9],
318                                                                            module_uuid[10],
319                                                                            module_uuid[11],
320                                                                            module_uuid[12],
321                                                                            module_uuid[13],
322                                                                            module_uuid[14],
323                                                                            module_uuid[15]));
324
325            if (module_uuid_ref.get())
326            {
327                CFCReleaser<CFURLRef> exec_url;
328                const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
329                if (exec_fspec)
330                {
331                    char exec_cf_path[PATH_MAX];
332                    if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
333                        exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL,
334                                                                                  (const UInt8 *)exec_cf_path,
335                                                                                  strlen(exec_cf_path),
336                                                                                  FALSE));
337                }
338
339                CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
340                char path[PATH_MAX];
341
342                if (dsym_url.get())
343                {
344                    if (out_dsym_fspec)
345                    {
346                        if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
347                        {
348                            out_dsym_fspec->SetFile(path, path[0] == '~');
349
350                            if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory)
351                            {
352                                *out_dsym_fspec = Symbols::FindSymbolFileInBundle (*out_dsym_fspec, uuid, arch);
353                                if (*out_dsym_fspec)
354                                    ++items_found;
355                            }
356                            else
357                            {
358                                ++items_found;
359                            }
360                        }
361                    }
362
363                    CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));
364                    CFDictionaryRef uuid_dict = NULL;
365                    if (dict.get())
366                    {
367                        CFCString uuid_cfstr (uuid->GetAsString().c_str());
368                        uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get()));
369                        if (uuid_dict)
370                        {
371
372                            CFStringRef actual_src_cfpath = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSourcePath")));
373                            if (actual_src_cfpath)
374                            {
375                                CFStringRef build_src_cfpath = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGBuildSourcePath")));
376                                if (build_src_cfpath)
377                                {
378                                    char actual_src_path[PATH_MAX];
379                                    char build_src_path[PATH_MAX];
380                                    ::CFStringGetFileSystemRepresentation (actual_src_cfpath, actual_src_path, sizeof(actual_src_path));
381                                    ::CFStringGetFileSystemRepresentation (build_src_cfpath, build_src_path, sizeof(build_src_path));
382                                    if (actual_src_path[0] == '~')
383                                    {
384                                        FileSpec resolved_source_path(actual_src_path, true);
385                                        resolved_source_path.GetPath(actual_src_path, sizeof(actual_src_path));
386                                    }
387                                    module_spec.GetSourceMappingList().Append (ConstString(build_src_path), ConstString(actual_src_path), true);
388                                }
389                            }
390                        }
391                    }
392
393                    if (out_exec_fspec)
394                    {
395                        bool success = false;
396                        if (uuid_dict)
397                        {
398                            CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable")));
399                            if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path)))
400                            {
401                                ++items_found;
402                                out_exec_fspec->SetFile(path, path[0] == '~');
403                                if (out_exec_fspec->Exists())
404                                    success = true;
405                            }
406                        }
407
408                        if (!success)
409                        {
410                            // No dictionary, check near the dSYM bundle for an executable that matches...
411                            if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
412                            {
413                                char *dsym_extension_pos = ::strstr (path, ".dSYM");
414                                if (dsym_extension_pos)
415                                {
416                                    *dsym_extension_pos = '\0';
417                                    FileSpec file_spec (path, true);
418                                    switch (file_spec.GetFileType())
419                                    {
420                                        case FileSpec::eFileTypeDirectory:  // Bundle directory?
421                                            {
422                                                CFCBundle bundle (path);
423                                                CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ());
424                                                if (bundle_exe_url.get())
425                                                {
426                                                    if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1))
427                                                    {
428                                                        FileSpec bundle_exe_file_spec (path, true);
429
430                                                        if (FileAtPathContainsArchAndUUID (bundle_exe_file_spec, arch, uuid))
431                                                        {
432                                                            ++items_found;
433                                                            *out_exec_fspec = bundle_exe_file_spec;
434                                                        }
435                                                    }
436                                                }
437                                            }
438                                            break;
439
440                                        case FileSpec::eFileTypePipe:       // Forget pipes
441                                        case FileSpec::eFileTypeSocket:     // We can't process socket files
442                                        case FileSpec::eFileTypeInvalid:    // File doesn't exist...
443                                            break;
444
445                                        case FileSpec::eFileTypeUnknown:
446                                        case FileSpec::eFileTypeRegular:
447                                        case FileSpec::eFileTypeSymbolicLink:
448                                        case FileSpec::eFileTypeOther:
449                                            if (FileAtPathContainsArchAndUUID (file_spec, arch, uuid))
450                                            {
451                                                ++items_found;
452                                                *out_exec_fspec = file_spec;
453                                            }
454                                            break;
455                                    }
456                                }
457                            }
458                        }
459                    }
460                }
461            }
462        }
463    }
464#endif // #if !defined (__arm__)
465
466    return items_found;
467}
468
469static bool
470LocateDSYMInVincinityOfExecutable (const ModuleSpec &module_spec, FileSpec &dsym_fspec)
471{
472    const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
473    if (exec_fspec)
474    {
475        char path[PATH_MAX];
476        if (exec_fspec->GetPath(path, sizeof(path)))
477        {
478            // Make sure the module isn't already just a dSYM file...
479            if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL)
480            {
481                size_t obj_file_path_length = strlen(path);
482                strlcat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path));
483                strlcat(path, exec_fspec->GetFilename().AsCString(), sizeof(path));
484
485                dsym_fspec.SetFile(path, false);
486
487                if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID (dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr()))
488                {
489                    return true;
490                }
491                else
492                {
493                    path[obj_file_path_length] = '\0';
494
495                    char *last_dot = strrchr(path, '.');
496                    while (last_dot != NULL && last_dot[0])
497                    {
498                        char *next_slash = strchr(last_dot, '/');
499                        if (next_slash != NULL)
500                        {
501                            *next_slash = '\0';
502                            strlcat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path));
503                            strlcat(path, exec_fspec->GetFilename().AsCString(), sizeof(path));
504                            dsym_fspec.SetFile(path, false);
505                            if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID (dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr()))
506                                return true;
507                            else
508                            {
509                                *last_dot = '\0';
510                                char *prev_slash = strrchr(path, '/');
511                                if (prev_slash != NULL)
512                                    *prev_slash = '\0';
513                                else
514                                    break;
515                            }
516                        }
517                        else
518                        {
519                            break;
520                        }
521                    }
522                }
523            }
524        }
525    }
526    dsym_fspec.Clear();
527    return false;
528}
529
530FileSpec
531Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec)
532{
533    const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
534    const ArchSpec *arch = module_spec.GetArchitecturePtr();
535    const UUID *uuid = module_spec.GetUUIDPtr();
536    Timer scoped_timer (__PRETTY_FUNCTION__,
537                        "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
538                        exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>",
539                        arch ? arch->GetArchitectureName() : "<NULL>",
540                        uuid);
541
542    FileSpec objfile_fspec;
543    if (exec_fspec && FileAtPathContainsArchAndUUID (exec_fspec, arch, uuid))
544        objfile_fspec = exec_fspec;
545    else
546        LocateMacOSXFilesUsingDebugSymbols (module_spec, &objfile_fspec, NULL);
547    return objfile_fspec;
548}
549
550FileSpec
551Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec)
552{
553    const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
554    const ArchSpec *arch = module_spec.GetArchitecturePtr();
555    const UUID *uuid = module_spec.GetUUIDPtr();
556
557    Timer scoped_timer (__PRETTY_FUNCTION__,
558                        "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)",
559                        exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>",
560                        arch ? arch->GetArchitectureName() : "<NULL>",
561                        uuid);
562
563    FileSpec symbol_fspec;
564    // First try and find the dSYM in the same directory as the executable or in
565    // an appropriate parent directory
566    if (LocateDSYMInVincinityOfExecutable (module_spec, symbol_fspec) == false)
567    {
568        // We failed to easily find the dSYM above, so use DebugSymbols
569        LocateMacOSXFilesUsingDebugSymbols (module_spec, NULL, &symbol_fspec);
570    }
571    return symbol_fspec;
572}
573
574
575static bool
576GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec)
577{
578    bool success = false;
579    if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ())
580    {
581        std::string str;
582        CFStringRef cf_str;
583
584        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable"));
585        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
586        {
587            if (CFCString::FileSystemRepresentation(cf_str, str))
588                module_spec.GetFileSpec().SetFile (str.c_str(), true);
589        }
590
591        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath"));
592        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
593        {
594            if (CFCString::FileSystemRepresentation(cf_str, str))
595            {
596                module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true);
597                success = true;
598            }
599        }
600
601        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture"));
602        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
603        {
604            if (CFCString::FileSystemRepresentation(cf_str, str))
605                module_spec.GetArchitecture().SetTriple(str.c_str());
606        }
607
608        std::string DBGBuildSourcePath;
609        std::string DBGSourcePath;
610
611        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath"));
612        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
613        {
614            CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
615        }
616
617        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath"));
618        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
619        {
620            CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
621        }
622
623        if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty())
624        {
625            module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true);
626        }
627    }
628    return success;
629}
630
631
632bool
633Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup)
634{
635    bool success = false;
636    const UUID *uuid_ptr = module_spec.GetUUIDPtr();
637    const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
638
639    // It's expensive to check for the DBGShellCommands defaults setting, only do it once per
640    // lldb run and cache the result.
641    static bool g_have_checked_for_dbgshell_command = false;
642    static const char *g_dbgshell_command = NULL;
643    if (g_have_checked_for_dbgshell_command == false)
644    {
645        g_have_checked_for_dbgshell_command = true;
646        CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols"));
647        if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID())
648        {
649            char cstr_buf[PATH_MAX];
650            if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8))
651            {
652                g_dbgshell_command = strdup (cstr_buf);  // this malloc'ed memory will never be freed
653            }
654        }
655        if (defaults_setting)
656        {
657            CFRelease (defaults_setting);
658        }
659    }
660
661    // When g_dbgshell_command is NULL, the user has not enabled the use of an external program
662    // to find the symbols, don't run it for them.
663    if (force_lookup == false && g_dbgshell_command == NULL)
664    {
665        return false;
666    }
667
668    if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists()))
669    {
670        static bool g_located_dsym_for_uuid_exe = false;
671        static bool g_dsym_for_uuid_exe_exists = false;
672        static char g_dsym_for_uuid_exe_path[PATH_MAX];
673        if (!g_located_dsym_for_uuid_exe)
674        {
675            g_located_dsym_for_uuid_exe = true;
676            const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
677            FileSpec dsym_for_uuid_exe_spec;
678            if (dsym_for_uuid_exe_path_cstr)
679            {
680                dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true);
681                g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
682            }
683
684            if (!g_dsym_for_uuid_exe_exists)
685            {
686                dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false);
687                g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
688                if (!g_dsym_for_uuid_exe_exists)
689                {
690                    long bufsize;
691                    if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1)
692                    {
693                        char buffer[bufsize];
694                        struct passwd pwd;
695                        struct passwd *tilde_rc = NULL;
696                        // we are a library so we need to use the reentrant version of getpwnam()
697                        if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0
698                            && tilde_rc
699                            && tilde_rc->pw_dir)
700                        {
701                            std::string dsymforuuid_path(tilde_rc->pw_dir);
702                            dsymforuuid_path += "/bin/dsymForUUID";
703                            dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false);
704                            g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
705                        }
706                    }
707                }
708            }
709            if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL)
710            {
711                dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true);
712                g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
713            }
714
715            if (g_dsym_for_uuid_exe_exists)
716                dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path));
717        }
718        if (g_dsym_for_uuid_exe_exists)
719        {
720            std::string uuid_str;
721            char file_path[PATH_MAX];
722            file_path[0] = '\0';
723
724            if (uuid_ptr)
725                uuid_str = uuid_ptr->GetAsString();
726
727            if (file_spec_ptr)
728                file_spec_ptr->GetPath(file_path, sizeof(file_path));
729
730            StreamString command;
731            if (!uuid_str.empty())
732                command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str());
733            else if (file_path && file_path[0])
734                command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path);
735
736            if (!command.GetString().empty())
737            {
738                int exit_status = -1;
739                int signo = -1;
740                std::string command_output;
741                Error error = Host::RunShellCommand (command.GetData(),
742                                                     NULL,              // current working directory
743                                                     &exit_status,      // Exit status
744                                                     &signo,            // Signal int *
745                                                     &command_output,   // Command output
746                                                     30,                // Large timeout to allow for long dsym download times
747                                                     NULL);             // Don't run in a shell (we don't need shell expansion)
748                if (error.Success() && exit_status == 0 && !command_output.empty())
749                {
750                    CFCData data (CFDataCreateWithBytesNoCopy (NULL,
751                                                               (const UInt8 *)command_output.data(),
752                                                               command_output.size(),
753                                                               kCFAllocatorNull));
754
755                    CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL));
756
757                    if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ())
758                    {
759                        if (!uuid_str.empty())
760                        {
761                            CFCString uuid_cfstr(uuid_str.c_str());
762                            CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get());
763                            success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec);
764                        }
765                        else
766                        {
767                            const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
768                            if (num_values > 0)
769                            {
770                                std::vector<CFStringRef> keys (num_values, NULL);
771                                std::vector<CFDictionaryRef> values (num_values, NULL);
772                                ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]);
773                                if (num_values == 1)
774                                {
775                                    return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec);
776                                }
777                                else
778                                {
779                                    for (CFIndex i=0; i<num_values; ++i)
780                                    {
781                                        ModuleSpec curr_module_spec;
782                                        if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec))
783                                        {
784                                            if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture()))
785                                            {
786                                                module_spec = curr_module_spec;
787                                                return true;
788                                            }
789                                        }
790                                    }
791                                }
792                            }
793                        }
794                    }
795                }
796            }
797        }
798    }
799    return success;
800}
801
802