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