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
28using namespace lldb;
29using namespace lldb_private;
30
31//----------------------------------------------------------------------
32// SymbolVendorMacOSX constructor
33//----------------------------------------------------------------------
34SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp) :
35    SymbolVendor (module_sp)
36{
37}
38
39//----------------------------------------------------------------------
40// Destructor
41//----------------------------------------------------------------------
42SymbolVendorMacOSX::~SymbolVendorMacOSX()
43{
44}
45
46
47static bool
48UUIDsMatch(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
86void
87SymbolVendorMacOSX::Initialize()
88{
89    PluginManager::RegisterPlugin (GetPluginNameStatic(),
90                                   GetPluginDescriptionStatic(),
91                                   CreateInstance);
92}
93
94void
95SymbolVendorMacOSX::Terminate()
96{
97    PluginManager::UnregisterPlugin (CreateInstance);
98}
99
100
101lldb_private::ConstString
102SymbolVendorMacOSX::GetPluginNameStatic()
103{
104    static ConstString g_name("macosx");
105    return g_name;
106}
107
108const char *
109SymbolVendorMacOSX::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//----------------------------------------------------------------------
123SymbolVendor*
124SymbolVendorMacOSX::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//------------------------------------------------------------------
301ConstString
302SymbolVendorMacOSX::GetPluginName()
303{
304    return GetPluginNameStatic();
305}
306
307uint32_t
308SymbolVendorMacOSX::GetPluginVersion()
309{
310    return 1;
311}
312
313