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