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