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
40 using namespace lldb;
41 using namespace lldb_private;
42 using namespace llvm::MachO;
43
44 #if !defined (__arm__) // No DebugSymbols on the iOS devices
45 extern "C" {
46
47 CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
48 CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
49
50 }
51 #endif
52
53 static bool
SkinnyMachOFileContainsArchAndUUID(const FileSpec & file_spec,const ArchSpec * arch,const lldb_private::UUID * uuid,off_t file_offset,DataExtractor & data,lldb::offset_t data_offset,const uint32_t magic)54 SkinnyMachOFileContainsArchAndUUID
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
134 bool
UniversalMachOFileContainsArchAndUUID(const FileSpec & file_spec,const ArchSpec * arch,const lldb_private::UUID * uuid,off_t file_offset,DataExtractor & data,lldb::offset_t data_offset,const uint32_t magic)135 UniversalMachOFileContainsArchAndUUID
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
197 static bool
FileAtPathContainsArchAndUUID(const FileSpec & file_spec,const ArchSpec * arch,const lldb_private::UUID * uuid)198 FileAtPathContainsArchAndUUID
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
237 FileSpec
FindSymbolFileInBundle(const FileSpec & dsym_bundle_fspec,const lldb_private::UUID * uuid,const ArchSpec * arch)238 Symbols::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
280 static int
LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec & module_spec,FileSpec * out_exec_fspec,FileSpec * out_dsym_fspec)281 LocateMacOSXFilesUsingDebugSymbols
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
469 static bool
LocateDSYMInVincinityOfExecutable(const ModuleSpec & module_spec,FileSpec & dsym_fspec)470 LocateDSYMInVincinityOfExecutable (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
530 FileSpec
LocateExecutableObjectFile(const ModuleSpec & module_spec)531 Symbols::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
550 FileSpec
LocateExecutableSymbolFile(const ModuleSpec & module_spec)551 Symbols::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
575 static bool
GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,ModuleSpec & module_spec)576 GetModuleSpecInfoFromUUIDDictionary (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
632 bool
DownloadObjectAndSymbolFile(ModuleSpec & module_spec,bool force_lookup)633 Symbols::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