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 #include "lldb/Core/Module.h"
12 #include "lldb/Core/ModuleSpec.h"
13 #include "lldb/Symbol/ObjectFile.h"
14 #include "lldb/Target/Target.h"
15 #include "lldb/Utility/ArchSpec.h"
16 #include "lldb/Utility/DataBuffer.h"
17 #include "lldb/Utility/DataExtractor.h"
18 #include "lldb/Utility/Log.h"
19 #include "lldb/Utility/SafeMachO.h"
20 #include "lldb/Utility/StreamString.h"
21 #include "lldb/Utility/Timer.h"
22 #include "lldb/Utility/UUID.h"
23 
24 #include "llvm/Support/FileSystem.h"
25 
26 // From MacOSX system header "mach/machine.h"
27 typedef int cpu_type_t;
28 typedef int cpu_subtype_t;
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace llvm::MachO;
33 
34 #if defined(__APPLE__)
35 
36 // Forward declaration of method defined in source/Host/macosx/Symbols.cpp
37 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
38                                        ModuleSpec &return_module_spec);
39 
40 #else
41 
42 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
43                                        ModuleSpec &return_module_spec) {
44   // Cannot find MacOSX files using debug symbols on non MacOSX.
45   return 0;
46 }
47 
48 #endif
49 
50 static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec,
51                                           const ArchSpec *arch,
52                                           const lldb_private::UUID *uuid) {
53   ModuleSpecList module_specs;
54   if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) {
55     ModuleSpec spec;
56     for (size_t i = 0; i < module_specs.GetSize(); ++i) {
57       bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
58       UNUSED_IF_ASSERT_DISABLED(got_spec);
59       assert(got_spec);
60       if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
61           (arch == NULL || (spec.GetArchitecturePtr() &&
62                             spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
63         return true;
64       }
65     }
66   }
67   return false;
68 }
69 
70 static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,
71                                               FileSpec &dsym_fspec) {
72   Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
73   const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
74   if (exec_fspec) {
75     char path[PATH_MAX];
76     if (exec_fspec->GetPath(path, sizeof(path))) {
77       // Make sure the module isn't already just a dSYM file...
78       if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) {
79         if (log) {
80           if (module_spec.GetUUIDPtr() && module_spec.GetUUIDPtr()->IsValid()) {
81             log->Printf(
82                 "Searching for dSYM bundle next to executable %s, UUID %s",
83                 path, module_spec.GetUUIDPtr()->GetAsString().c_str());
84           } else {
85             log->Printf("Searching for dSYM bundle next to executable %s",
86                         path);
87           }
88         }
89         ::strncat(path, ".dSYM/Contents/Resources/DWARF/",
90                   sizeof(path) - strlen(path) - 1);
91         ::strncat(path, exec_fspec->GetFilename().AsCString(),
92                   sizeof(path) - strlen(path) - 1);
93 
94         dsym_fspec.SetFile(path, false);
95 
96         ModuleSpecList module_specs;
97         ModuleSpec matched_module_spec;
98         if (dsym_fspec.Exists() &&
99             FileAtPathContainsArchAndUUID(dsym_fspec,
100                                           module_spec.GetArchitecturePtr(),
101                                           module_spec.GetUUIDPtr())) {
102           if (log) {
103             log->Printf("dSYM with matching UUID & arch found at %s", path);
104           }
105           return true;
106         } else {
107           FileSpec parent_dirs = exec_fspec;
108 
109           // Remove the binary name from the FileSpec
110           parent_dirs.RemoveLastPathComponent();
111 
112           // Add a ".dSYM" name to each directory component of the path,
113           // stripping off components.  e.g. we may have a binary like
114           // /S/L/F/Foundation.framework/Versions/A/Foundation
115           // and
116           // /S/L/F/Foundation.framework.dSYM
117           //
118           // so we'll need to start with /S/L/F/Foundation.framework/Versions/A,
119           // add the .dSYM part to the "A", and if that doesn't exist, strip off
120           // the "A" and try it again with "Versions", etc., until we find a
121           // dSYM bundle or we've stripped off enough path components that
122           // there's no need to continue.
123 
124           for (int i = 0; i < 4; i++) {
125             // Does this part of the path have a "." character - could it be a
126             // bundle's top level directory?
127             const char *fn = parent_dirs.GetFilename().AsCString();
128             if (fn == nullptr)
129               break;
130             if (::strchr(fn, '.') != nullptr) {
131               dsym_fspec = parent_dirs;
132               dsym_fspec.RemoveLastPathComponent();
133 
134               // If the current directory name is "Foundation.framework", see if
135               // "Foundation.framework.dSYM/Contents/Resources/DWARF/Foundation"
136               // exists & has the right uuid.
137               std::string dsym_fn = fn;
138               dsym_fn += ".dSYM";
139               dsym_fspec.AppendPathComponent(dsym_fn.c_str());
140               dsym_fspec.AppendPathComponent("Contents");
141               dsym_fspec.AppendPathComponent("Resources");
142               dsym_fspec.AppendPathComponent("DWARF");
143               dsym_fspec.AppendPathComponent(
144                   exec_fspec->GetFilename().AsCString());
145               if (dsym_fspec.Exists() &&
146                   FileAtPathContainsArchAndUUID(
147                       dsym_fspec, module_spec.GetArchitecturePtr(),
148                       module_spec.GetUUIDPtr())) {
149                 if (log) {
150                   log->Printf("dSYM with matching UUID & arch found at %s",
151                               dsym_fspec.GetPath().c_str());
152                 }
153                 return true;
154               }
155             }
156             parent_dirs.RemoveLastPathComponent();
157           }
158         }
159       }
160     }
161   }
162   dsym_fspec.Clear();
163   return false;
164 }
165 
166 FileSpec LocateExecutableSymbolFileDsym(const ModuleSpec &module_spec) {
167   const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
168   const ArchSpec *arch = module_spec.GetArchitecturePtr();
169   const UUID *uuid = module_spec.GetUUIDPtr();
170 
171   static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
172   Timer scoped_timer(
173       func_cat,
174       "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)",
175       exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
176       arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
177 
178   FileSpec symbol_fspec;
179   ModuleSpec dsym_module_spec;
180   // First try and find the dSYM in the same directory as the executable or in
181   // an appropriate parent directory
182   if (LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec) == false) {
183     // We failed to easily find the dSYM above, so use DebugSymbols
184     LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);
185   } else {
186     dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
187   }
188   return dsym_module_spec.GetSymbolFileSpec();
189 }
190 
191 ModuleSpec Symbols::LocateExecutableObjectFile(const ModuleSpec &module_spec) {
192   ModuleSpec result;
193   const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
194   const ArchSpec *arch = module_spec.GetArchitecturePtr();
195   const UUID *uuid = module_spec.GetUUIDPtr();
196   static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
197   Timer scoped_timer(
198       func_cat, "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
199       exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
200       arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
201 
202   ModuleSpecList module_specs;
203   ModuleSpec matched_module_spec;
204   if (exec_fspec &&
205       ObjectFile::GetModuleSpecifications(*exec_fspec, 0, 0, module_specs) &&
206       module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) {
207     result.GetFileSpec() = exec_fspec;
208   } else {
209     LocateMacOSXFilesUsingDebugSymbols(module_spec, result);
210   }
211   return result;
212 }
213 
214 FileSpec Symbols::LocateExecutableSymbolFile(const ModuleSpec &module_spec) {
215   FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec();
216   if (symbol_file_spec.IsAbsolute() && symbol_file_spec.Exists())
217     return symbol_file_spec;
218 
219   const char *symbol_filename = symbol_file_spec.GetFilename().AsCString();
220   if (symbol_filename && symbol_filename[0]) {
221     FileSpecList debug_file_search_paths(
222         Target::GetDefaultDebugFileSearchPaths());
223 
224     // Add module directory.
225     const ConstString &file_dir = module_spec.GetFileSpec().GetDirectory();
226     debug_file_search_paths.AppendIfUnique(
227         FileSpec(file_dir.AsCString("."), true));
228 
229     // Add current working directory.
230     debug_file_search_paths.AppendIfUnique(FileSpec(".", true));
231 
232 #ifndef LLVM_ON_WIN32
233 #if defined(__NetBSD__)
234     // Add /usr/libdata/debug directory.
235     debug_file_search_paths.AppendIfUnique(
236         FileSpec("/usr/libdata/debug", true));
237 #else
238     // Add /usr/lib/debug directory.
239     debug_file_search_paths.AppendIfUnique(FileSpec("/usr/lib/debug", true));
240 #endif
241 #endif // LLVM_ON_WIN32
242 
243     std::string uuid_str;
244     const UUID &module_uuid = module_spec.GetUUID();
245     if (module_uuid.IsValid()) {
246       // Some debug files are stored in the .build-id directory like this:
247       //   /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug
248       uuid_str = module_uuid.GetAsString("");
249       uuid_str.insert(2, 1, '/');
250       uuid_str = uuid_str + ".debug";
251     }
252 
253     size_t num_directories = debug_file_search_paths.GetSize();
254     for (size_t idx = 0; idx < num_directories; ++idx) {
255       FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx);
256       dirspec.ResolvePath();
257       if (!llvm::sys::fs::is_directory(dirspec.GetPath()))
258         continue;
259 
260       std::vector<std::string> files;
261       std::string dirname = dirspec.GetPath();
262 
263       files.push_back(dirname + "/" + symbol_filename);
264       files.push_back(dirname + "/.debug/" + symbol_filename);
265       files.push_back(dirname + "/.build-id/" + uuid_str);
266 
267       // Some debug files may stored in the module directory like this:
268       //   /usr/lib/debug/usr/lib/library.so.debug
269       if (!file_dir.IsEmpty())
270         files.push_back(dirname + file_dir.AsCString() + "/" + symbol_filename);
271 
272       const uint32_t num_files = files.size();
273       for (size_t idx_file = 0; idx_file < num_files; ++idx_file) {
274         const std::string &filename = files[idx_file];
275         FileSpec file_spec(filename, true);
276 
277         if (llvm::sys::fs::equivalent(file_spec.GetPath(),
278                                       module_spec.GetFileSpec().GetPath()))
279           continue;
280 
281         if (file_spec.Exists()) {
282           lldb_private::ModuleSpecList specs;
283           const size_t num_specs =
284               ObjectFile::GetModuleSpecifications(file_spec, 0, 0, specs);
285           assert(num_specs <= 1 &&
286                  "Symbol Vendor supports only a single architecture");
287           if (num_specs == 1) {
288             ModuleSpec mspec;
289             if (specs.GetModuleSpecAtIndex(0, mspec)) {
290               // Skip the uuids check if module_uuid is invalid.
291               // For example, this happens for *.dwp files since
292               // at the moment llvm-dwp doesn't output build ids,
293               // nor does binutils dwp.
294               if (!module_uuid.IsValid() || module_uuid == mspec.GetUUID())
295                 return file_spec;
296             }
297           }
298         }
299       }
300     }
301   }
302 
303   return LocateExecutableSymbolFileDsym(module_spec);
304 }
305 
306 #if !defined(__APPLE__)
307 
308 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &symfile_bundle,
309                                          const lldb_private::UUID *uuid,
310                                          const ArchSpec *arch) {
311   // FIXME
312   return FileSpec();
313 }
314 
315 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
316                                           bool force_lookup) {
317   // Fill in the module_spec.GetFileSpec() for the object file and/or the
318   // module_spec.GetSymbolFileSpec() for the debug symbols file.
319   return false;
320 }
321 
322 #endif
323