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 //----------------------------------------------------------------------
SymbolVendorMacOSX(const lldb::ModuleSP & module_sp)34 SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp) :
35     SymbolVendor (module_sp)
36 {
37 }
38 
39 //----------------------------------------------------------------------
40 // Destructor
41 //----------------------------------------------------------------------
~SymbolVendorMacOSX()42 SymbolVendorMacOSX::~SymbolVendorMacOSX()
43 {
44 }
45 
46 
47 static bool
UUIDsMatch(Module * module,ObjectFile * ofile,lldb_private::Stream * feedback_strm)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 void
Initialize()87 SymbolVendorMacOSX::Initialize()
88 {
89     PluginManager::RegisterPlugin (GetPluginNameStatic(),
90                                    GetPluginDescriptionStatic(),
91                                    CreateInstance);
92 }
93 
94 void
Terminate()95 SymbolVendorMacOSX::Terminate()
96 {
97     PluginManager::UnregisterPlugin (CreateInstance);
98 }
99 
100 
101 lldb_private::ConstString
GetPluginNameStatic()102 SymbolVendorMacOSX::GetPluginNameStatic()
103 {
104     static ConstString g_name("macosx");
105     return g_name;
106 }
107 
108 const char *
GetPluginDescriptionStatic()109 SymbolVendorMacOSX::GetPluginDescriptionStatic()
110 {
111     return "Symbol vendor for MacOSX that looks for dSYM files that match executables.";
112 }
113 
114 
115 
116 //----------------------------------------------------------------------
117 // CreateInstance
118 //
119 // Platforms can register a callback to use when creating symbol
120 // vendors to allow for complex debug information file setups, and to
121 // also allow for finding separate debug information files.
122 //----------------------------------------------------------------------
123 SymbolVendor*
CreateInstance(const lldb::ModuleSP & module_sp,lldb_private::Stream * feedback_strm)124 SymbolVendorMacOSX::CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm)
125 {
126     if (!module_sp)
127         return NULL;
128 
129     ObjectFile * obj_file = module_sp->GetObjectFile();
130     if (!obj_file)
131         return NULL;
132 
133     static ConstString obj_file_macho("mach-o");
134     ConstString obj_name = obj_file->GetPluginName();
135     if (obj_name != obj_file_macho)
136         return NULL;
137 
138     Timer scoped_timer (__PRETTY_FUNCTION__,
139                         "SymbolVendorMacOSX::CreateInstance (module = %s)",
140                         module_sp->GetFileSpec().GetPath().c_str());
141     SymbolVendorMacOSX* symbol_vendor = new SymbolVendorMacOSX(module_sp);
142     if (symbol_vendor)
143     {
144         char path[PATH_MAX];
145         path[0] = '\0';
146 
147         // Try and locate the dSYM file on Mac OS X
148         Timer scoped_timer2 ("SymbolVendorMacOSX::CreateInstance () locate dSYM",
149                              "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM",
150                              module_sp->GetFileSpec().GetPath().c_str());
151 
152         // First check to see if the module has a symbol file in mind already.
153         // If it does, then we MUST use that.
154         FileSpec dsym_fspec (module_sp->GetSymbolFileFileSpec());
155 
156         ObjectFileSP dsym_objfile_sp;
157         if (!dsym_fspec)
158         {
159             // No symbol file was specified in the module, lets try and find
160             // one ourselves.
161             FileSpec file_spec = obj_file->GetFileSpec();
162             if (!file_spec)
163                 file_spec = module_sp->GetFileSpec();
164 
165             ModuleSpec module_spec(file_spec, module_sp->GetArchitecture());
166             module_spec.GetUUID() = module_sp->GetUUID();
167             dsym_fspec = Symbols::LocateExecutableSymbolFile (module_spec);
168             if (module_spec.GetSourceMappingList().GetSize())
169                 module_sp->GetSourceMappingList().Append (module_spec.GetSourceMappingList (), true);
170         }
171 
172         if (dsym_fspec)
173         {
174             DataBufferSP dsym_file_data_sp;
175             lldb::offset_t dsym_file_data_offset = 0;
176             dsym_objfile_sp = ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(), dsym_file_data_sp, dsym_file_data_offset);
177             if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm))
178             {
179                 char dsym_path[PATH_MAX];
180                 if (module_sp->GetSourceMappingList().IsEmpty() && dsym_fspec.GetPath(dsym_path, sizeof(dsym_path)))
181                 {
182                     lldb_private::UUID dsym_uuid;
183                     if (dsym_objfile_sp->GetUUID(&dsym_uuid))
184                     {
185                         std::string uuid_str = dsym_uuid.GetAsString ();
186                         if (!uuid_str.empty())
187                         {
188                             char *resources = strstr (dsym_path, "/Contents/Resources/");
189                             if (resources)
190                             {
191                                 char dsym_uuid_plist_path[PATH_MAX];
192                                 resources[strlen("/Contents/Resources/")] = '\0';
193                                 snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path), "%s%s.plist", dsym_path, uuid_str.c_str());
194                                 FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false);
195                                 if (dsym_uuid_plist_spec.Exists())
196                                 {
197                                     xmlDoc *doc = ::xmlReadFile (dsym_uuid_plist_path, NULL, 0);
198                                     if (doc)
199                                     {
200                                         char DBGBuildSourcePath[PATH_MAX];
201                                         char DBGSourcePath[PATH_MAX];
202                                         DBGBuildSourcePath[0] = '\0';
203                                         DBGSourcePath[0] = '\0';
204                                         for (xmlNode *node = doc->children; node; node = node ? node->next : NULL)
205                                         {
206                                             if (node->type == XML_ELEMENT_NODE)
207                                             {
208                                                 if (node->name && strcmp((const char*)node->name, "plist") == 0)
209                                                 {
210                                                     xmlNode *dict_node = node->children;
211                                                     while (dict_node && dict_node->type != XML_ELEMENT_NODE)
212                                                         dict_node = dict_node->next;
213                                                     if (dict_node && dict_node->name && strcmp((const char *)dict_node->name, "dict") == 0)
214                                                     {
215                                                         for (xmlNode *key_node = dict_node->children; key_node; key_node = key_node->next)
216                                                         {
217                                                             if (key_node && key_node->type == XML_ELEMENT_NODE && key_node->name)
218                                                             {
219                                                                 if (strcmp((const char *)key_node->name, "key") == 0)
220                                                                 {
221                                                                     const char *key_name = (const char *)::xmlNodeGetContent(key_node);
222                                                                     if (strcmp(key_name, "DBGBuildSourcePath") == 0)
223                                                                     {
224                                                                         xmlNode *value_node = key_node->next;
225                                                                         while (value_node && value_node->type != XML_ELEMENT_NODE)
226                                                                             value_node = value_node->next;
227                                                                         if (value_node && value_node->name)
228                                                                         {
229                                                                             if (strcmp((const char *)value_node->name, "string") == 0)
230                                                                             {
231                                                                                 const char *node_content = (const char *)::xmlNodeGetContent(value_node);
232                                                                                 if (node_content)
233                                                                                 {
234                                                                                     strncpy(DBGBuildSourcePath, node_content, sizeof(DBGBuildSourcePath));
235                                                                                     xmlFree((void *) node_content);
236                                                                                 }
237                                                                             }
238                                                                             key_node = value_node;
239                                                                         }
240                                                                     }
241                                                                     else if (strcmp(key_name, "DBGSourcePath") == 0)
242                                                                     {
243                                                                         xmlNode *value_node = key_node->next;
244                                                                         while (value_node && value_node->type != XML_ELEMENT_NODE)
245                                                                             value_node = value_node->next;
246                                                                         if (value_node && value_node->name)
247                                                                         {
248                                                                             if (strcmp((const char *)value_node->name, "string") == 0)
249                                                                             {
250                                                                                 const char *node_content = (const char *)::xmlNodeGetContent(value_node);
251                                                                                 if (node_content)
252                                                                                 {
253                                                                                     FileSpec resolved_source_path(node_content, true);
254                                                                                     resolved_source_path.GetPath(DBGSourcePath, sizeof(DBGSourcePath));
255                                                                                     xmlFree ((void *) node_content);
256                                                                                 }
257                                                                             }
258                                                                             key_node = value_node;
259                                                                         }
260                                                                     }
261                                                                     if (key_name != NULL)
262                                                                         xmlFree((void *) key_name);
263                                                                 }
264                                                             }
265                                                         }
266                                                     }
267                                                 }
268                                             }
269                                         }
270                                         ::xmlFreeDoc (doc);
271 
272                                         if (DBGBuildSourcePath[0] && DBGSourcePath[0])
273                                         {
274                                             module_sp->GetSourceMappingList().Append (ConstString(DBGBuildSourcePath), ConstString(DBGSourcePath), true);
275                                         }
276                                     }
277                                 }
278                             }
279                         }
280                     }
281                 }
282 
283                 symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
284                 return symbol_vendor;
285             }
286         }
287 
288         // Just create our symbol vendor using the current objfile as this is either
289         // an executable with no dSYM (that we could locate), an executable with
290         // a dSYM that has a UUID that doesn't match.
291         symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this());
292     }
293     return symbol_vendor;
294 }
295 
296 
297 
298 //------------------------------------------------------------------
299 // PluginInterface protocol
300 //------------------------------------------------------------------
301 ConstString
GetPluginName()302 SymbolVendorMacOSX::GetPluginName()
303 {
304     return GetPluginNameStatic();
305 }
306 
307 uint32_t
GetPluginVersion()308 SymbolVendorMacOSX::GetPluginVersion()
309 {
310     return 1;
311 }
312 
313