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 98 // vendors to allow for complex debug information file setups, and to 99 // also allow for 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. 134 // If 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 140 // one 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 = ObjectFile::FindPlugin( 157 module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(), 158 dsym_file_data_sp, dsym_file_data_offset); 159 if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) { 160 // We need a XML parser if we hope to parse a plist... 161 if (XMLDocument::XMLEnabled()) { 162 char dsym_path[PATH_MAX]; 163 if (module_sp->GetSourceMappingList().IsEmpty() && 164 dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) { 165 lldb_private::UUID dsym_uuid; 166 if (dsym_objfile_sp->GetUUID(&dsym_uuid)) { 167 std::string uuid_str = dsym_uuid.GetAsString(); 168 if (!uuid_str.empty()) { 169 char *resources = strstr(dsym_path, "/Contents/Resources/"); 170 if (resources) { 171 char dsym_uuid_plist_path[PATH_MAX]; 172 resources[strlen("/Contents/Resources/")] = '\0'; 173 snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path), 174 "%s%s.plist", dsym_path, uuid_str.c_str()); 175 FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false); 176 if (dsym_uuid_plist_spec.Exists()) { 177 ApplePropertyList plist(dsym_uuid_plist_path); 178 if (plist) { 179 std::string DBGBuildSourcePath; 180 std::string DBGSourcePath; 181 182 plist.GetValueAsString("DBGBuildSourcePath", 183 DBGBuildSourcePath); 184 plist.GetValueAsString("DBGSourcePath", DBGSourcePath); 185 if (!DBGBuildSourcePath.empty() && 186 !DBGSourcePath.empty()) { 187 if (DBGSourcePath[0] == '~') { 188 FileSpec resolved_source_path(DBGSourcePath.c_str(), 189 true); 190 DBGSourcePath = resolved_source_path.GetPath(); 191 } 192 module_sp->GetSourceMappingList().Append( 193 ConstString(DBGBuildSourcePath), 194 ConstString(DBGSourcePath), true); 195 } 196 197 // DBGSourcePathRemapping is a dictionary in the plist 198 // with 199 // keys which are DBGBuildSourcePath file paths and 200 // values which are DBGSourcePath file paths 201 202 StructuredData::ObjectSP plist_sp = 203 plist.GetStructuredData(); 204 if (plist_sp.get() && plist_sp->GetAsDictionary() && 205 plist_sp->GetAsDictionary()->HasKey( 206 "DBGSourcePathRemapping") && 207 plist_sp->GetAsDictionary() 208 ->GetValueForKey("DBGSourcePathRemapping") 209 ->GetAsDictionary()) { 210 211 // In an early version of DBGSourcePathRemapping, the 212 // DBGSourcePath 213 // values were incorrect. If we have a newer style 214 // DBGSourcePathRemapping, there will be a DBGVersion 215 // key in the plist with version 2 or higher. 216 // 217 // If this is an old style DBGSourcePathRemapping, 218 // ignore the 219 // value half of the key-value remappings and use reuse 220 // the original 221 // gloal DBGSourcePath string. 222 bool new_style_source_remapping_dictionary = false; 223 bool do_truncate_remapping_names = false; 224 std::string original_DBGSourcePath_value = 225 DBGSourcePath; 226 if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) { 227 std::string version_string = 228 plist_sp->GetAsDictionary() 229 ->GetValueForKey("DBGVersion") 230 ->GetStringValue(""); 231 if (!version_string.empty() && 232 isdigit(version_string[0])) { 233 int version_number = atoi(version_string.c_str()); 234 if (version_number > 1) { 235 new_style_source_remapping_dictionary = true; 236 } 237 if (version_number == 2) { 238 do_truncate_remapping_names = true; 239 } 240 } 241 } 242 243 StructuredData::Dictionary *remappings_dict = 244 plist_sp->GetAsDictionary() 245 ->GetValueForKey("DBGSourcePathRemapping") 246 ->GetAsDictionary(); 247 remappings_dict->ForEach( 248 [&module_sp, new_style_source_remapping_dictionary, 249 original_DBGSourcePath_value, do_truncate_remapping_names]( 250 ConstString key, 251 StructuredData::Object *object) -> bool { 252 if (object && object->GetAsString()) { 253 254 // key is DBGBuildSourcePath 255 // object is DBGSourcePath 256 std::string DBGSourcePath = 257 object->GetStringValue(); 258 if (new_style_source_remapping_dictionary == 259 false && 260 !original_DBGSourcePath_value.empty()) { 261 DBGSourcePath = original_DBGSourcePath_value; 262 } 263 if (DBGSourcePath[0] == '~') { 264 FileSpec resolved_source_path( 265 DBGSourcePath.c_str(), true); 266 DBGSourcePath = 267 resolved_source_path.GetPath(); 268 } 269 module_sp->GetSourceMappingList().Append( 270 key, ConstString(DBGSourcePath), true); 271 // With version 2 of DBGSourcePathRemapping, we can chop off the 272 // last two filename parts from the source remapping and get a 273 // more general source remapping that still works. Add this as 274 // another option in addition to the full source path remap. 275 if (do_truncate_remapping_names) { 276 FileSpec build_path(key.AsCString(), false); 277 FileSpec source_path(DBGSourcePath.c_str(), false); 278 build_path.RemoveLastPathComponent(); 279 build_path.RemoveLastPathComponent(); 280 source_path.RemoveLastPathComponent(); 281 source_path.RemoveLastPathComponent(); 282 module_sp->GetSourceMappingList().Append( 283 ConstString(build_path.GetPath().c_str()), 284 ConstString(source_path.GetPath().c_str()), true); 285 } 286 } 287 return true; 288 }); 289 } 290 } 291 } 292 } 293 } 294 } 295 } 296 } 297 298 symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp); 299 return symbol_vendor; 300 } 301 } 302 303 // Just create our symbol vendor using the current objfile as this is either 304 // an executable with no dSYM (that we could locate), an executable with 305 // a dSYM that has a UUID that doesn't match. 306 symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this()); 307 } 308 return symbol_vendor; 309 } 310 311 //------------------------------------------------------------------ 312 // PluginInterface protocol 313 //------------------------------------------------------------------ 314 ConstString SymbolVendorMacOSX::GetPluginName() { 315 return GetPluginNameStatic(); 316 } 317 318 uint32_t SymbolVendorMacOSX::GetPluginVersion() { return 1; } 319