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 <libxml/parser.h> 13 #include <libxml/tree.h> 14 #include <string.h> 15 16 #include <AvailabilityMacros.h> 17 18 #include "lldb/Core/Module.h" 19 #include "lldb/Core/ModuleSpec.h" 20 #include "lldb/Core/PluginManager.h" 21 #include "lldb/Core/Section.h" 22 #include "lldb/Core/StreamString.h" 23 #include "lldb/Core/Timer.h" 24 #include "lldb/Host/Host.h" 25 #include "lldb/Host/Symbols.h" 26 #include "lldb/Symbol/ObjectFile.h" 27 28 using namespace lldb; 29 using namespace lldb_private; 30 31 //---------------------------------------------------------------------- 32 // SymbolVendorMacOSX constructor 33 //---------------------------------------------------------------------- 34 SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp) : 35 SymbolVendor (module_sp) 36 { 37 } 38 39 //---------------------------------------------------------------------- 40 // Destructor 41 //---------------------------------------------------------------------- 42 SymbolVendorMacOSX::~SymbolVendorMacOSX() 43 { 44 } 45 46 47 static bool 48 UUIDsMatch(Module *module, ObjectFile *ofile, lldb_private::Stream *feedback_strm) 49 { 50 if (module && ofile) 51 { 52 // Make sure the UUIDs match 53 lldb_private::UUID dsym_uuid; 54 55 if (!ofile->GetUUID(&dsym_uuid)) 56 { 57 if (feedback_strm) 58 { 59 feedback_strm->PutCString("warning: failed to get the uuid for object file: '"); 60 ofile->GetFileSpec().Dump(feedback_strm); 61 feedback_strm->PutCString("\n"); 62 } 63 return false; 64 } 65 66 if (dsym_uuid == module->GetUUID()) 67 return true; 68 69 // Emit some warning messages since the UUIDs do not match! 70 if (feedback_strm) 71 { 72 feedback_strm->PutCString("warning: UUID mismatch detected between modules:\n "); 73 module->GetUUID().Dump(feedback_strm); 74 feedback_strm->PutChar(' '); 75 module->GetFileSpec().Dump(feedback_strm); 76 feedback_strm->PutCString("\n "); 77 dsym_uuid.Dump(feedback_strm); 78 feedback_strm->PutChar(' '); 79 ofile->GetFileSpec().Dump(feedback_strm); 80 feedback_strm->EOL(); 81 } 82 } 83 return false; 84 } 85 86 static void 87 ReplaceDSYMSectionsWithExecutableSections (ObjectFile *exec_objfile, ObjectFile *dsym_objfile) 88 { 89 // We need both the executable and the dSYM to live off of the 90 // same section lists. So we take all of the sections from the 91 // executable, and replace them in the dSYM. This allows section 92 // offset addresses that come from the dSYM to automatically 93 // get updated as images (shared libraries) get loaded and 94 // unloaded. 95 SectionList *exec_section_list = exec_objfile->GetSectionList(); 96 SectionList *dsym_section_list = dsym_objfile->GetSectionList(); 97 if (exec_section_list && dsym_section_list) 98 { 99 const uint32_t num_exec_sections = dsym_section_list->GetSize(); 100 uint32_t exec_sect_idx; 101 for (exec_sect_idx = 0; exec_sect_idx < num_exec_sections; ++exec_sect_idx) 102 { 103 SectionSP exec_sect_sp(exec_section_list->GetSectionAtIndex(exec_sect_idx)); 104 if (exec_sect_sp.get()) 105 { 106 // Try and replace any sections that exist in both the executable 107 // and in the dSYM with those from the executable. If we fail to 108 // replace the one in the dSYM, then add the executable section to 109 // the dSYM. 110 SectionSP dsym_sect_sp(dsym_section_list->FindSectionByID(exec_sect_sp->GetID())); 111 if (dsym_sect_sp.get() && dsym_sect_sp->GetName() != exec_sect_sp->GetName()) 112 { 113 // The sections in a dSYM are normally a superset of the sections in an executable. 114 // If we find a section # in the exectuable & dSYM that don't have the same name, 115 // something has changed since the dSYM was written. The mach_kernel DSTROOT binary 116 // has a CTF segment added, for instance, and it's easiest to simply not add that to 117 // the dSYM - none of the nlist entries are going to have references to that section. 118 continue; 119 } 120 if (dsym_section_list->ReplaceSection(exec_sect_sp->GetID(), exec_sect_sp, 0) == false) 121 dsym_section_list->AddSection(exec_sect_sp); 122 } 123 } 124 125 dsym_section_list->Finalize(); // Now that we're done adding sections, finalize to build fast-lookup caches 126 } 127 } 128 129 void 130 SymbolVendorMacOSX::Initialize() 131 { 132 PluginManager::RegisterPlugin (GetPluginNameStatic(), 133 GetPluginDescriptionStatic(), 134 CreateInstance); 135 } 136 137 void 138 SymbolVendorMacOSX::Terminate() 139 { 140 PluginManager::UnregisterPlugin (CreateInstance); 141 } 142 143 144 const char * 145 SymbolVendorMacOSX::GetPluginNameStatic() 146 { 147 return "symbol-vendor.macosx"; 148 } 149 150 const char * 151 SymbolVendorMacOSX::GetPluginDescriptionStatic() 152 { 153 return "Symbol vendor for MacOSX that looks for dSYM files that match executables."; 154 } 155 156 157 158 //---------------------------------------------------------------------- 159 // CreateInstance 160 // 161 // Platforms can register a callback to use when creating symbol 162 // vendors to allow for complex debug information file setups, and to 163 // also allow for finding separate debug information files. 164 //---------------------------------------------------------------------- 165 SymbolVendor* 166 SymbolVendorMacOSX::CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm) 167 { 168 if (!module_sp) 169 return NULL; 170 171 Timer scoped_timer (__PRETTY_FUNCTION__, 172 "SymbolVendorMacOSX::CreateInstance (module = %s)", 173 module_sp->GetFileSpec().GetPath().c_str()); 174 SymbolVendorMacOSX* symbol_vendor = new SymbolVendorMacOSX(module_sp); 175 if (symbol_vendor) 176 { 177 char path[PATH_MAX]; 178 path[0] = '\0'; 179 180 // Try and locate the dSYM file on Mac OS X 181 ObjectFile * obj_file = module_sp->GetObjectFile(); 182 if (obj_file) 183 { 184 Timer scoped_timer2 ("SymbolVendorMacOSX::CreateInstance () locate dSYM", 185 "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM", 186 module_sp->GetFileSpec().GetPath().c_str()); 187 188 // First check to see if the module has a symbol file in mind already. 189 // If it does, then we MUST use that. 190 FileSpec dsym_fspec (module_sp->GetSymbolFileFileSpec()); 191 192 ObjectFileSP dsym_objfile_sp; 193 if (!dsym_fspec) 194 { 195 // No symbol file was specified in the module, lets try and find 196 // one ourselves. 197 FileSpec file_spec = obj_file->GetFileSpec(); 198 if (!file_spec) 199 file_spec = module_sp->GetFileSpec(); 200 201 ModuleSpec module_spec(file_spec, module_sp->GetArchitecture()); 202 module_spec.GetUUID() = module_sp->GetUUID(); 203 dsym_fspec = Symbols::LocateExecutableSymbolFile (module_spec); 204 if (module_spec.GetSourceMappingList().GetSize()) 205 module_sp->GetSourceMappingList().Append (module_spec.GetSourceMappingList (), true); 206 } 207 208 if (dsym_fspec) 209 { 210 DataBufferSP dsym_file_data_sp; 211 lldb::offset_t dsym_file_data_offset = 0; 212 dsym_objfile_sp = ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(), dsym_file_data_sp, dsym_file_data_offset); 213 if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) 214 { 215 char dsym_path[PATH_MAX]; 216 if (module_sp->GetSourceMappingList().IsEmpty() && dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) 217 { 218 lldb_private::UUID dsym_uuid; 219 if (dsym_objfile_sp->GetUUID(&dsym_uuid)) 220 { 221 std::string uuid_str = dsym_uuid.GetAsString (); 222 if (!uuid_str.empty()) 223 { 224 char *resources = strstr (dsym_path, "/Contents/Resources/"); 225 if (resources) 226 { 227 char dsym_uuid_plist_path[PATH_MAX]; 228 resources[strlen("/Contents/Resources/")] = '\0'; 229 snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path), "%s%s.plist", dsym_path, uuid_str.c_str()); 230 FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false); 231 if (dsym_uuid_plist_spec.Exists()) 232 { 233 xmlDoc *doc = ::xmlReadFile (dsym_uuid_plist_path, NULL, 0); 234 if (doc) 235 { 236 char DBGBuildSourcePath[PATH_MAX]; 237 char DBGSourcePath[PATH_MAX]; 238 DBGBuildSourcePath[0] = '\0'; 239 DBGSourcePath[0] = '\0'; 240 for (xmlNode *node = doc->children; node; node = node ? node->next : NULL) 241 { 242 if (node->type == XML_ELEMENT_NODE) 243 { 244 if (node->name && strcmp((const char*)node->name, "plist") == 0) 245 { 246 xmlNode *dict_node = node->children; 247 while (dict_node && dict_node->type != XML_ELEMENT_NODE) 248 dict_node = dict_node->next; 249 if (dict_node && dict_node->name && strcmp((const char *)dict_node->name, "dict") == 0) 250 { 251 for (xmlNode *key_node = dict_node->children; key_node; key_node = key_node->next) 252 { 253 if (key_node && key_node->type == XML_ELEMENT_NODE && key_node->name) 254 { 255 if (strcmp((const char *)key_node->name, "key") == 0) 256 { 257 const char *key_name = (const char *)::xmlNodeGetContent(key_node); 258 if (strcmp(key_name, "DBGBuildSourcePath") == 0) 259 { 260 xmlNode *value_node = key_node->next; 261 while (value_node && value_node->type != XML_ELEMENT_NODE) 262 value_node = value_node->next; 263 if (value_node && value_node->name) 264 { 265 if (strcmp((const char *)value_node->name, "string") == 0) 266 { 267 const char *node_content = (const char *)::xmlNodeGetContent(value_node); 268 if (node_content) 269 { 270 strncpy(DBGBuildSourcePath, node_content, sizeof(DBGBuildSourcePath)); 271 } 272 } 273 key_node = value_node; 274 } 275 } 276 else if (strcmp(key_name, "DBGSourcePath") == 0) 277 { 278 xmlNode *value_node = key_node->next; 279 while (value_node && value_node->type != XML_ELEMENT_NODE) 280 value_node = value_node->next; 281 if (value_node && value_node->name) 282 { 283 if (strcmp((const char *)value_node->name, "string") == 0) 284 { 285 const char *node_content = (const char *)::xmlNodeGetContent(value_node); 286 if (node_content) 287 { 288 FileSpec resolved_source_path(node_content, true); 289 resolved_source_path.GetPath(DBGSourcePath, sizeof(DBGSourcePath)); 290 } 291 } 292 key_node = value_node; 293 } 294 } 295 } 296 } 297 } 298 } 299 } 300 } 301 } 302 ::xmlFreeDoc (doc); 303 304 if (DBGBuildSourcePath[0] && DBGSourcePath[0]) 305 { 306 module_sp->GetSourceMappingList().Append (ConstString(DBGBuildSourcePath), ConstString(DBGSourcePath), true); 307 } 308 } 309 } 310 } 311 } 312 } 313 } 314 315 ReplaceDSYMSectionsWithExecutableSections (obj_file, dsym_objfile_sp.get()); 316 symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp); 317 return symbol_vendor; 318 } 319 } 320 321 // Just create our symbol vendor using the current objfile as this is either 322 // an executable with no dSYM (that we could locate), an executable with 323 // a dSYM that has a UUID that doesn't match. 324 symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this()); 325 } 326 } 327 return symbol_vendor; 328 } 329 330 331 332 //------------------------------------------------------------------ 333 // PluginInterface protocol 334 //------------------------------------------------------------------ 335 const char * 336 SymbolVendorMacOSX::GetPluginName() 337 { 338 return "SymbolVendorMacOSX"; 339 } 340 341 const char * 342 SymbolVendorMacOSX::GetShortPluginName() 343 { 344 return GetPluginNameStatic(); 345 } 346 347 uint32_t 348 SymbolVendorMacOSX::GetPluginVersion() 349 { 350 return 1; 351 } 352 353