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