1dda28197Spatrick //===-- LocateSymbolFileMacOSX.cpp ----------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "lldb/Symbol/LocateSymbolFile.h"
10061da546Spatrick
11061da546Spatrick #include <dirent.h>
12061da546Spatrick #include <dlfcn.h>
13061da546Spatrick #include <pwd.h>
14061da546Spatrick
15061da546Spatrick #include <CoreFoundation/CoreFoundation.h>
16061da546Spatrick
17061da546Spatrick #include "Host/macosx/cfcpp/CFCBundle.h"
18061da546Spatrick #include "Host/macosx/cfcpp/CFCData.h"
19061da546Spatrick #include "Host/macosx/cfcpp/CFCReleaser.h"
20061da546Spatrick #include "Host/macosx/cfcpp/CFCString.h"
21*f6aab3d8Srobert #include "lldb/Core/Module.h"
22061da546Spatrick #include "lldb/Core/ModuleList.h"
23061da546Spatrick #include "lldb/Core/ModuleSpec.h"
24061da546Spatrick #include "lldb/Host/Host.h"
25*f6aab3d8Srobert #include "lldb/Host/HostInfo.h"
26061da546Spatrick #include "lldb/Symbol/ObjectFile.h"
27061da546Spatrick #include "lldb/Utility/ArchSpec.h"
28061da546Spatrick #include "lldb/Utility/DataBuffer.h"
29061da546Spatrick #include "lldb/Utility/DataExtractor.h"
30061da546Spatrick #include "lldb/Utility/Endian.h"
31*f6aab3d8Srobert #include "lldb/Utility/LLDBLog.h"
32061da546Spatrick #include "lldb/Utility/Log.h"
33061da546Spatrick #include "lldb/Utility/StreamString.h"
34061da546Spatrick #include "lldb/Utility/Timer.h"
35061da546Spatrick #include "lldb/Utility/UUID.h"
36061da546Spatrick #include "mach/machine.h"
37061da546Spatrick
38061da546Spatrick #include "llvm/ADT/ScopeExit.h"
39061da546Spatrick #include "llvm/Support/FileSystem.h"
40061da546Spatrick
41061da546Spatrick using namespace lldb;
42061da546Spatrick using namespace lldb_private;
43061da546Spatrick
44*f6aab3d8Srobert static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(
45*f6aab3d8Srobert CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
46*f6aab3d8Srobert static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) =
47*f6aab3d8Srobert nullptr;
48061da546Spatrick
LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec & module_spec,ModuleSpec & return_module_spec)49061da546Spatrick int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
50061da546Spatrick ModuleSpec &return_module_spec) {
51*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host);
52061da546Spatrick if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
53061da546Spatrick LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
54061da546Spatrick return 0;
55061da546Spatrick }
56061da546Spatrick
57061da546Spatrick return_module_spec = module_spec;
58061da546Spatrick return_module_spec.GetFileSpec().Clear();
59061da546Spatrick return_module_spec.GetSymbolFileSpec().Clear();
60061da546Spatrick
61be691f3bSpatrick const UUID *uuid = module_spec.GetUUIDPtr();
62be691f3bSpatrick const ArchSpec *arch = module_spec.GetArchitecturePtr();
63be691f3bSpatrick
64061da546Spatrick int items_found = 0;
65061da546Spatrick
66061da546Spatrick if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
67061da546Spatrick g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
68*f6aab3d8Srobert void *handle = dlopen(
69*f6aab3d8Srobert "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",
70*f6aab3d8Srobert RTLD_LAZY | RTLD_LOCAL);
71061da546Spatrick if (handle) {
72*f6aab3d8Srobert g_dlsym_DBGCopyFullDSYMURLForUUID =
73*f6aab3d8Srobert (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,
74*f6aab3d8Srobert "DBGCopyFullDSYMURLForUUID");
75*f6aab3d8Srobert g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym(
76*f6aab3d8Srobert handle, "DBGCopyDSYMPropertyLists");
77061da546Spatrick }
78061da546Spatrick }
79061da546Spatrick
80061da546Spatrick if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
81061da546Spatrick g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
82061da546Spatrick return items_found;
83061da546Spatrick }
84061da546Spatrick
85061da546Spatrick if (uuid && uuid->IsValid()) {
86061da546Spatrick // Try and locate the dSYM file using DebugSymbols first
87061da546Spatrick llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
88061da546Spatrick if (module_uuid.size() == 16) {
89061da546Spatrick CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
90061da546Spatrick NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
91061da546Spatrick module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
92061da546Spatrick module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
93061da546Spatrick module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
94061da546Spatrick
95061da546Spatrick if (module_uuid_ref.get()) {
96061da546Spatrick CFCReleaser<CFURLRef> exec_url;
97061da546Spatrick const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
98061da546Spatrick if (exec_fspec) {
99061da546Spatrick char exec_cf_path[PATH_MAX];
100061da546Spatrick if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
101061da546Spatrick exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
102061da546Spatrick NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
103061da546Spatrick FALSE));
104061da546Spatrick }
105061da546Spatrick
106*f6aab3d8Srobert CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
107*f6aab3d8Srobert module_uuid_ref.get(), exec_url.get()));
108061da546Spatrick char path[PATH_MAX];
109061da546Spatrick
110061da546Spatrick if (dsym_url.get()) {
111061da546Spatrick if (::CFURLGetFileSystemRepresentation(
112061da546Spatrick dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
113061da546Spatrick LLDB_LOGF(log,
114061da546Spatrick "DebugSymbols framework returned dSYM path of %s for "
115061da546Spatrick "UUID %s -- looking for the dSYM",
116061da546Spatrick path, uuid->GetAsString().c_str());
117061da546Spatrick FileSpec dsym_filespec(path);
118061da546Spatrick if (path[0] == '~')
119061da546Spatrick FileSystem::Instance().Resolve(dsym_filespec);
120061da546Spatrick
121061da546Spatrick if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
122061da546Spatrick dsym_filespec =
123061da546Spatrick Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch);
124061da546Spatrick ++items_found;
125061da546Spatrick } else {
126061da546Spatrick ++items_found;
127061da546Spatrick }
128061da546Spatrick return_module_spec.GetSymbolFileSpec() = dsym_filespec;
129061da546Spatrick }
130061da546Spatrick
131061da546Spatrick bool success = false;
132061da546Spatrick if (log) {
133061da546Spatrick if (::CFURLGetFileSystemRepresentation(
134061da546Spatrick dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
135061da546Spatrick LLDB_LOGF(log,
136061da546Spatrick "DebugSymbols framework returned dSYM path of %s for "
137061da546Spatrick "UUID %s -- looking for an exec file",
138061da546Spatrick path, uuid->GetAsString().c_str());
139061da546Spatrick }
140061da546Spatrick }
141061da546Spatrick
142061da546Spatrick CFCReleaser<CFDictionaryRef> dict(
143061da546Spatrick g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
144061da546Spatrick CFDictionaryRef uuid_dict = NULL;
145061da546Spatrick if (dict.get()) {
146061da546Spatrick CFCString uuid_cfstr(uuid->GetAsString().c_str());
147061da546Spatrick uuid_dict = static_cast<CFDictionaryRef>(
148061da546Spatrick ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
149061da546Spatrick }
150*f6aab3d8Srobert
151*f6aab3d8Srobert // Check to see if we have the file on the local filesystem.
152*f6aab3d8Srobert if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
153*f6aab3d8Srobert ModuleSpec exe_spec;
154*f6aab3d8Srobert exe_spec.GetFileSpec() = module_spec.GetFileSpec();
155*f6aab3d8Srobert exe_spec.GetUUID() = module_spec.GetUUID();
156*f6aab3d8Srobert ModuleSP module_sp;
157*f6aab3d8Srobert module_sp.reset(new Module(exe_spec));
158*f6aab3d8Srobert if (module_sp && module_sp->GetObjectFile() &&
159*f6aab3d8Srobert module_sp->MatchesModuleSpec(exe_spec)) {
160*f6aab3d8Srobert success = true;
161*f6aab3d8Srobert return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
162*f6aab3d8Srobert LLDB_LOGF(log, "using original binary filepath %s for UUID %s",
163*f6aab3d8Srobert module_spec.GetFileSpec().GetPath().c_str(),
164*f6aab3d8Srobert uuid->GetAsString().c_str());
165*f6aab3d8Srobert ++items_found;
166*f6aab3d8Srobert }
167*f6aab3d8Srobert }
168*f6aab3d8Srobert
169*f6aab3d8Srobert // Check if the requested image is in our shared cache.
170*f6aab3d8Srobert if (!success) {
171*f6aab3d8Srobert SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
172*f6aab3d8Srobert module_spec.GetFileSpec().GetPath());
173*f6aab3d8Srobert
174*f6aab3d8Srobert // If we found it and it has the correct UUID, let's proceed with
175*f6aab3d8Srobert // creating a module from the memory contents.
176*f6aab3d8Srobert if (image_info.uuid && (!module_spec.GetUUID() ||
177*f6aab3d8Srobert module_spec.GetUUID() == image_info.uuid)) {
178*f6aab3d8Srobert success = true;
179*f6aab3d8Srobert return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
180*f6aab3d8Srobert LLDB_LOGF(log,
181*f6aab3d8Srobert "using binary from shared cache for filepath %s for "
182*f6aab3d8Srobert "UUID %s",
183*f6aab3d8Srobert module_spec.GetFileSpec().GetPath().c_str(),
184*f6aab3d8Srobert uuid->GetAsString().c_str());
185*f6aab3d8Srobert ++items_found;
186*f6aab3d8Srobert }
187*f6aab3d8Srobert }
188*f6aab3d8Srobert
189*f6aab3d8Srobert // Use the DBGSymbolRichExecutable filepath if present
190*f6aab3d8Srobert if (!success && uuid_dict) {
191061da546Spatrick CFStringRef exec_cf_path =
192061da546Spatrick static_cast<CFStringRef>(::CFDictionaryGetValue(
193061da546Spatrick uuid_dict, CFSTR("DBGSymbolRichExecutable")));
194061da546Spatrick if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
195061da546Spatrick exec_cf_path, path, sizeof(path))) {
196061da546Spatrick LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",
197061da546Spatrick path, uuid->GetAsString().c_str());
198061da546Spatrick ++items_found;
199061da546Spatrick FileSpec exec_filespec(path);
200061da546Spatrick if (path[0] == '~')
201061da546Spatrick FileSystem::Instance().Resolve(exec_filespec);
202061da546Spatrick if (FileSystem::Instance().Exists(exec_filespec)) {
203061da546Spatrick success = true;
204061da546Spatrick return_module_spec.GetFileSpec() = exec_filespec;
205061da546Spatrick }
206061da546Spatrick }
207061da546Spatrick }
208061da546Spatrick
209*f6aab3d8Srobert // Look next to the dSYM for the binary file.
210061da546Spatrick if (!success) {
211061da546Spatrick if (::CFURLGetFileSystemRepresentation(
212061da546Spatrick dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
213061da546Spatrick char *dsym_extension_pos = ::strstr(path, ".dSYM");
214061da546Spatrick if (dsym_extension_pos) {
215061da546Spatrick *dsym_extension_pos = '\0';
216061da546Spatrick LLDB_LOGF(log,
217061da546Spatrick "Looking for executable binary next to dSYM "
218061da546Spatrick "bundle with name with name %s",
219061da546Spatrick path);
220061da546Spatrick FileSpec file_spec(path);
221061da546Spatrick FileSystem::Instance().Resolve(file_spec);
222061da546Spatrick ModuleSpecList module_specs;
223061da546Spatrick ModuleSpec matched_module_spec;
224061da546Spatrick using namespace llvm::sys::fs;
225061da546Spatrick switch (get_file_type(file_spec.GetPath())) {
226061da546Spatrick
227061da546Spatrick case file_type::directory_file: // Bundle directory?
228061da546Spatrick {
229061da546Spatrick CFCBundle bundle(path);
230061da546Spatrick CFCReleaser<CFURLRef> bundle_exe_url(
231061da546Spatrick bundle.CopyExecutableURL());
232061da546Spatrick if (bundle_exe_url.get()) {
233061da546Spatrick if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
234061da546Spatrick true, (UInt8 *)path,
235061da546Spatrick sizeof(path) - 1)) {
236061da546Spatrick FileSpec bundle_exe_file_spec(path);
237061da546Spatrick FileSystem::Instance().Resolve(bundle_exe_file_spec);
238061da546Spatrick if (ObjectFile::GetModuleSpecifications(
239061da546Spatrick bundle_exe_file_spec, 0, 0, module_specs) &&
240061da546Spatrick module_specs.FindMatchingModuleSpec(
241061da546Spatrick module_spec, matched_module_spec))
242061da546Spatrick
243061da546Spatrick {
244061da546Spatrick ++items_found;
245061da546Spatrick return_module_spec.GetFileSpec() = bundle_exe_file_spec;
246061da546Spatrick LLDB_LOGF(log,
247061da546Spatrick "Executable binary %s next to dSYM is "
248061da546Spatrick "compatible; using",
249061da546Spatrick path);
250061da546Spatrick }
251061da546Spatrick }
252061da546Spatrick }
253061da546Spatrick } break;
254061da546Spatrick
255061da546Spatrick case file_type::fifo_file: // Forget pipes
256061da546Spatrick case file_type::socket_file: // We can't process socket files
257061da546Spatrick case file_type::file_not_found: // File doesn't exist...
258061da546Spatrick case file_type::status_error:
259061da546Spatrick break;
260061da546Spatrick
261061da546Spatrick case file_type::type_unknown:
262061da546Spatrick case file_type::regular_file:
263061da546Spatrick case file_type::symlink_file:
264061da546Spatrick case file_type::block_file:
265061da546Spatrick case file_type::character_file:
266061da546Spatrick if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
267061da546Spatrick module_specs) &&
268061da546Spatrick module_specs.FindMatchingModuleSpec(module_spec,
269061da546Spatrick matched_module_spec))
270061da546Spatrick
271061da546Spatrick {
272061da546Spatrick ++items_found;
273061da546Spatrick return_module_spec.GetFileSpec() = file_spec;
274061da546Spatrick LLDB_LOGF(log,
275061da546Spatrick "Executable binary %s next to dSYM is "
276061da546Spatrick "compatible; using",
277061da546Spatrick path);
278061da546Spatrick }
279061da546Spatrick break;
280061da546Spatrick }
281061da546Spatrick }
282061da546Spatrick }
283061da546Spatrick }
284061da546Spatrick }
285061da546Spatrick }
286061da546Spatrick }
287061da546Spatrick }
288061da546Spatrick
289061da546Spatrick return items_found;
290061da546Spatrick }
291061da546Spatrick
FindSymbolFileInBundle(const FileSpec & dsym_bundle_fspec,const lldb_private::UUID * uuid,const ArchSpec * arch)292061da546Spatrick FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec,
293061da546Spatrick const lldb_private::UUID *uuid,
294061da546Spatrick const ArchSpec *arch) {
295dda28197Spatrick std::string dsym_bundle_path = dsym_bundle_fspec.GetPath();
296dda28197Spatrick llvm::SmallString<128> buffer(dsym_bundle_path);
297dda28197Spatrick llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF");
298061da546Spatrick
299dda28197Spatrick std::error_code EC;
300dda28197Spatrick llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs =
301dda28197Spatrick FileSystem::Instance().GetVirtualFileSystem();
302dda28197Spatrick llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC);
303dda28197Spatrick llvm::vfs::recursive_directory_iterator End;
304dda28197Spatrick for (; Iter != End && !EC; Iter.increment(EC)) {
305dda28197Spatrick llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path());
306dda28197Spatrick if (Status->isDirectory())
307061da546Spatrick continue;
308061da546Spatrick
309dda28197Spatrick FileSpec dsym_fspec(Iter->path());
310061da546Spatrick ModuleSpecList module_specs;
311061da546Spatrick if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
312061da546Spatrick ModuleSpec spec;
313061da546Spatrick for (size_t i = 0; i < module_specs.GetSize(); ++i) {
314061da546Spatrick bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
315dda28197Spatrick assert(got_spec); // The call has side-effects so can't be inlined.
316061da546Spatrick UNUSED_IF_ASSERT_DISABLED(got_spec);
317dda28197Spatrick if ((uuid == nullptr ||
318061da546Spatrick (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
319dda28197Spatrick (arch == nullptr ||
320061da546Spatrick (spec.GetArchitecturePtr() &&
321061da546Spatrick spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
322061da546Spatrick return dsym_fspec;
323061da546Spatrick }
324061da546Spatrick }
325061da546Spatrick }
326061da546Spatrick }
327061da546Spatrick
328061da546Spatrick return {};
329061da546Spatrick }
330061da546Spatrick
GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,ModuleSpec & module_spec,Status & error)331061da546Spatrick static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
332*f6aab3d8Srobert ModuleSpec &module_spec,
333*f6aab3d8Srobert Status &error) {
334*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host);
335061da546Spatrick bool success = false;
336061da546Spatrick if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
337061da546Spatrick std::string str;
338061da546Spatrick CFStringRef cf_str;
339061da546Spatrick CFDictionaryRef cf_dict;
340061da546Spatrick
341*f6aab3d8Srobert cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
342*f6aab3d8Srobert CFSTR("DBGError"));
343*f6aab3d8Srobert if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
344*f6aab3d8Srobert if (CFCString::FileSystemRepresentation(cf_str, str)) {
345*f6aab3d8Srobert error.SetErrorString(str);
346*f6aab3d8Srobert }
347*f6aab3d8Srobert }
348*f6aab3d8Srobert
349061da546Spatrick cf_str = (CFStringRef)CFDictionaryGetValue(
350061da546Spatrick (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
351061da546Spatrick if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
352061da546Spatrick if (CFCString::FileSystemRepresentation(cf_str, str)) {
353061da546Spatrick module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
354061da546Spatrick FileSystem::Instance().Resolve(module_spec.GetFileSpec());
355061da546Spatrick LLDB_LOGF(log,
356061da546Spatrick "From dsymForUUID plist: Symbol rich executable is at '%s'",
357061da546Spatrick str.c_str());
358061da546Spatrick }
359061da546Spatrick }
360061da546Spatrick
361061da546Spatrick cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
362061da546Spatrick CFSTR("DBGDSYMPath"));
363061da546Spatrick if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
364061da546Spatrick if (CFCString::FileSystemRepresentation(cf_str, str)) {
365061da546Spatrick module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
366061da546Spatrick FileSpec::Style::native);
367061da546Spatrick FileSystem::Instance().Resolve(module_spec.GetFileSpec());
368061da546Spatrick success = true;
369*f6aab3d8Srobert LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str());
370061da546Spatrick }
371061da546Spatrick }
372061da546Spatrick
373061da546Spatrick std::string DBGBuildSourcePath;
374061da546Spatrick std::string DBGSourcePath;
375061da546Spatrick
376061da546Spatrick // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
377061da546Spatrick // If DBGVersion 2, strip last two components of path remappings from
378061da546Spatrick // entries to fix an issue with a specific set of
379061da546Spatrick // DBGSourcePathRemapping entries that lldb worked
380061da546Spatrick // with.
381061da546Spatrick // If DBGVersion 3, trust & use the source path remappings as-is.
382061da546Spatrick //
383061da546Spatrick cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
384061da546Spatrick (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
385061da546Spatrick if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
386061da546Spatrick // If we see DBGVersion with a value of 2 or higher, this is a new style
387061da546Spatrick // DBGSourcePathRemapping dictionary
388061da546Spatrick bool new_style_source_remapping_dictionary = false;
389061da546Spatrick bool do_truncate_remapping_names = false;
390061da546Spatrick std::string original_DBGSourcePath_value = DBGSourcePath;
391061da546Spatrick cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
392061da546Spatrick CFSTR("DBGVersion"));
393061da546Spatrick if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
394061da546Spatrick std::string version;
395061da546Spatrick CFCString::FileSystemRepresentation(cf_str, version);
396061da546Spatrick if (!version.empty() && isdigit(version[0])) {
397061da546Spatrick int version_number = atoi(version.c_str());
398061da546Spatrick if (version_number > 1) {
399061da546Spatrick new_style_source_remapping_dictionary = true;
400061da546Spatrick }
401061da546Spatrick if (version_number == 2) {
402061da546Spatrick do_truncate_remapping_names = true;
403061da546Spatrick }
404061da546Spatrick }
405061da546Spatrick }
406061da546Spatrick
407061da546Spatrick CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
408061da546Spatrick if (kv_pair_count > 0) {
409061da546Spatrick CFStringRef *keys =
410061da546Spatrick (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
411061da546Spatrick CFStringRef *values =
412061da546Spatrick (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
413061da546Spatrick if (keys != nullptr && values != nullptr) {
414061da546Spatrick CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
415061da546Spatrick (const void **)keys,
416061da546Spatrick (const void **)values);
417061da546Spatrick }
418061da546Spatrick for (CFIndex i = 0; i < kv_pair_count; i++) {
419061da546Spatrick DBGBuildSourcePath.clear();
420061da546Spatrick DBGSourcePath.clear();
421061da546Spatrick if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
422061da546Spatrick CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
423061da546Spatrick }
424061da546Spatrick if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
425061da546Spatrick CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
426061da546Spatrick }
427061da546Spatrick if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
428061da546Spatrick // In the "old style" DBGSourcePathRemapping dictionary, the
429061da546Spatrick // DBGSourcePath values (the "values" half of key-value path pairs)
430061da546Spatrick // were wrong. Ignore them and use the universal DBGSourcePath
431061da546Spatrick // string from earlier.
432061da546Spatrick if (new_style_source_remapping_dictionary &&
433061da546Spatrick !original_DBGSourcePath_value.empty()) {
434061da546Spatrick DBGSourcePath = original_DBGSourcePath_value;
435061da546Spatrick }
436061da546Spatrick if (DBGSourcePath[0] == '~') {
437061da546Spatrick FileSpec resolved_source_path(DBGSourcePath.c_str());
438061da546Spatrick FileSystem::Instance().Resolve(resolved_source_path);
439061da546Spatrick DBGSourcePath = resolved_source_path.GetPath();
440061da546Spatrick }
441061da546Spatrick // With version 2 of DBGSourcePathRemapping, we can chop off the
442061da546Spatrick // last two filename parts from the source remapping and get a more
443061da546Spatrick // general source remapping that still works. Add this as another
444061da546Spatrick // option in addition to the full source path remap.
445*f6aab3d8Srobert module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
446*f6aab3d8Srobert DBGSourcePath, true);
447061da546Spatrick if (do_truncate_remapping_names) {
448061da546Spatrick FileSpec build_path(DBGBuildSourcePath.c_str());
449061da546Spatrick FileSpec source_path(DBGSourcePath.c_str());
450061da546Spatrick build_path.RemoveLastPathComponent();
451061da546Spatrick build_path.RemoveLastPathComponent();
452061da546Spatrick source_path.RemoveLastPathComponent();
453061da546Spatrick source_path.RemoveLastPathComponent();
454061da546Spatrick module_spec.GetSourceMappingList().Append(
455*f6aab3d8Srobert build_path.GetPath(), source_path.GetPath(), true);
456061da546Spatrick }
457061da546Spatrick }
458061da546Spatrick }
459061da546Spatrick if (keys)
460061da546Spatrick free(keys);
461061da546Spatrick if (values)
462061da546Spatrick free(values);
463061da546Spatrick }
464061da546Spatrick }
465061da546Spatrick
466061da546Spatrick // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
467061da546Spatrick // source remappings list.
468061da546Spatrick
469061da546Spatrick cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
470061da546Spatrick CFSTR("DBGBuildSourcePath"));
471061da546Spatrick if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
472061da546Spatrick CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
473061da546Spatrick }
474061da546Spatrick
475061da546Spatrick cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
476061da546Spatrick CFSTR("DBGSourcePath"));
477061da546Spatrick if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
478061da546Spatrick CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
479061da546Spatrick }
480061da546Spatrick
481061da546Spatrick if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
482061da546Spatrick if (DBGSourcePath[0] == '~') {
483061da546Spatrick FileSpec resolved_source_path(DBGSourcePath.c_str());
484061da546Spatrick FileSystem::Instance().Resolve(resolved_source_path);
485061da546Spatrick DBGSourcePath = resolved_source_path.GetPath();
486061da546Spatrick }
487*f6aab3d8Srobert module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
488*f6aab3d8Srobert DBGSourcePath, true);
489061da546Spatrick }
490061da546Spatrick }
491061da546Spatrick return success;
492061da546Spatrick }
493061da546Spatrick
494*f6aab3d8Srobert /// It's expensive to check for the DBGShellCommands defaults setting. Only do
495*f6aab3d8Srobert /// it once per lldb run and cache the result.
GetDbgShellCommand()496*f6aab3d8Srobert static llvm::StringRef GetDbgShellCommand() {
497*f6aab3d8Srobert static std::once_flag g_once_flag;
498*f6aab3d8Srobert static std::string g_dbgshell_command;
499*f6aab3d8Srobert std::call_once(g_once_flag, [&]() {
500061da546Spatrick CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
501061da546Spatrick CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
502061da546Spatrick if (defaults_setting &&
503061da546Spatrick CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
504*f6aab3d8Srobert char buffer[PATH_MAX];
505*f6aab3d8Srobert if (CFStringGetCString((CFStringRef)defaults_setting, buffer,
506*f6aab3d8Srobert sizeof(buffer), kCFStringEncodingUTF8)) {
507*f6aab3d8Srobert g_dbgshell_command = buffer;
508061da546Spatrick }
509061da546Spatrick }
510061da546Spatrick if (defaults_setting) {
511061da546Spatrick CFRelease(defaults_setting);
512061da546Spatrick }
513*f6aab3d8Srobert });
514*f6aab3d8Srobert return g_dbgshell_command;
515061da546Spatrick }
516061da546Spatrick
517*f6aab3d8Srobert /// Get the dsymForUUID executable and cache the result so we don't end up
518*f6aab3d8Srobert /// stat'ing the binary over and over.
GetDsymForUUIDExecutable()519*f6aab3d8Srobert static FileSpec GetDsymForUUIDExecutable() {
520*f6aab3d8Srobert // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the
521*f6aab3d8Srobert // test suite to override the dsymForUUID location. Because we must be able
522*f6aab3d8Srobert // to change the value within a single test, don't bother caching it.
523*f6aab3d8Srobert if (const char *dsymForUUID_env =
524*f6aab3d8Srobert getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
525*f6aab3d8Srobert FileSpec dsymForUUID_executable(dsymForUUID_env);
526*f6aab3d8Srobert FileSystem::Instance().Resolve(dsymForUUID_executable);
527*f6aab3d8Srobert if (FileSystem::Instance().Exists(dsymForUUID_executable))
528*f6aab3d8Srobert return dsymForUUID_executable;
529*f6aab3d8Srobert }
530*f6aab3d8Srobert
531*f6aab3d8Srobert static std::once_flag g_once_flag;
532*f6aab3d8Srobert static FileSpec g_dsymForUUID_executable;
533*f6aab3d8Srobert std::call_once(g_once_flag, [&]() {
534*f6aab3d8Srobert // Try the DBGShellCommand.
535*f6aab3d8Srobert llvm::StringRef dbgshell_command = GetDbgShellCommand();
536*f6aab3d8Srobert if (!dbgshell_command.empty()) {
537*f6aab3d8Srobert g_dsymForUUID_executable = FileSpec(dbgshell_command);
538*f6aab3d8Srobert FileSystem::Instance().Resolve(g_dsymForUUID_executable);
539*f6aab3d8Srobert if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
540*f6aab3d8Srobert return;
541*f6aab3d8Srobert }
542*f6aab3d8Srobert
543*f6aab3d8Srobert // Try dsymForUUID in /usr/local/bin
544*f6aab3d8Srobert {
545*f6aab3d8Srobert g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID");
546*f6aab3d8Srobert if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
547*f6aab3d8Srobert return;
548*f6aab3d8Srobert }
549*f6aab3d8Srobert
550*f6aab3d8Srobert // We couldn't find the dsymForUUID binary.
551*f6aab3d8Srobert g_dsymForUUID_executable = {};
552*f6aab3d8Srobert });
553*f6aab3d8Srobert return g_dsymForUUID_executable;
554*f6aab3d8Srobert }
555*f6aab3d8Srobert
DownloadObjectAndSymbolFile(ModuleSpec & module_spec,Status & error,bool force_lookup,bool copy_executable)556*f6aab3d8Srobert bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
557*f6aab3d8Srobert Status &error, bool force_lookup,
558*f6aab3d8Srobert bool copy_executable) {
559*f6aab3d8Srobert const UUID *uuid_ptr = module_spec.GetUUIDPtr();
560*f6aab3d8Srobert const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
561*f6aab3d8Srobert
562*f6aab3d8Srobert llvm::StringRef dbgshell_command = GetDbgShellCommand();
563*f6aab3d8Srobert
564*f6aab3d8Srobert // When dbgshell_command is empty, the user has not enabled the use of an
565061da546Spatrick // external program to find the symbols, don't run it for them.
566*f6aab3d8Srobert if (!force_lookup && dbgshell_command.empty())
567*f6aab3d8Srobert return false;
568*f6aab3d8Srobert
569*f6aab3d8Srobert // We need a UUID or valid (existing FileSpec.
570*f6aab3d8Srobert if (!uuid_ptr &&
571*f6aab3d8Srobert (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr)))
572*f6aab3d8Srobert return false;
573*f6aab3d8Srobert
574*f6aab3d8Srobert // We need a dsymForUUID binary or an equivalent executable/script.
575*f6aab3d8Srobert FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable();
576*f6aab3d8Srobert if (!dsymForUUID_exe_spec)
577*f6aab3d8Srobert return false;
578*f6aab3d8Srobert
579*f6aab3d8Srobert const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath();
580*f6aab3d8Srobert const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : "";
581*f6aab3d8Srobert const std::string file_path_str =
582*f6aab3d8Srobert file_spec_ptr ? file_spec_ptr->GetPath() : "";
583*f6aab3d8Srobert
584*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host);
585*f6aab3d8Srobert
586*f6aab3d8Srobert // Create the dsymForUUID command.
587*f6aab3d8Srobert StreamString command;
588*f6aab3d8Srobert const char *copy_executable_arg = copy_executable ? "--copyExecutable " : "";
589*f6aab3d8Srobert if (!uuid_str.empty()) {
590*f6aab3d8Srobert command.Printf("%s --ignoreNegativeCache %s%s",
591*f6aab3d8Srobert dsymForUUID_exe_path.c_str(), copy_executable_arg,
592*f6aab3d8Srobert uuid_str.c_str());
593*f6aab3d8Srobert LLDB_LOGF(log, "Calling %s with UUID %s to find dSYM: %s",
594*f6aab3d8Srobert dsymForUUID_exe_path.c_str(), uuid_str.c_str(),
595*f6aab3d8Srobert command.GetString().data());
596*f6aab3d8Srobert } else if (!file_path_str.empty()) {
597*f6aab3d8Srobert command.Printf("%s --ignoreNegativeCache %s%s",
598*f6aab3d8Srobert dsymForUUID_exe_path.c_str(), copy_executable_arg,
599*f6aab3d8Srobert file_path_str.c_str());
600*f6aab3d8Srobert LLDB_LOGF(log, "Calling %s with file %s to find dSYM: %s",
601*f6aab3d8Srobert dsymForUUID_exe_path.c_str(), file_path_str.c_str(),
602*f6aab3d8Srobert command.GetString().data());
603*f6aab3d8Srobert } else {
604061da546Spatrick return false;
605061da546Spatrick }
606061da546Spatrick
607*f6aab3d8Srobert // Invoke dsymForUUID.
608061da546Spatrick int exit_status = -1;
609061da546Spatrick int signo = -1;
610061da546Spatrick std::string command_output;
611*f6aab3d8Srobert error = Host::RunShellCommand(
612061da546Spatrick command.GetData(),
613061da546Spatrick FileSpec(), // current working directory
614061da546Spatrick &exit_status, // Exit status
615061da546Spatrick &signo, // Signal int *
616061da546Spatrick &command_output, // Command output
617061da546Spatrick std::chrono::seconds(
618be691f3bSpatrick 640), // Large timeout to allow for long dsym download times
619061da546Spatrick false); // Don't run in a shell (we don't need shell expansion)
620*f6aab3d8Srobert
621*f6aab3d8Srobert if (error.Fail() || exit_status != 0 || command_output.empty()) {
622*f6aab3d8Srobert LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')",
623*f6aab3d8Srobert command.GetData(), exit_status, error.AsCString(),
624*f6aab3d8Srobert command_output.c_str());
625*f6aab3d8Srobert return false;
626*f6aab3d8Srobert }
627*f6aab3d8Srobert
628*f6aab3d8Srobert CFCData data(
629*f6aab3d8Srobert CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(),
630*f6aab3d8Srobert command_output.size(), kCFAllocatorNull));
631061da546Spatrick
632061da546Spatrick CFCReleaser<CFDictionaryRef> plist(
633*f6aab3d8Srobert (CFDictionaryRef)::CFPropertyListCreateWithData(
634*f6aab3d8Srobert NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));
635061da546Spatrick
636*f6aab3d8Srobert if (!plist.get()) {
637*f6aab3d8Srobert LLDB_LOGF(log, "'%s' failed: output is not a valid plist",
638*f6aab3d8Srobert command.GetData());
639*f6aab3d8Srobert return false;
640*f6aab3d8Srobert }
641*f6aab3d8Srobert
642*f6aab3d8Srobert if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
643*f6aab3d8Srobert LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary",
644*f6aab3d8Srobert command.GetData());
645*f6aab3d8Srobert return false;
646*f6aab3d8Srobert }
647*f6aab3d8Srobert
648061da546Spatrick if (!uuid_str.empty()) {
649061da546Spatrick CFCString uuid_cfstr(uuid_str.c_str());
650*f6aab3d8Srobert CFDictionaryRef uuid_dict =
651*f6aab3d8Srobert (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());
652*f6aab3d8Srobert return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error);
653*f6aab3d8Srobert }
654*f6aab3d8Srobert
655*f6aab3d8Srobert if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) {
656061da546Spatrick std::vector<CFStringRef> keys(num_values, NULL);
657061da546Spatrick std::vector<CFDictionaryRef> values(num_values, NULL);
658061da546Spatrick ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
659061da546Spatrick (const void **)&values[0]);
660061da546Spatrick if (num_values == 1) {
661*f6aab3d8Srobert return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error);
662*f6aab3d8Srobert }
663*f6aab3d8Srobert
664061da546Spatrick for (CFIndex i = 0; i < num_values; ++i) {
665061da546Spatrick ModuleSpec curr_module_spec;
666*f6aab3d8Srobert if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec,
667*f6aab3d8Srobert error)) {
668061da546Spatrick if (module_spec.GetArchitecture().IsCompatibleMatch(
669061da546Spatrick curr_module_spec.GetArchitecture())) {
670061da546Spatrick module_spec = curr_module_spec;
671061da546Spatrick return true;
672061da546Spatrick }
673061da546Spatrick }
674061da546Spatrick }
675061da546Spatrick }
676*f6aab3d8Srobert
677*f6aab3d8Srobert return false;
678061da546Spatrick }
679