]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - source/Host/macosx/Symbols.cpp
Vendor import of lldb trunk r256945:
[FreeBSD/FreeBSD.git] / source / Host / macosx / Symbols.cpp
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 "lldb/Utility/SafeMachO.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/Log.h"
26 #include "lldb/Core/Module.h"
27 #include "lldb/Core/ModuleSpec.h"
28 #include "lldb/Core/StreamString.h"
29 #include "lldb/Core/Timer.h"
30 #include "lldb/Core/UUID.h"
31 #include "lldb/Host/Endian.h"
32 #include "lldb/Host/Host.h"
33 #include "lldb/Symbol/ObjectFile.h"
34 #include "lldb/Utility/CleanUp.h"
35 #include "Host/macosx/cfcpp/CFCBundle.h"
36 #include "Host/macosx/cfcpp/CFCData.h"
37 #include "Host/macosx/cfcpp/CFCReleaser.h"
38 #include "Host/macosx/cfcpp/CFCString.h"
39 #include "mach/machine.h"
40
41 using namespace lldb;
42 using namespace lldb_private;
43 using namespace llvm::MachO;
44
45 #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices
46 extern "C" {
47
48 CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
49 CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
50
51 }
52 #endif
53
54 int
55 LocateMacOSXFilesUsingDebugSymbols
56 (
57     const ModuleSpec &module_spec,
58     ModuleSpec &return_module_spec
59 )
60 {
61     return_module_spec = module_spec;
62     return_module_spec.GetFileSpec().Clear();
63     return_module_spec.GetSymbolFileSpec().Clear();
64
65     int items_found = 0;
66
67 #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices
68
69     const UUID *uuid = module_spec.GetUUIDPtr();
70     const ArchSpec *arch = module_spec.GetArchitecturePtr();
71
72     if (uuid && uuid->IsValid())
73     {
74         // Try and locate the dSYM file using DebugSymbols first
75         const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes();
76         if (module_uuid != NULL)
77         {
78             CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL,
79                                                                             module_uuid[0],
80                                                                             module_uuid[1],
81                                                                             module_uuid[2],
82                                                                             module_uuid[3],
83                                                                             module_uuid[4],
84                                                                             module_uuid[5],
85                                                                             module_uuid[6],
86                                                                             module_uuid[7],
87                                                                             module_uuid[8],
88                                                                             module_uuid[9],
89                                                                             module_uuid[10],
90                                                                             module_uuid[11],
91                                                                             module_uuid[12],
92                                                                             module_uuid[13],
93                                                                             module_uuid[14],
94                                                                             module_uuid[15]));
95
96             if (module_uuid_ref.get())
97             {
98                 CFCReleaser<CFURLRef> exec_url;
99                 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
100                 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
101                 if (exec_fspec)
102                 {
103                     char exec_cf_path[PATH_MAX];
104                     if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
105                         exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL,
106                                                                                   (const UInt8 *)exec_cf_path,
107                                                                                   strlen(exec_cf_path),
108                                                                                   FALSE));
109                 }
110
111                 CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
112                 char path[PATH_MAX];
113
114                 if (dsym_url.get())
115                 {
116                     if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
117                     {
118                         if (log)
119                         {
120                             log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str());
121                         }
122                         FileSpec dsym_filespec(path, path[0] == '~');
123
124                         if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory)
125                         {
126                             dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch);
127                             ++items_found;
128                         }
129                         else
130                         {
131                             ++items_found;
132                         }
133                         return_module_spec.GetSymbolFileSpec() = dsym_filespec;
134                     }
135
136                     bool success = false;
137                     if (log)
138                     {
139                         if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
140                         {
141                             log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str());
142                         }
143
144                     }
145
146                     CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));
147                     CFDictionaryRef uuid_dict = NULL;
148                     if (dict.get())
149                     {
150                         CFCString uuid_cfstr (uuid->GetAsString().c_str());
151                         uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get()));
152                     }
153                     if (uuid_dict)
154                     {
155                         CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable")));
156                         if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path)))
157                         {
158                             if (log)
159                             {
160                                 log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str());
161                             }
162                             ++items_found;
163                             FileSpec exec_filespec (path, path[0] == '~');
164                             if (exec_filespec.Exists())
165                             {
166                                 success = true;
167                                 return_module_spec.GetFileSpec() = exec_filespec;
168                             }
169                         }
170                     }
171
172                     if (!success)
173                     {
174                         // No dictionary, check near the dSYM bundle for an executable that matches...
175                         if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
176                         {
177                             char *dsym_extension_pos = ::strstr (path, ".dSYM");
178                             if (dsym_extension_pos)
179                             {
180                                 *dsym_extension_pos = '\0';
181                                 if (log)
182                                 {
183                                     log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path);
184                                 }
185                                 FileSpec file_spec (path, true);
186                                 ModuleSpecList module_specs;
187                                 ModuleSpec matched_module_spec;
188                                 switch (file_spec.GetFileType())
189                                 {
190                                     case FileSpec::eFileTypeDirectory:  // Bundle directory?
191                                         {
192                                             CFCBundle bundle (path);
193                                             CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ());
194                                             if (bundle_exe_url.get())
195                                             {
196                                                 if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1))
197                                                 {
198                                                     FileSpec bundle_exe_file_spec (path, true);
199                                                     if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) &&
200                                                         module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
201
202                                                     {
203                                                         ++items_found;
204                                                         return_module_spec.GetFileSpec() = bundle_exe_file_spec;
205                                                         if (log)
206                                                         {
207                                                             log->Printf ("Executable binary %s next to dSYM is compatible; using", path);
208                                                         }
209                                                     }
210                                                 }
211                                             }
212                                         }
213                                         break;
214
215                                     case FileSpec::eFileTypePipe:       // Forget pipes
216                                     case FileSpec::eFileTypeSocket:     // We can't process socket files
217                                     case FileSpec::eFileTypeInvalid:    // File doesn't exist...
218                                         break;
219
220                                     case FileSpec::eFileTypeUnknown:
221                                     case FileSpec::eFileTypeRegular:
222                                     case FileSpec::eFileTypeSymbolicLink:
223                                     case FileSpec::eFileTypeOther:
224                                         if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) &&
225                                             module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
226
227                                         {
228                                             ++items_found;
229                                             return_module_spec.GetFileSpec() = file_spec;
230                                             if (log)
231                                             {
232                                                 log->Printf ("Executable binary %s next to dSYM is compatible; using", path);
233                                             }
234                                         }
235                                         break;
236                                 }
237                             }
238                         }
239                     }
240                 }
241             }
242         }
243     }
244 #endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__)
245
246     return items_found;
247 }
248
249 FileSpec
250 Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec,
251                                  const lldb_private::UUID *uuid,
252                                  const ArchSpec *arch)
253 {
254     char path[PATH_MAX];
255
256     FileSpec dsym_fspec;
257
258     if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
259     {
260         ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
261
262         lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir);
263         if (dirp.is_valid())
264         {
265             dsym_fspec.GetDirectory().SetCString(path);
266             struct dirent* dp;
267             while ((dp = readdir(dirp.get())) != NULL)
268             {
269                 // Only search directories
270                 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
271                 {
272                     if (dp->d_namlen == 1 && dp->d_name[0] == '.')
273                         continue;
274
275                     if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
276                         continue;
277                 }
278
279                 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
280                 {
281                     dsym_fspec.GetFilename().SetCString(dp->d_name);
282                     ModuleSpecList module_specs;
283                     if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs))
284                     {
285                         ModuleSpec spec;
286                         for (size_t i = 0; i < module_specs.GetSize(); ++i)
287                         {
288                             assert(module_specs.GetModuleSpecAtIndex(i, spec));
289                             if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
290                                 (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch))))
291                             {
292                                 return dsym_fspec;
293                             }
294                         }
295                     }
296                 }
297             }
298         }
299     }
300     dsym_fspec.Clear();
301     return dsym_fspec;
302 }
303
304 static bool
305 GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec)
306 {
307     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
308     bool success = false;
309     if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ())
310     {
311         std::string str;
312         CFStringRef cf_str;
313         
314         cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable"));
315         if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
316         {
317             if (CFCString::FileSystemRepresentation(cf_str, str))
318             {
319                 module_spec.GetFileSpec().SetFile (str.c_str(), true);
320                 if (log)
321                 {
322                     log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str());
323                 }
324             }
325         }
326         
327         cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath"));
328         if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
329         {
330             if (CFCString::FileSystemRepresentation(cf_str, str))
331             {
332                 module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true);
333                 success = true;
334                 if (log)
335                 {
336                     log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str());
337                 }
338             }
339         }
340         
341         cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture"));
342         if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
343         {
344             if (CFCString::FileSystemRepresentation(cf_str, str))
345                 module_spec.GetArchitecture().SetTriple(str.c_str());
346         }
347
348         std::string DBGBuildSourcePath;
349         std::string DBGSourcePath;
350
351         cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath"));
352         if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
353         {
354             CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
355         }
356
357         cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath"));
358         if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
359         {
360             CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
361         }
362         
363         if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty())
364         {
365             module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true);
366         }
367     }
368     return success;
369 }
370
371
372 bool
373 Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup)
374 {
375     bool success = false;
376     const UUID *uuid_ptr = module_spec.GetUUIDPtr();
377     const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
378
379     // It's expensive to check for the DBGShellCommands defaults setting, only do it once per
380     // lldb run and cache the result.  
381     static bool g_have_checked_for_dbgshell_command = false;
382     static const char *g_dbgshell_command = NULL;
383     if (g_have_checked_for_dbgshell_command == false)
384     {
385         g_have_checked_for_dbgshell_command = true;
386         CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols"));
387         if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID())
388         { 
389             char cstr_buf[PATH_MAX];
390             if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8))
391             {
392                 g_dbgshell_command = strdup (cstr_buf);  // this malloc'ed memory will never be freed
393             }
394         }
395         if (defaults_setting)
396         {
397             CFRelease (defaults_setting);
398         }
399     }
400
401     // When g_dbgshell_command is NULL, the user has not enabled the use of an external program
402     // to find the symbols, don't run it for them.
403     if (force_lookup == false && g_dbgshell_command == NULL)
404     {
405         return false;
406     }
407
408     if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists()))
409     {
410         static bool g_located_dsym_for_uuid_exe = false;
411         static bool g_dsym_for_uuid_exe_exists = false;
412         static char g_dsym_for_uuid_exe_path[PATH_MAX];
413         if (!g_located_dsym_for_uuid_exe)
414         {
415             g_located_dsym_for_uuid_exe = true;
416             const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
417             FileSpec dsym_for_uuid_exe_spec;
418             if (dsym_for_uuid_exe_path_cstr)
419             {
420                 dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true);
421                 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
422             }
423             
424             if (!g_dsym_for_uuid_exe_exists)
425             {
426                 dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false);
427                 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
428                 if (!g_dsym_for_uuid_exe_exists)
429                 {
430                     long bufsize;
431                     if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1)
432                     {
433                         char buffer[bufsize];
434                         struct passwd pwd;
435                         struct passwd *tilde_rc = NULL;
436                         // we are a library so we need to use the reentrant version of getpwnam()
437                         if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 
438                             && tilde_rc 
439                             && tilde_rc->pw_dir)
440                         {
441                             std::string dsymforuuid_path(tilde_rc->pw_dir);
442                             dsymforuuid_path += "/bin/dsymForUUID";
443                             dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false);
444                             g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
445                         }
446                     }
447                 }
448             }
449             if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL)
450             {
451                 dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true);
452                 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
453             }
454
455             if (g_dsym_for_uuid_exe_exists)
456                 dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path));
457         }
458         if (g_dsym_for_uuid_exe_exists)
459         {
460             std::string uuid_str;
461             char file_path[PATH_MAX];
462             file_path[0] = '\0';
463
464             if (uuid_ptr)
465                 uuid_str = uuid_ptr->GetAsString();
466
467             if (file_spec_ptr)
468                 file_spec_ptr->GetPath(file_path, sizeof(file_path));
469             
470             StreamString command;
471             if (!uuid_str.empty())
472                 command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str());
473             else if (file_path[0] != '\0')
474                 command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path);
475             
476             if (!command.GetString().empty())
477             {
478                 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
479                 int exit_status = -1;
480                 int signo = -1;
481                 std::string command_output;
482                 if (log)
483                 {
484                     if (!uuid_str.empty())
485                         log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str());
486                     else if (file_path[0] != '\0')
487                         log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path);
488                 }
489                 Error error = Host::RunShellCommand (command.GetData(),
490                                                      NULL,              // current working directory
491                                                      &exit_status,      // Exit status
492                                                      &signo,            // Signal int *
493                                                      &command_output,   // Command output
494                                                      30,                // Large timeout to allow for long dsym download times
495                                                      false);            // Don't run in a shell (we don't need shell expansion)
496                 if (error.Success() && exit_status == 0 && !command_output.empty())
497                 {
498                     CFCData data (CFDataCreateWithBytesNoCopy (NULL,
499                                                                (const UInt8 *)command_output.data(),
500                                                                command_output.size(),
501                                                                kCFAllocatorNull));
502                     
503                     CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL));
504                     
505                     if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ())
506                     {
507                         if (!uuid_str.empty())
508                         {
509                             CFCString uuid_cfstr(uuid_str.c_str());
510                             CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get());
511                             success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec);
512                         }
513                         else
514                         {
515                             const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
516                             if (num_values > 0)
517                             {
518                                 std::vector<CFStringRef> keys (num_values, NULL);
519                                 std::vector<CFDictionaryRef> values (num_values, NULL);
520                                 ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]);
521                                 if (num_values == 1)
522                                 {
523                                     return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec);
524                                 }
525                                 else
526                                 {
527                                     for (CFIndex i=0; i<num_values; ++i)
528                                     {
529                                         ModuleSpec curr_module_spec;
530                                         if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec))
531                                         {
532                                             if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture()))
533                                             {
534                                                 module_spec = curr_module_spec;
535                                                 return true;
536                                             }
537                                         }
538                                     }
539                                 }
540                             }
541                         }
542                     }
543                 }
544                 else
545                 {
546                     if (log)
547                     {
548                         if (!uuid_str.empty())
549                             log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str());
550                         else if (file_path[0] != '\0')
551                             log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path);
552                     }
553                 }
554             }
555         }
556     }
557     return success;
558 }
559