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