xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- SymbolVendorMacOSX.cpp --------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "SymbolVendorMacOSX.h"
10061da546Spatrick 
11be691f3bSpatrick #include <cstring>
12061da546Spatrick 
13061da546Spatrick #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
14061da546Spatrick #include "lldb/Core/Module.h"
15061da546Spatrick #include "lldb/Core/ModuleSpec.h"
16061da546Spatrick #include "lldb/Core/PluginManager.h"
17061da546Spatrick #include "lldb/Core/Section.h"
18061da546Spatrick #include "lldb/Host/Host.h"
19061da546Spatrick #include "lldb/Host/XML.h"
20061da546Spatrick #include "lldb/Symbol/LocateSymbolFile.h"
21061da546Spatrick #include "lldb/Symbol/ObjectFile.h"
22061da546Spatrick #include "lldb/Target/Target.h"
23061da546Spatrick #include "lldb/Utility/StreamString.h"
24061da546Spatrick #include "lldb/Utility/Timer.h"
25061da546Spatrick 
26061da546Spatrick using namespace lldb;
27061da546Spatrick using namespace lldb_private;
28061da546Spatrick 
LLDB_PLUGIN_DEFINE(SymbolVendorMacOSX)29dda28197Spatrick LLDB_PLUGIN_DEFINE(SymbolVendorMacOSX)
30dda28197Spatrick 
31061da546Spatrick // SymbolVendorMacOSX constructor
32061da546Spatrick SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp)
33061da546Spatrick     : SymbolVendor(module_sp) {}
34061da546Spatrick 
UUIDsMatch(Module * module,ObjectFile * ofile,lldb_private::Stream * feedback_strm)35061da546Spatrick static bool UUIDsMatch(Module *module, ObjectFile *ofile,
36061da546Spatrick                        lldb_private::Stream *feedback_strm) {
37061da546Spatrick   if (module && ofile) {
38061da546Spatrick     // Make sure the UUIDs match
39061da546Spatrick     lldb_private::UUID dsym_uuid = ofile->GetUUID();
40061da546Spatrick     if (!dsym_uuid) {
41061da546Spatrick       if (feedback_strm) {
42061da546Spatrick         feedback_strm->PutCString(
43061da546Spatrick             "warning: failed to get the uuid for object file: '");
44061da546Spatrick         ofile->GetFileSpec().Dump(feedback_strm->AsRawOstream());
45061da546Spatrick         feedback_strm->PutCString("\n");
46061da546Spatrick       }
47061da546Spatrick       return false;
48061da546Spatrick     }
49061da546Spatrick 
50061da546Spatrick     if (dsym_uuid == module->GetUUID())
51061da546Spatrick       return true;
52061da546Spatrick 
53061da546Spatrick     // Emit some warning messages since the UUIDs do not match!
54061da546Spatrick     if (feedback_strm) {
55061da546Spatrick       feedback_strm->PutCString(
56061da546Spatrick           "warning: UUID mismatch detected between modules:\n    ");
57061da546Spatrick       module->GetUUID().Dump(feedback_strm);
58061da546Spatrick       feedback_strm->PutChar(' ');
59061da546Spatrick       module->GetFileSpec().Dump(feedback_strm->AsRawOstream());
60061da546Spatrick       feedback_strm->PutCString("\n    ");
61061da546Spatrick       dsym_uuid.Dump(feedback_strm);
62061da546Spatrick       feedback_strm->PutChar(' ');
63061da546Spatrick       ofile->GetFileSpec().Dump(feedback_strm->AsRawOstream());
64061da546Spatrick       feedback_strm->EOL();
65061da546Spatrick     }
66061da546Spatrick   }
67061da546Spatrick   return false;
68061da546Spatrick }
69061da546Spatrick 
Initialize()70061da546Spatrick void SymbolVendorMacOSX::Initialize() {
71061da546Spatrick   PluginManager::RegisterPlugin(GetPluginNameStatic(),
72061da546Spatrick                                 GetPluginDescriptionStatic(), CreateInstance);
73061da546Spatrick }
74061da546Spatrick 
Terminate()75061da546Spatrick void SymbolVendorMacOSX::Terminate() {
76061da546Spatrick   PluginManager::UnregisterPlugin(CreateInstance);
77061da546Spatrick }
78061da546Spatrick 
GetPluginDescriptionStatic()79*f6aab3d8Srobert llvm::StringRef SymbolVendorMacOSX::GetPluginDescriptionStatic() {
80061da546Spatrick   return "Symbol vendor for MacOSX that looks for dSYM files that match "
81061da546Spatrick          "executables.";
82061da546Spatrick }
83061da546Spatrick 
84061da546Spatrick // CreateInstance
85061da546Spatrick //
86061da546Spatrick // Platforms can register a callback to use when creating symbol vendors to
87061da546Spatrick // allow for complex debug information file setups, and to also allow for
88061da546Spatrick // finding separate debug information files.
89061da546Spatrick SymbolVendor *
CreateInstance(const lldb::ModuleSP & module_sp,lldb_private::Stream * feedback_strm)90061da546Spatrick SymbolVendorMacOSX::CreateInstance(const lldb::ModuleSP &module_sp,
91061da546Spatrick                                    lldb_private::Stream *feedback_strm) {
92061da546Spatrick   if (!module_sp)
93061da546Spatrick     return NULL;
94061da546Spatrick 
95061da546Spatrick   ObjectFile *obj_file =
96061da546Spatrick       llvm::dyn_cast_or_null<ObjectFileMachO>(module_sp->GetObjectFile());
97061da546Spatrick   if (!obj_file)
98061da546Spatrick     return NULL;
99061da546Spatrick 
100061da546Spatrick   static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
101061da546Spatrick   Timer scoped_timer(func_cat,
102061da546Spatrick                      "SymbolVendorMacOSX::CreateInstance (module = %s)",
103061da546Spatrick                      module_sp->GetFileSpec().GetPath().c_str());
104061da546Spatrick   SymbolVendorMacOSX *symbol_vendor = new SymbolVendorMacOSX(module_sp);
105061da546Spatrick   if (symbol_vendor) {
106061da546Spatrick     char path[PATH_MAX];
107061da546Spatrick     path[0] = '\0';
108061da546Spatrick 
109061da546Spatrick     // Try and locate the dSYM file on Mac OS X
110061da546Spatrick     static Timer::Category func_cat2(
111061da546Spatrick         "SymbolVendorMacOSX::CreateInstance() locate dSYM");
112061da546Spatrick     Timer scoped_timer2(
113061da546Spatrick         func_cat2,
114061da546Spatrick         "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM",
115061da546Spatrick         module_sp->GetFileSpec().GetPath().c_str());
116061da546Spatrick 
117061da546Spatrick     // First check to see if the module has a symbol file in mind already. If
118061da546Spatrick     // it does, then we MUST use that.
119061da546Spatrick     FileSpec dsym_fspec(module_sp->GetSymbolFileFileSpec());
120061da546Spatrick 
121061da546Spatrick     ObjectFileSP dsym_objfile_sp;
122061da546Spatrick     if (!dsym_fspec) {
123061da546Spatrick       // No symbol file was specified in the module, lets try and find one
124061da546Spatrick       // ourselves.
125061da546Spatrick       FileSpec file_spec = obj_file->GetFileSpec();
126061da546Spatrick       if (!file_spec)
127061da546Spatrick         file_spec = module_sp->GetFileSpec();
128061da546Spatrick 
129061da546Spatrick       ModuleSpec module_spec(file_spec, module_sp->GetArchitecture());
130061da546Spatrick       module_spec.GetUUID() = module_sp->GetUUID();
131061da546Spatrick       FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
132061da546Spatrick       dsym_fspec =
133061da546Spatrick           Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
134061da546Spatrick       if (module_spec.GetSourceMappingList().GetSize())
135061da546Spatrick         module_sp->GetSourceMappingList().Append(
136061da546Spatrick             module_spec.GetSourceMappingList(), true);
137061da546Spatrick     }
138061da546Spatrick 
139061da546Spatrick     if (dsym_fspec) {
140dda28197Spatrick       // Compute dSYM root.
141dda28197Spatrick       std::string dsym_root = dsym_fspec.GetPath();
142dda28197Spatrick       const size_t pos = dsym_root.find("/Contents/Resources/");
143dda28197Spatrick       dsym_root = pos != std::string::npos ? dsym_root.substr(0, pos) : "";
144dda28197Spatrick 
145061da546Spatrick       DataBufferSP dsym_file_data_sp;
146061da546Spatrick       lldb::offset_t dsym_file_data_offset = 0;
147061da546Spatrick       dsym_objfile_sp =
148061da546Spatrick           ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0,
149061da546Spatrick                                  FileSystem::Instance().GetByteSize(dsym_fspec),
150061da546Spatrick                                  dsym_file_data_sp, dsym_file_data_offset);
151*f6aab3d8Srobert       // Important to save the dSYM FileSpec so we don't call
152*f6aab3d8Srobert       // Symbols::LocateExecutableSymbolFile a second time while trying to
153*f6aab3d8Srobert       // add the symbol ObjectFile to this Module.
154*f6aab3d8Srobert       if (dsym_objfile_sp && !module_sp->GetSymbolFileFileSpec()) {
155*f6aab3d8Srobert         module_sp->SetSymbolFileFileSpec(dsym_fspec);
156*f6aab3d8Srobert       }
157061da546Spatrick       if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) {
158061da546Spatrick         // We need a XML parser if we hope to parse a plist...
159061da546Spatrick         if (XMLDocument::XMLEnabled()) {
160dda28197Spatrick           if (module_sp->GetSourceMappingList().IsEmpty()) {
161061da546Spatrick             lldb_private::UUID dsym_uuid = dsym_objfile_sp->GetUUID();
162061da546Spatrick             if (dsym_uuid) {
163061da546Spatrick               std::string uuid_str = dsym_uuid.GetAsString();
164dda28197Spatrick               if (!uuid_str.empty() && !dsym_root.empty()) {
165061da546Spatrick                 char dsym_uuid_plist_path[PATH_MAX];
166061da546Spatrick                 snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path),
167dda28197Spatrick                          "%s/Contents/Resources/%s.plist", dsym_root.c_str(),
168dda28197Spatrick                          uuid_str.c_str());
169061da546Spatrick                 FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path);
170061da546Spatrick                 if (FileSystem::Instance().Exists(dsym_uuid_plist_spec)) {
171061da546Spatrick                   ApplePropertyList plist(dsym_uuid_plist_path);
172061da546Spatrick                   if (plist) {
173061da546Spatrick                     std::string DBGBuildSourcePath;
174061da546Spatrick                     std::string DBGSourcePath;
175061da546Spatrick 
176061da546Spatrick                     // DBGSourcePathRemapping is a dictionary in the plist
177061da546Spatrick                     // with keys which are DBGBuildSourcePath file paths and
178061da546Spatrick                     // values which are DBGSourcePath file paths
179061da546Spatrick 
180061da546Spatrick                     StructuredData::ObjectSP plist_sp =
181061da546Spatrick                         plist.GetStructuredData();
182061da546Spatrick                     if (plist_sp.get() && plist_sp->GetAsDictionary() &&
183061da546Spatrick                         plist_sp->GetAsDictionary()->HasKey(
184061da546Spatrick                             "DBGSourcePathRemapping") &&
185061da546Spatrick                         plist_sp->GetAsDictionary()
186061da546Spatrick                             ->GetValueForKey("DBGSourcePathRemapping")
187061da546Spatrick                             ->GetAsDictionary()) {
188061da546Spatrick 
189dda28197Spatrick                       // If DBGVersion 1 or DBGVersion missing, ignore
190dda28197Spatrick                       // DBGSourcePathRemapping. If DBGVersion 2, strip last two
191dda28197Spatrick                       // components of path remappings from
192dda28197Spatrick                       //                  entries to fix an issue with a
193dda28197Spatrick                       //                  specific set of DBGSourcePathRemapping
194dda28197Spatrick                       //                  entries that lldb worked with.
195dda28197Spatrick                       // If DBGVersion 3, trust & use the source path remappings
196dda28197Spatrick                       // as-is.
197061da546Spatrick                       //
198061da546Spatrick 
199061da546Spatrick                       bool new_style_source_remapping_dictionary = false;
200061da546Spatrick                       bool do_truncate_remapping_names = false;
201dda28197Spatrick                       std::string original_DBGSourcePath_value = DBGSourcePath;
202061da546Spatrick                       if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) {
203061da546Spatrick                         std::string version_string =
204dda28197Spatrick                             std::string(plist_sp->GetAsDictionary()
205061da546Spatrick                                             ->GetValueForKey("DBGVersion")
206dda28197Spatrick                                             ->GetStringValue(""));
207061da546Spatrick                         if (!version_string.empty() &&
208061da546Spatrick                             isdigit(version_string[0])) {
209061da546Spatrick                           int version_number = atoi(version_string.c_str());
210061da546Spatrick                           if (version_number > 1) {
211061da546Spatrick                             new_style_source_remapping_dictionary = true;
212061da546Spatrick                           }
213061da546Spatrick                           if (version_number == 2) {
214061da546Spatrick                             do_truncate_remapping_names = true;
215061da546Spatrick                           }
216061da546Spatrick                         }
217061da546Spatrick                       }
218061da546Spatrick 
219061da546Spatrick                       StructuredData::Dictionary *remappings_dict =
220061da546Spatrick                           plist_sp->GetAsDictionary()
221061da546Spatrick                               ->GetValueForKey("DBGSourcePathRemapping")
222061da546Spatrick                               ->GetAsDictionary();
223061da546Spatrick                       remappings_dict->ForEach(
224061da546Spatrick                           [&module_sp, new_style_source_remapping_dictionary,
225dda28197Spatrick                            original_DBGSourcePath_value,
226dda28197Spatrick                            do_truncate_remapping_names](
227061da546Spatrick                               ConstString key,
228061da546Spatrick                               StructuredData::Object *object) -> bool {
229061da546Spatrick                             if (object && object->GetAsString()) {
230061da546Spatrick 
231061da546Spatrick                               // key is DBGBuildSourcePath
232061da546Spatrick                               // object is DBGSourcePath
233061da546Spatrick                               std::string DBGSourcePath =
234dda28197Spatrick                                   std::string(object->GetStringValue());
235061da546Spatrick                               if (!new_style_source_remapping_dictionary &&
236061da546Spatrick                                   !original_DBGSourcePath_value.empty()) {
237061da546Spatrick                                 DBGSourcePath = original_DBGSourcePath_value;
238061da546Spatrick                               }
239061da546Spatrick                               module_sp->GetSourceMappingList().Append(
240*f6aab3d8Srobert                                   key.GetStringRef(), DBGSourcePath, true);
241061da546Spatrick                               // With version 2 of DBGSourcePathRemapping, we
242061da546Spatrick                               // can chop off the last two filename parts
243061da546Spatrick                               // from the source remapping and get a more
244061da546Spatrick                               // general source remapping that still works.
245061da546Spatrick                               // Add this as another option in addition to
246061da546Spatrick                               // the full source path remap.
247061da546Spatrick                               if (do_truncate_remapping_names) {
248061da546Spatrick                                 FileSpec build_path(key.AsCString());
249061da546Spatrick                                 FileSpec source_path(DBGSourcePath.c_str());
250061da546Spatrick                                 build_path.RemoveLastPathComponent();
251061da546Spatrick                                 build_path.RemoveLastPathComponent();
252061da546Spatrick                                 source_path.RemoveLastPathComponent();
253061da546Spatrick                                 source_path.RemoveLastPathComponent();
254061da546Spatrick                                 module_sp->GetSourceMappingList().Append(
255*f6aab3d8Srobert                                     build_path.GetPath(), source_path.GetPath(),
256dda28197Spatrick                                     true);
257061da546Spatrick                               }
258061da546Spatrick                             }
259061da546Spatrick                             return true;
260061da546Spatrick                           });
261061da546Spatrick                     }
262061da546Spatrick 
263061da546Spatrick                     // If we have a DBGBuildSourcePath + DBGSourcePath pair,
264061da546Spatrick                     // append those to the source path remappings.
265061da546Spatrick 
266061da546Spatrick                     plist.GetValueAsString("DBGBuildSourcePath",
267061da546Spatrick                                            DBGBuildSourcePath);
268061da546Spatrick                     plist.GetValueAsString("DBGSourcePath", DBGSourcePath);
269dda28197Spatrick                     if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
270061da546Spatrick                       module_sp->GetSourceMappingList().Append(
271*f6aab3d8Srobert                           DBGBuildSourcePath, DBGSourcePath, true);
272061da546Spatrick                     }
273061da546Spatrick                   }
274061da546Spatrick                 }
275061da546Spatrick               }
276061da546Spatrick             }
277061da546Spatrick           }
278061da546Spatrick         }
279061da546Spatrick 
280061da546Spatrick         symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
281061da546Spatrick         return symbol_vendor;
282061da546Spatrick       }
283061da546Spatrick     }
284061da546Spatrick 
285061da546Spatrick     // Just create our symbol vendor using the current objfile as this is
286061da546Spatrick     // either an executable with no dSYM (that we could locate), an executable
287061da546Spatrick     // with a dSYM that has a UUID that doesn't match.
288061da546Spatrick     symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this());
289061da546Spatrick   }
290061da546Spatrick   return symbol_vendor;
291061da546Spatrick }
292