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 vendors to
98 // allow for complex debug information file setups, and to also allow for
99 // 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. If
134     // 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 one
140       // 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 =
157           ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0,
158                                  FileSystem::Instance().GetByteSize(dsym_fspec),
159                                  dsym_file_data_sp, dsym_file_data_offset);
160       if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) {
161         // We need a XML parser if we hope to parse a plist...
162         if (XMLDocument::XMLEnabled()) {
163           char dsym_path[PATH_MAX];
164           if (module_sp->GetSourceMappingList().IsEmpty() &&
165               dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) {
166             lldb_private::UUID dsym_uuid;
167             if (dsym_objfile_sp->GetUUID(&dsym_uuid)) {
168               std::string uuid_str = dsym_uuid.GetAsString();
169               if (!uuid_str.empty()) {
170                 char *resources = strstr(dsym_path, "/Contents/Resources/");
171                 if (resources) {
172                   char dsym_uuid_plist_path[PATH_MAX];
173                   resources[strlen("/Contents/Resources/")] = '\0';
174                   snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path),
175                            "%s%s.plist", dsym_path, uuid_str.c_str());
176                   FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path);
177                   if (FileSystem::Instance().Exists(dsym_uuid_plist_spec)) {
178                     ApplePropertyList plist(dsym_uuid_plist_path);
179                     if (plist) {
180                       std::string DBGBuildSourcePath;
181                       std::string DBGSourcePath;
182 
183                       // DBGSourcePathRemapping is a dictionary in the plist
184                       // with keys which are DBGBuildSourcePath file paths and
185                       // values which are DBGSourcePath file paths
186 
187                       StructuredData::ObjectSP plist_sp =
188                           plist.GetStructuredData();
189                       if (plist_sp.get() && plist_sp->GetAsDictionary() &&
190                           plist_sp->GetAsDictionary()->HasKey(
191                               "DBGSourcePathRemapping") &&
192                           plist_sp->GetAsDictionary()
193                               ->GetValueForKey("DBGSourcePathRemapping")
194                               ->GetAsDictionary()) {
195 
196                         // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
197                         // If DBGVersion 2, strip last two components of path remappings from
198                         //                  entries to fix an issue with a specific set of
199                         //                  DBGSourcePathRemapping entries that lldb worked
200                         //                  with.
201                         // If DBGVersion 3, trust & use the source path remappings as-is.
202                         //
203 
204                         bool new_style_source_remapping_dictionary = false;
205                         bool do_truncate_remapping_names = false;
206                         std::string original_DBGSourcePath_value =
207                             DBGSourcePath;
208                         if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) {
209                           std::string version_string =
210                               plist_sp->GetAsDictionary()
211                                   ->GetValueForKey("DBGVersion")
212                                   ->GetStringValue("");
213                           if (!version_string.empty() &&
214                               isdigit(version_string[0])) {
215                             int version_number = atoi(version_string.c_str());
216                             if (version_number > 1) {
217                               new_style_source_remapping_dictionary = true;
218                             }
219                             if (version_number == 2) {
220                                 do_truncate_remapping_names = true;
221                             }
222                           }
223                         }
224 
225                         StructuredData::Dictionary *remappings_dict =
226                             plist_sp->GetAsDictionary()
227                                 ->GetValueForKey("DBGSourcePathRemapping")
228                                 ->GetAsDictionary();
229                         remappings_dict->ForEach(
230                             [&module_sp, new_style_source_remapping_dictionary,
231                              original_DBGSourcePath_value, do_truncate_remapping_names](
232                                 ConstString key,
233                                 StructuredData::Object *object) -> bool {
234                               if (object && object->GetAsString()) {
235 
236                                 // key is DBGBuildSourcePath
237                                 // object is DBGSourcePath
238                                 std::string DBGSourcePath =
239                                     object->GetStringValue();
240                                 if (!new_style_source_remapping_dictionary &&
241                                     !original_DBGSourcePath_value.empty()) {
242                                   DBGSourcePath = original_DBGSourcePath_value;
243                                 }
244                                 if (DBGSourcePath[0] == '~') {
245                                   FileSpec resolved_source_path(
246                                       DBGSourcePath.c_str());
247                                   FileSystem::Instance().Resolve(
248                                       resolved_source_path);
249                                   DBGSourcePath =
250                                       resolved_source_path.GetPath();
251                                 }
252                                 module_sp->GetSourceMappingList().Append(
253                                     key, ConstString(DBGSourcePath), true);
254                                 // With version 2 of DBGSourcePathRemapping, we
255                                 // can chop off the last two filename parts
256                                 // from the source remapping and get a more
257                                 // general source remapping that still works.
258                                 // Add this as another option in addition to
259                                 // the full source path remap.
260                                 if (do_truncate_remapping_names) {
261                                   FileSpec build_path(key.AsCString());
262                                   FileSpec source_path(DBGSourcePath.c_str());
263                                   build_path.RemoveLastPathComponent();
264                                   build_path.RemoveLastPathComponent();
265                                   source_path.RemoveLastPathComponent();
266                                   source_path.RemoveLastPathComponent();
267                                   module_sp->GetSourceMappingList().Append(
268                                       ConstString(build_path.GetPath().c_str()),
269                                       ConstString(source_path.GetPath().c_str()), true);
270                                 }
271                               }
272                               return true;
273                             });
274                       }
275 
276                       // If we have a DBGBuildSourcePath + DBGSourcePath pair,
277                       // append those to the source path remappings.
278 
279                       plist.GetValueAsString("DBGBuildSourcePath",
280                                              DBGBuildSourcePath);
281                       plist.GetValueAsString("DBGSourcePath", DBGSourcePath);
282                       if (!DBGBuildSourcePath.empty() &&
283                           !DBGSourcePath.empty()) {
284                         if (DBGSourcePath[0] == '~') {
285                           FileSpec resolved_source_path(DBGSourcePath.c_str());
286                           FileSystem::Instance().Resolve(resolved_source_path);
287                           DBGSourcePath = resolved_source_path.GetPath();
288                         }
289                         module_sp->GetSourceMappingList().Append(
290                             ConstString(DBGBuildSourcePath),
291                             ConstString(DBGSourcePath), true);
292                       }
293                     }
294                   }
295                 }
296               }
297             }
298           }
299         }
300 
301         symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
302         return symbol_vendor;
303       }
304     }
305 
306     // Just create our symbol vendor using the current objfile as this is
307     // either an executable with no dSYM (that we could locate), an executable
308     // with a dSYM that has a UUID that doesn't match.
309     symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this());
310   }
311   return symbol_vendor;
312 }
313 
314 //------------------------------------------------------------------
315 // PluginInterface protocol
316 //------------------------------------------------------------------
317 ConstString SymbolVendorMacOSX::GetPluginName() {
318   return GetPluginNameStatic();
319 }
320 
321 uint32_t SymbolVendorMacOSX::GetPluginVersion() { return 1; }
322