1 //===-- SymbolVendorMacOSX.cpp --------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "SymbolVendorMacOSX.h" 10 11 #include <cstring> 12 13 #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" 14 #include "lldb/Core/Module.h" 15 #include "lldb/Core/ModuleSpec.h" 16 #include "lldb/Core/PluginManager.h" 17 #include "lldb/Core/Section.h" 18 #include "lldb/Host/Host.h" 19 #include "lldb/Host/XML.h" 20 #include "lldb/Symbol/LocateSymbolFile.h" 21 #include "lldb/Symbol/ObjectFile.h" 22 #include "lldb/Target/Target.h" 23 #include "lldb/Utility/StreamString.h" 24 #include "lldb/Utility/Timer.h" 25 26 using namespace lldb; 27 using namespace lldb_private; 28 29 LLDB_PLUGIN_DEFINE(SymbolVendorMacOSX) 30 31 // SymbolVendorMacOSX constructor 32 SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp) 33 : SymbolVendor(module_sp) {} 34 35 static bool UUIDsMatch(Module *module, ObjectFile *ofile, 36 lldb_private::Stream *feedback_strm) { 37 if (module && ofile) { 38 // Make sure the UUIDs match 39 lldb_private::UUID dsym_uuid = ofile->GetUUID(); 40 if (!dsym_uuid) { 41 if (feedback_strm) { 42 feedback_strm->PutCString( 43 "warning: failed to get the uuid for object file: '"); 44 ofile->GetFileSpec().Dump(feedback_strm->AsRawOstream()); 45 feedback_strm->PutCString("\n"); 46 } 47 return false; 48 } 49 50 if (dsym_uuid == module->GetUUID()) 51 return true; 52 53 // Emit some warning messages since the UUIDs do not match! 54 if (feedback_strm) { 55 feedback_strm->PutCString( 56 "warning: UUID mismatch detected between modules:\n "); 57 module->GetUUID().Dump(feedback_strm); 58 feedback_strm->PutChar(' '); 59 module->GetFileSpec().Dump(feedback_strm->AsRawOstream()); 60 feedback_strm->PutCString("\n "); 61 dsym_uuid.Dump(feedback_strm); 62 feedback_strm->PutChar(' '); 63 ofile->GetFileSpec().Dump(feedback_strm->AsRawOstream()); 64 feedback_strm->EOL(); 65 } 66 } 67 return false; 68 } 69 70 void SymbolVendorMacOSX::Initialize() { 71 PluginManager::RegisterPlugin(GetPluginNameStatic(), 72 GetPluginDescriptionStatic(), CreateInstance); 73 } 74 75 void SymbolVendorMacOSX::Terminate() { 76 PluginManager::UnregisterPlugin(CreateInstance); 77 } 78 79 llvm::StringRef SymbolVendorMacOSX::GetPluginDescriptionStatic() { 80 return "Symbol vendor for MacOSX that looks for dSYM files that match " 81 "executables."; 82 } 83 84 // CreateInstance 85 // 86 // Platforms can register a callback to use when creating symbol vendors to 87 // allow for complex debug information file setups, and to also allow for 88 // finding separate debug information files. 89 SymbolVendor * 90 SymbolVendorMacOSX::CreateInstance(const lldb::ModuleSP &module_sp, 91 lldb_private::Stream *feedback_strm) { 92 if (!module_sp) 93 return NULL; 94 95 ObjectFile *obj_file = 96 llvm::dyn_cast_or_null<ObjectFileMachO>(module_sp->GetObjectFile()); 97 if (!obj_file) 98 return NULL; 99 100 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); 101 Timer scoped_timer(func_cat, 102 "SymbolVendorMacOSX::CreateInstance (module = %s)", 103 module_sp->GetFileSpec().GetPath().c_str()); 104 SymbolVendorMacOSX *symbol_vendor = new SymbolVendorMacOSX(module_sp); 105 if (symbol_vendor) { 106 char path[PATH_MAX]; 107 path[0] = '\0'; 108 109 // Try and locate the dSYM file on Mac OS X 110 static Timer::Category func_cat2( 111 "SymbolVendorMacOSX::CreateInstance() locate dSYM"); 112 Timer scoped_timer2( 113 func_cat2, 114 "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM", 115 module_sp->GetFileSpec().GetPath().c_str()); 116 117 // First check to see if the module has a symbol file in mind already. If 118 // it does, then we MUST use that. 119 FileSpec dsym_fspec(module_sp->GetSymbolFileFileSpec()); 120 121 ObjectFileSP dsym_objfile_sp; 122 if (!dsym_fspec) { 123 // No symbol file was specified in the module, lets try and find one 124 // ourselves. 125 FileSpec file_spec = obj_file->GetFileSpec(); 126 if (!file_spec) 127 file_spec = module_sp->GetFileSpec(); 128 129 ModuleSpec module_spec(file_spec, module_sp->GetArchitecture()); 130 module_spec.GetUUID() = module_sp->GetUUID(); 131 FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); 132 dsym_fspec = 133 Symbols::LocateExecutableSymbolFile(module_spec, search_paths); 134 if (module_spec.GetSourceMappingList().GetSize()) 135 module_sp->GetSourceMappingList().Append( 136 module_spec.GetSourceMappingList(), true); 137 } 138 139 if (dsym_fspec) { 140 // Compute dSYM root. 141 std::string dsym_root = dsym_fspec.GetPath(); 142 const size_t pos = dsym_root.find("/Contents/Resources/"); 143 dsym_root = pos != std::string::npos ? dsym_root.substr(0, pos) : ""; 144 145 DataBufferSP dsym_file_data_sp; 146 lldb::offset_t dsym_file_data_offset = 0; 147 dsym_objfile_sp = 148 ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0, 149 FileSystem::Instance().GetByteSize(dsym_fspec), 150 dsym_file_data_sp, dsym_file_data_offset); 151 // Important to save the dSYM FileSpec so we don't call 152 // Symbols::LocateExecutableSymbolFile a second time while trying to 153 // add the symbol ObjectFile to this Module. 154 if (dsym_objfile_sp && !module_sp->GetSymbolFileFileSpec()) { 155 module_sp->SetSymbolFileFileSpec(dsym_fspec); 156 } 157 if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) { 158 // We need a XML parser if we hope to parse a plist... 159 if (XMLDocument::XMLEnabled()) { 160 if (module_sp->GetSourceMappingList().IsEmpty()) { 161 lldb_private::UUID dsym_uuid = dsym_objfile_sp->GetUUID(); 162 if (dsym_uuid) { 163 std::string uuid_str = dsym_uuid.GetAsString(); 164 if (!uuid_str.empty() && !dsym_root.empty()) { 165 char dsym_uuid_plist_path[PATH_MAX]; 166 snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path), 167 "%s/Contents/Resources/%s.plist", dsym_root.c_str(), 168 uuid_str.c_str()); 169 FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path); 170 if (FileSystem::Instance().Exists(dsym_uuid_plist_spec)) { 171 ApplePropertyList plist(dsym_uuid_plist_path); 172 if (plist) { 173 std::string DBGBuildSourcePath; 174 std::string DBGSourcePath; 175 176 // DBGSourcePathRemapping is a dictionary in the plist 177 // with keys which are DBGBuildSourcePath file paths and 178 // values which are DBGSourcePath file paths 179 180 StructuredData::ObjectSP plist_sp = 181 plist.GetStructuredData(); 182 if (plist_sp.get() && plist_sp->GetAsDictionary() && 183 plist_sp->GetAsDictionary()->HasKey( 184 "DBGSourcePathRemapping") && 185 plist_sp->GetAsDictionary() 186 ->GetValueForKey("DBGSourcePathRemapping") 187 ->GetAsDictionary()) { 188 189 // If DBGVersion 1 or DBGVersion missing, ignore 190 // DBGSourcePathRemapping. If DBGVersion 2, strip last two 191 // components of path remappings from 192 // entries to fix an issue with a 193 // specific set of DBGSourcePathRemapping 194 // entries that lldb worked with. 195 // If DBGVersion 3, trust & use the source path remappings 196 // as-is. 197 // 198 199 bool new_style_source_remapping_dictionary = false; 200 bool do_truncate_remapping_names = false; 201 std::string original_DBGSourcePath_value = DBGSourcePath; 202 if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) { 203 std::string version_string = 204 std::string(plist_sp->GetAsDictionary() 205 ->GetValueForKey("DBGVersion") 206 ->GetStringValue("")); 207 if (!version_string.empty() && 208 isdigit(version_string[0])) { 209 int version_number = atoi(version_string.c_str()); 210 if (version_number > 1) { 211 new_style_source_remapping_dictionary = true; 212 } 213 if (version_number == 2) { 214 do_truncate_remapping_names = true; 215 } 216 } 217 } 218 219 StructuredData::Dictionary *remappings_dict = 220 plist_sp->GetAsDictionary() 221 ->GetValueForKey("DBGSourcePathRemapping") 222 ->GetAsDictionary(); 223 remappings_dict->ForEach( 224 [&module_sp, new_style_source_remapping_dictionary, 225 original_DBGSourcePath_value, 226 do_truncate_remapping_names]( 227 ConstString key, 228 StructuredData::Object *object) -> bool { 229 if (object && object->GetAsString()) { 230 231 // key is DBGBuildSourcePath 232 // object is DBGSourcePath 233 std::string DBGSourcePath = 234 std::string(object->GetStringValue()); 235 if (!new_style_source_remapping_dictionary && 236 !original_DBGSourcePath_value.empty()) { 237 DBGSourcePath = original_DBGSourcePath_value; 238 } 239 module_sp->GetSourceMappingList().Append( 240 key.GetStringRef(), DBGSourcePath, true); 241 // With version 2 of DBGSourcePathRemapping, we 242 // can chop off the last two filename parts 243 // from the source remapping and get a more 244 // general source remapping that still works. 245 // Add this as another option in addition to 246 // the full source path remap. 247 if (do_truncate_remapping_names) { 248 FileSpec build_path(key.AsCString()); 249 FileSpec source_path(DBGSourcePath.c_str()); 250 build_path.RemoveLastPathComponent(); 251 build_path.RemoveLastPathComponent(); 252 source_path.RemoveLastPathComponent(); 253 source_path.RemoveLastPathComponent(); 254 module_sp->GetSourceMappingList().Append( 255 build_path.GetPath(), source_path.GetPath(), 256 true); 257 } 258 } 259 return true; 260 }); 261 } 262 263 // If we have a DBGBuildSourcePath + DBGSourcePath pair, 264 // append those to the source path remappings. 265 266 plist.GetValueAsString("DBGBuildSourcePath", 267 DBGBuildSourcePath); 268 plist.GetValueAsString("DBGSourcePath", DBGSourcePath); 269 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 270 module_sp->GetSourceMappingList().Append( 271 DBGBuildSourcePath, DBGSourcePath, true); 272 } 273 } 274 } 275 } 276 } 277 } 278 } 279 280 symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp); 281 return symbol_vendor; 282 } 283 } 284 285 // Just create our symbol vendor using the current objfile as this is 286 // either an executable with no dSYM (that we could locate), an executable 287 // with a dSYM that has a UUID that doesn't match. 288 symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this()); 289 } 290 return symbol_vendor; 291 } 292