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