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/ModuleSpec.h" 12 #include "lldb/Host/FileSystem.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, FileSpec::Style::native); 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 and 115 // /S/L/F/Foundation.framework.dSYM 116 // 117 // so we'll need to start with 118 // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the 119 // "A", and if that doesn't exist, strip off the "A" and try it again 120 // with "Versions", etc., until we find a dSYM bundle or we've 121 // stripped off enough path components that there's no need to 122 // 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 135 // if 136 // "Foundation.framework.dSYM/Contents/Resources/DWARF/Foundation" 137 // exists & has the right uuid. 138 std::string dsym_fn = fn; 139 dsym_fn += ".dSYM"; 140 dsym_fspec.AppendPathComponent(dsym_fn.c_str()); 141 dsym_fspec.AppendPathComponent("Contents"); 142 dsym_fspec.AppendPathComponent("Resources"); 143 dsym_fspec.AppendPathComponent("DWARF"); 144 dsym_fspec.AppendPathComponent( 145 exec_fspec->GetFilename().AsCString()); 146 if (dsym_fspec.Exists() && 147 FileAtPathContainsArchAndUUID( 148 dsym_fspec, module_spec.GetArchitecturePtr(), 149 module_spec.GetUUIDPtr())) { 150 if (log) { 151 log->Printf("dSYM with matching UUID & arch found at %s", 152 dsym_fspec.GetPath().c_str()); 153 } 154 return true; 155 } 156 } 157 parent_dirs.RemoveLastPathComponent(); 158 } 159 } 160 } 161 } 162 } 163 dsym_fspec.Clear(); 164 return false; 165 } 166 167 FileSpec LocateExecutableSymbolFileDsym(const ModuleSpec &module_spec) { 168 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 169 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 170 const UUID *uuid = module_spec.GetUUIDPtr(); 171 172 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); 173 Timer scoped_timer( 174 func_cat, 175 "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)", 176 exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>", 177 arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid); 178 179 FileSpec symbol_fspec; 180 ModuleSpec dsym_module_spec; 181 // First try and find the dSYM in the same directory as the executable or in 182 // an appropriate parent directory 183 if (LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec) == false) { 184 // We failed to easily find the dSYM above, so use DebugSymbols 185 LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec); 186 } else { 187 dsym_module_spec.GetSymbolFileSpec() = symbol_fspec; 188 } 189 return dsym_module_spec.GetSymbolFileSpec(); 190 } 191 192 ModuleSpec Symbols::LocateExecutableObjectFile(const ModuleSpec &module_spec) { 193 ModuleSpec result; 194 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 195 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 196 const UUID *uuid = module_spec.GetUUIDPtr(); 197 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); 198 Timer scoped_timer( 199 func_cat, "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", 200 exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>", 201 arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid); 202 203 ModuleSpecList module_specs; 204 ModuleSpec matched_module_spec; 205 if (exec_fspec && 206 ObjectFile::GetModuleSpecifications(*exec_fspec, 0, 0, module_specs) && 207 module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) { 208 result.GetFileSpec() = exec_fspec; 209 } else { 210 LocateMacOSXFilesUsingDebugSymbols(module_spec, result); 211 } 212 return result; 213 } 214 215 FileSpec Symbols::LocateExecutableSymbolFile(const ModuleSpec &module_spec) { 216 FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec(); 217 if (symbol_file_spec.IsAbsolute() && symbol_file_spec.Exists()) 218 return symbol_file_spec; 219 220 const char *symbol_filename = symbol_file_spec.GetFilename().AsCString(); 221 if (symbol_filename && symbol_filename[0]) { 222 FileSpecList debug_file_search_paths( 223 Target::GetDefaultDebugFileSearchPaths()); 224 225 // Add module directory. 226 FileSpec module_file_spec = module_spec.GetFileSpec(); 227 // We keep the unresolved pathname if it fails. 228 FileSystem::ResolveSymbolicLink(module_file_spec, module_file_spec); 229 230 const ConstString &file_dir = module_file_spec.GetDirectory(); 231 debug_file_search_paths.AppendIfUnique( 232 FileSpec(file_dir.AsCString("."), true)); 233 234 // Add current working directory. 235 debug_file_search_paths.AppendIfUnique(FileSpec(".", true)); 236 237 #ifndef _WIN32 238 #if defined(__NetBSD__) 239 // Add /usr/libdata/debug directory. 240 debug_file_search_paths.AppendIfUnique( 241 FileSpec("/usr/libdata/debug", true)); 242 #else 243 // Add /usr/lib/debug directory. 244 debug_file_search_paths.AppendIfUnique(FileSpec("/usr/lib/debug", true)); 245 #endif 246 #endif // _WIN32 247 248 std::string uuid_str; 249 const UUID &module_uuid = module_spec.GetUUID(); 250 if (module_uuid.IsValid()) { 251 // Some debug files are stored in the .build-id directory like this: 252 // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug 253 uuid_str = module_uuid.GetAsString(""); 254 std::transform(uuid_str.begin(), uuid_str.end(), uuid_str.begin(), 255 ::tolower); 256 uuid_str.insert(2, 1, '/'); 257 uuid_str = uuid_str + ".debug"; 258 } 259 260 size_t num_directories = debug_file_search_paths.GetSize(); 261 for (size_t idx = 0; idx < num_directories; ++idx) { 262 FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx); 263 dirspec.ResolvePath(); 264 if (!llvm::sys::fs::is_directory(dirspec.GetPath())) 265 continue; 266 267 std::vector<std::string> files; 268 std::string dirname = dirspec.GetPath(); 269 270 files.push_back(dirname + "/" + symbol_filename); 271 files.push_back(dirname + "/.debug/" + symbol_filename); 272 files.push_back(dirname + "/.build-id/" + uuid_str); 273 274 // Some debug files may stored in the module directory like this: 275 // /usr/lib/debug/usr/lib/library.so.debug 276 if (!file_dir.IsEmpty()) 277 files.push_back(dirname + file_dir.AsCString() + "/" + symbol_filename); 278 279 const uint32_t num_files = files.size(); 280 for (size_t idx_file = 0; idx_file < num_files; ++idx_file) { 281 const std::string &filename = files[idx_file]; 282 FileSpec file_spec(filename, true); 283 284 if (llvm::sys::fs::equivalent(file_spec.GetPath(), 285 module_file_spec.GetPath())) 286 continue; 287 288 if (file_spec.Exists()) { 289 lldb_private::ModuleSpecList specs; 290 const size_t num_specs = 291 ObjectFile::GetModuleSpecifications(file_spec, 0, 0, specs); 292 assert(num_specs <= 1 && 293 "Symbol Vendor supports only a single architecture"); 294 if (num_specs == 1) { 295 ModuleSpec mspec; 296 if (specs.GetModuleSpecAtIndex(0, mspec)) { 297 // Skip the uuids check if module_uuid is invalid. For example, 298 // this happens for *.dwp files since at the moment llvm-dwp 299 // doesn't output build ids, nor does binutils dwp. 300 if (!module_uuid.IsValid() || module_uuid == mspec.GetUUID()) 301 return file_spec; 302 } 303 } 304 } 305 } 306 } 307 } 308 309 return LocateExecutableSymbolFileDsym(module_spec); 310 } 311 312 #if !defined(__APPLE__) 313 314 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &symfile_bundle, 315 const lldb_private::UUID *uuid, 316 const ArchSpec *arch) { 317 // FIXME 318 return FileSpec(); 319 } 320 321 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec, 322 bool force_lookup) { 323 // Fill in the module_spec.GetFileSpec() for the object file and/or the 324 // module_spec.GetSymbolFileSpec() for the debug symbols file. 325 return false; 326 } 327 328 #endif 329