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/Core/StreamString.h"
19 #include "lldb/Core/Timer.h"
20 #include "lldb/Host/Host.h"
21 #include "lldb/Host/Symbols.h"
22 #include "lldb/Host/XML.h"
23 #include "lldb/Symbol/ObjectFile.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   Timer scoped_timer(LLVM_PRETTY_FUNCTION,
117                      "SymbolVendorMacOSX::CreateInstance (module = %s)",
118                      module_sp->GetFileSpec().GetPath().c_str());
119   SymbolVendorMacOSX *symbol_vendor = new SymbolVendorMacOSX(module_sp);
120   if (symbol_vendor) {
121     char path[PATH_MAX];
122     path[0] = '\0';
123 
124     // Try and locate the dSYM file on Mac OS X
125     Timer scoped_timer2(
126         "SymbolVendorMacOSX::CreateInstance () locate dSYM",
127         "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM",
128         module_sp->GetFileSpec().GetPath().c_str());
129 
130     // First check to see if the module has a symbol file in mind already.
131     // If it does, then we MUST use that.
132     FileSpec dsym_fspec(module_sp->GetSymbolFileFileSpec());
133 
134     ObjectFileSP dsym_objfile_sp;
135     if (!dsym_fspec) {
136       // No symbol file was specified in the module, lets try and find
137       // one ourselves.
138       FileSpec file_spec = obj_file->GetFileSpec();
139       if (!file_spec)
140         file_spec = module_sp->GetFileSpec();
141 
142       ModuleSpec module_spec(file_spec, module_sp->GetArchitecture());
143       module_spec.GetUUID() = module_sp->GetUUID();
144       dsym_fspec = Symbols::LocateExecutableSymbolFile(module_spec);
145       if (module_spec.GetSourceMappingList().GetSize())
146         module_sp->GetSourceMappingList().Append(
147             module_spec.GetSourceMappingList(), true);
148     }
149 
150     if (dsym_fspec) {
151       DataBufferSP dsym_file_data_sp;
152       lldb::offset_t dsym_file_data_offset = 0;
153       dsym_objfile_sp = ObjectFile::FindPlugin(
154           module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(),
155           dsym_file_data_sp, dsym_file_data_offset);
156       if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) {
157         // We need a XML parser if we hope to parse a plist...
158         if (XMLDocument::XMLEnabled()) {
159           char dsym_path[PATH_MAX];
160           if (module_sp->GetSourceMappingList().IsEmpty() &&
161               dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) {
162             lldb_private::UUID dsym_uuid;
163             if (dsym_objfile_sp->GetUUID(&dsym_uuid)) {
164               std::string uuid_str = dsym_uuid.GetAsString();
165               if (!uuid_str.empty()) {
166                 char *resources = strstr(dsym_path, "/Contents/Resources/");
167                 if (resources) {
168                   char dsym_uuid_plist_path[PATH_MAX];
169                   resources[strlen("/Contents/Resources/")] = '\0';
170                   snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path),
171                            "%s%s.plist", dsym_path, uuid_str.c_str());
172                   FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false);
173                   if (dsym_uuid_plist_spec.Exists()) {
174                     ApplePropertyList plist(dsym_uuid_plist_path);
175                     if (plist) {
176                       std::string DBGBuildSourcePath;
177                       std::string DBGSourcePath;
178 
179                       plist.GetValueAsString("DBGBuildSourcePath",
180                                              DBGBuildSourcePath);
181                       plist.GetValueAsString("DBGSourcePath", DBGSourcePath);
182                       if (!DBGBuildSourcePath.empty() &&
183                           !DBGSourcePath.empty()) {
184                         if (DBGSourcePath[0] == '~') {
185                           FileSpec resolved_source_path(DBGSourcePath.c_str(),
186                                                         true);
187                           DBGSourcePath = resolved_source_path.GetPath();
188                         }
189                         module_sp->GetSourceMappingList().Append(
190                             ConstString(DBGBuildSourcePath),
191                             ConstString(DBGSourcePath), true);
192                       }
193 
194                       // DBGSourcePathRemapping is a dictionary in the plist
195                       // with
196                       // keys which are DBGBuildSourcePath file paths and
197                       // values which are DBGSourcePath file paths
198 
199                       StructuredData::ObjectSP plist_sp =
200                           plist.GetStructuredData();
201                       if (plist_sp.get() && plist_sp->GetAsDictionary() &&
202                           plist_sp->GetAsDictionary()->HasKey(
203                               "DBGSourcePathRemapping") &&
204                           plist_sp->GetAsDictionary()
205                               ->GetValueForKey("DBGSourcePathRemapping")
206                               ->GetAsDictionary()) {
207 
208                         // In an early version of DBGSourcePathRemapping, the
209                         // DBGSourcePath
210                         // values were incorrect.  If we have a newer style
211                         // DBGSourcePathRemapping, there will be a DBGVersion
212                         // key in the plist with version 2 or higher.
213                         //
214                         // If this is an old style DBGSourcePathRemapping,
215                         // ignore the
216                         // value half of the key-value remappings and use reuse
217                         // the original
218                         // gloal DBGSourcePath string.
219                         bool new_style_source_remapping_dictionary = false;
220                         std::string original_DBGSourcePath_value =
221                             DBGSourcePath;
222                         if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) {
223                           std::string version_string =
224                               plist_sp->GetAsDictionary()
225                                   ->GetValueForKey("DBGVersion")
226                                   ->GetStringValue("");
227                           if (!version_string.empty() &&
228                               isdigit(version_string[0])) {
229                             int version_number = atoi(version_string.c_str());
230                             if (version_number > 1) {
231                               new_style_source_remapping_dictionary = true;
232                             }
233                           }
234                         }
235 
236                         StructuredData::Dictionary *remappings_dict =
237                             plist_sp->GetAsDictionary()
238                                 ->GetValueForKey("DBGSourcePathRemapping")
239                                 ->GetAsDictionary();
240                         remappings_dict->ForEach(
241                             [&module_sp, new_style_source_remapping_dictionary,
242                              original_DBGSourcePath_value](
243                                 ConstString key,
244                                 StructuredData::Object *object) -> bool {
245                               if (object && object->GetAsString()) {
246 
247                                 // key is DBGBuildSourcePath
248                                 // object is DBGSourcePath
249                                 std::string DBGSourcePath =
250                                     object->GetStringValue();
251                                 if (new_style_source_remapping_dictionary ==
252                                         false &&
253                                     !original_DBGSourcePath_value.empty()) {
254                                   DBGSourcePath = original_DBGSourcePath_value;
255                                 }
256                                 if (DBGSourcePath[0] == '~') {
257                                   FileSpec resolved_source_path(
258                                       DBGSourcePath.c_str(), true);
259                                   DBGSourcePath =
260                                       resolved_source_path.GetPath();
261                                 }
262                                 module_sp->GetSourceMappingList().Append(
263                                     key, ConstString(DBGSourcePath), true);
264                               }
265                               return true;
266                             });
267                       }
268                     }
269                   }
270                 }
271               }
272             }
273           }
274         }
275 
276         symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
277         return symbol_vendor;
278       }
279     }
280 
281     // Just create our symbol vendor using the current objfile as this is either
282     // an executable with no dSYM (that we could locate), an executable with
283     // a dSYM that has a UUID that doesn't match.
284     symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this());
285   }
286   return symbol_vendor;
287 }
288 
289 //------------------------------------------------------------------
290 // PluginInterface protocol
291 //------------------------------------------------------------------
292 ConstString SymbolVendorMacOSX::GetPluginName() {
293   return GetPluginNameStatic();
294 }
295 
296 uint32_t SymbolVendorMacOSX::GetPluginVersion() { return 1; }
297