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 <string.h>
13 
14 #include "lldb/Core/Module.h"
15 #include "lldb/Core/ModuleSpec.h"
16 #include "lldb/Core/PluginManager.h"
17 #include "lldb/Core/Section.h"
18 #include "lldb/Host/Host.h"
19 #include "lldb/Host/Symbols.h"
20 #include "lldb/Host/XML.h"
21 #include "lldb/Symbol/ObjectFile.h"
22 #include "lldb/Utility/StreamString.h"
23 #include "lldb/Utility/Timer.h"
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 
28 //----------------------------------------------------------------------
29 // SymbolVendorMacOSX constructor
30 //----------------------------------------------------------------------
31 SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp)
32     : SymbolVendor(module_sp) {}
33 
34 //----------------------------------------------------------------------
35 // Destructor
36 //----------------------------------------------------------------------
37 SymbolVendorMacOSX::~SymbolVendorMacOSX() {}
38 
39 static bool UUIDsMatch(Module *module, ObjectFile *ofile,
40                        lldb_private::Stream *feedback_strm) {
41   if (module && ofile) {
42     // Make sure the UUIDs match
43     lldb_private::UUID dsym_uuid;
44 
45     if (!ofile->GetUUID(&dsym_uuid)) {
46       if (feedback_strm) {
47         feedback_strm->PutCString(
48             "warning: failed to get the uuid for object file: '");
49         ofile->GetFileSpec().Dump(feedback_strm);
50         feedback_strm->PutCString("\n");
51       }
52       return false;
53     }
54 
55     if (dsym_uuid == module->GetUUID())
56       return true;
57 
58     // Emit some warning messages since the UUIDs do not match!
59     if (feedback_strm) {
60       feedback_strm->PutCString(
61           "warning: UUID mismatch detected between modules:\n    ");
62       module->GetUUID().Dump(feedback_strm);
63       feedback_strm->PutChar(' ');
64       module->GetFileSpec().Dump(feedback_strm);
65       feedback_strm->PutCString("\n    ");
66       dsym_uuid.Dump(feedback_strm);
67       feedback_strm->PutChar(' ');
68       ofile->GetFileSpec().Dump(feedback_strm);
69       feedback_strm->EOL();
70     }
71   }
72   return false;
73 }
74 
75 void SymbolVendorMacOSX::Initialize() {
76   PluginManager::RegisterPlugin(GetPluginNameStatic(),
77                                 GetPluginDescriptionStatic(), CreateInstance);
78 }
79 
80 void SymbolVendorMacOSX::Terminate() {
81   PluginManager::UnregisterPlugin(CreateInstance);
82 }
83 
84 lldb_private::ConstString SymbolVendorMacOSX::GetPluginNameStatic() {
85   static ConstString g_name("macosx");
86   return g_name;
87 }
88 
89 const char *SymbolVendorMacOSX::GetPluginDescriptionStatic() {
90   return "Symbol vendor for MacOSX that looks for dSYM files that match "
91          "executables.";
92 }
93 
94 //----------------------------------------------------------------------
95 // CreateInstance
96 //
97 // Platforms can register a callback to use when creating symbol
98 // vendors to allow for complex debug information file setups, and to
99 // also allow for finding separate debug information files.
100 //----------------------------------------------------------------------
101 SymbolVendor *
102 SymbolVendorMacOSX::CreateInstance(const lldb::ModuleSP &module_sp,
103                                    lldb_private::Stream *feedback_strm) {
104   if (!module_sp)
105     return NULL;
106 
107   ObjectFile *obj_file = module_sp->GetObjectFile();
108   if (!obj_file)
109     return NULL;
110 
111   static ConstString obj_file_macho("mach-o");
112   ConstString obj_name = obj_file->GetPluginName();
113   if (obj_name != obj_file_macho)
114     return NULL;
115 
116   static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
117   Timer scoped_timer(func_cat,
118                      "SymbolVendorMacOSX::CreateInstance (module = %s)",
119                      module_sp->GetFileSpec().GetPath().c_str());
120   SymbolVendorMacOSX *symbol_vendor = new SymbolVendorMacOSX(module_sp);
121   if (symbol_vendor) {
122     char path[PATH_MAX];
123     path[0] = '\0';
124 
125     // Try and locate the dSYM file on Mac OS X
126     static Timer::Category func_cat2(
127         "SymbolVendorMacOSX::CreateInstance() locate dSYM");
128     Timer scoped_timer2(
129         func_cat2,
130         "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM",
131         module_sp->GetFileSpec().GetPath().c_str());
132 
133     // First check to see if the module has a symbol file in mind already.
134     // If it does, then we MUST use that.
135     FileSpec dsym_fspec(module_sp->GetSymbolFileFileSpec());
136 
137     ObjectFileSP dsym_objfile_sp;
138     if (!dsym_fspec) {
139       // No symbol file was specified in the module, lets try and find
140       // one ourselves.
141       FileSpec file_spec = obj_file->GetFileSpec();
142       if (!file_spec)
143         file_spec = module_sp->GetFileSpec();
144 
145       ModuleSpec module_spec(file_spec, module_sp->GetArchitecture());
146       module_spec.GetUUID() = module_sp->GetUUID();
147       dsym_fspec = Symbols::LocateExecutableSymbolFile(module_spec);
148       if (module_spec.GetSourceMappingList().GetSize())
149         module_sp->GetSourceMappingList().Append(
150             module_spec.GetSourceMappingList(), true);
151     }
152 
153     if (dsym_fspec) {
154       DataBufferSP dsym_file_data_sp;
155       lldb::offset_t dsym_file_data_offset = 0;
156       dsym_objfile_sp = ObjectFile::FindPlugin(
157           module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(),
158           dsym_file_data_sp, dsym_file_data_offset);
159       if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) {
160         // We need a XML parser if we hope to parse a plist...
161         if (XMLDocument::XMLEnabled()) {
162           char dsym_path[PATH_MAX];
163           if (module_sp->GetSourceMappingList().IsEmpty() &&
164               dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) {
165             lldb_private::UUID dsym_uuid;
166             if (dsym_objfile_sp->GetUUID(&dsym_uuid)) {
167               std::string uuid_str = dsym_uuid.GetAsString();
168               if (!uuid_str.empty()) {
169                 char *resources = strstr(dsym_path, "/Contents/Resources/");
170                 if (resources) {
171                   char dsym_uuid_plist_path[PATH_MAX];
172                   resources[strlen("/Contents/Resources/")] = '\0';
173                   snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path),
174                            "%s%s.plist", dsym_path, uuid_str.c_str());
175                   FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false);
176                   if (dsym_uuid_plist_spec.Exists()) {
177                     ApplePropertyList plist(dsym_uuid_plist_path);
178                     if (plist) {
179                       std::string DBGBuildSourcePath;
180                       std::string DBGSourcePath;
181 
182                       plist.GetValueAsString("DBGBuildSourcePath",
183                                              DBGBuildSourcePath);
184                       plist.GetValueAsString("DBGSourcePath", DBGSourcePath);
185                       if (!DBGBuildSourcePath.empty() &&
186                           !DBGSourcePath.empty()) {
187                         if (DBGSourcePath[0] == '~') {
188                           FileSpec resolved_source_path(DBGSourcePath.c_str(),
189                                                         true);
190                           DBGSourcePath = resolved_source_path.GetPath();
191                         }
192                         module_sp->GetSourceMappingList().Append(
193                             ConstString(DBGBuildSourcePath),
194                             ConstString(DBGSourcePath), true);
195                       }
196 
197                       // DBGSourcePathRemapping is a dictionary in the plist
198                       // with
199                       // keys which are DBGBuildSourcePath file paths and
200                       // values which are DBGSourcePath file paths
201 
202                       StructuredData::ObjectSP plist_sp =
203                           plist.GetStructuredData();
204                       if (plist_sp.get() && plist_sp->GetAsDictionary() &&
205                           plist_sp->GetAsDictionary()->HasKey(
206                               "DBGSourcePathRemapping") &&
207                           plist_sp->GetAsDictionary()
208                               ->GetValueForKey("DBGSourcePathRemapping")
209                               ->GetAsDictionary()) {
210 
211                         // In an early version of DBGSourcePathRemapping, the
212                         // DBGSourcePath
213                         // values were incorrect.  If we have a newer style
214                         // DBGSourcePathRemapping, there will be a DBGVersion
215                         // key in the plist with version 2 or higher.
216                         //
217                         // If this is an old style DBGSourcePathRemapping,
218                         // ignore the
219                         // value half of the key-value remappings and use reuse
220                         // the original
221                         // gloal DBGSourcePath string.
222                         bool new_style_source_remapping_dictionary = false;
223                         bool do_truncate_remapping_names = false;
224                         std::string original_DBGSourcePath_value =
225                             DBGSourcePath;
226                         if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) {
227                           std::string version_string =
228                               plist_sp->GetAsDictionary()
229                                   ->GetValueForKey("DBGVersion")
230                                   ->GetStringValue("");
231                           if (!version_string.empty() &&
232                               isdigit(version_string[0])) {
233                             int version_number = atoi(version_string.c_str());
234                             if (version_number > 1) {
235                               new_style_source_remapping_dictionary = true;
236                             }
237                             if (version_number == 2) {
238                                 do_truncate_remapping_names = true;
239                             }
240                           }
241                         }
242 
243                         StructuredData::Dictionary *remappings_dict =
244                             plist_sp->GetAsDictionary()
245                                 ->GetValueForKey("DBGSourcePathRemapping")
246                                 ->GetAsDictionary();
247                         remappings_dict->ForEach(
248                             [&module_sp, new_style_source_remapping_dictionary,
249                              original_DBGSourcePath_value, do_truncate_remapping_names](
250                                 ConstString key,
251                                 StructuredData::Object *object) -> bool {
252                               if (object && object->GetAsString()) {
253 
254                                 // key is DBGBuildSourcePath
255                                 // object is DBGSourcePath
256                                 std::string DBGSourcePath =
257                                     object->GetStringValue();
258                                 if (new_style_source_remapping_dictionary ==
259                                         false &&
260                                     !original_DBGSourcePath_value.empty()) {
261                                   DBGSourcePath = original_DBGSourcePath_value;
262                                 }
263                                 if (DBGSourcePath[0] == '~') {
264                                   FileSpec resolved_source_path(
265                                       DBGSourcePath.c_str(), true);
266                                   DBGSourcePath =
267                                       resolved_source_path.GetPath();
268                                 }
269                                 module_sp->GetSourceMappingList().Append(
270                                     key, ConstString(DBGSourcePath), true);
271                                 // With version 2 of DBGSourcePathRemapping, we can chop off the
272                                 // last two filename parts from the source remapping and get a
273                                 // more general source remapping that still works.  Add this as
274                                 // another option in addition to the full source path remap.
275                                 if (do_truncate_remapping_names) {
276                                   FileSpec build_path(key.AsCString(), false);
277                                   FileSpec source_path(DBGSourcePath.c_str(), false);
278                                   build_path.RemoveLastPathComponent();
279                                   build_path.RemoveLastPathComponent();
280                                   source_path.RemoveLastPathComponent();
281                                   source_path.RemoveLastPathComponent();
282                                   module_sp->GetSourceMappingList().Append(
283                                       ConstString(build_path.GetPath().c_str()),
284                                       ConstString(source_path.GetPath().c_str()), true);
285                                 }
286                               }
287                               return true;
288                             });
289                       }
290                     }
291                   }
292                 }
293               }
294             }
295           }
296         }
297 
298         symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
299         return symbol_vendor;
300       }
301     }
302 
303     // Just create our symbol vendor using the current objfile as this is either
304     // an executable with no dSYM (that we could locate), an executable with
305     // a dSYM that has a UUID that doesn't match.
306     symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this());
307   }
308   return symbol_vendor;
309 }
310 
311 //------------------------------------------------------------------
312 // PluginInterface protocol
313 //------------------------------------------------------------------
314 ConstString SymbolVendorMacOSX::GetPluginName() {
315   return GetPluginNameStatic();
316 }
317 
318 uint32_t SymbolVendorMacOSX::GetPluginVersion() { return 1; }
319