298 lines · cpp
1//===-- SymbolVendorMacOSX.cpp --------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "SymbolVendorMacOSX.h"10 11#include <cstring>12 13#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"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/XML.h"20#include "lldb/Symbol/ObjectFile.h"21#include "lldb/Target/Target.h"22#include "lldb/Utility/StreamString.h"23#include "lldb/Utility/Timer.h"24 25using namespace lldb;26using namespace lldb_private;27 28LLDB_PLUGIN_DEFINE(SymbolVendorMacOSX)29 30// SymbolVendorMacOSX constructor31SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp)32 : SymbolVendor(module_sp) {}33 34static bool UUIDsMatch(Module *module, ObjectFile *ofile,35 lldb_private::Stream *feedback_strm) {36 if (module && ofile) {37 // Make sure the UUIDs match38 lldb_private::UUID dsym_uuid = ofile->GetUUID();39 if (!dsym_uuid) {40 if (feedback_strm) {41 feedback_strm->PutCString(42 "warning: failed to get the uuid for object file: '");43 ofile->GetFileSpec().Dump(feedback_strm->AsRawOstream());44 feedback_strm->PutCString("\n");45 }46 return false;47 }48 49 if (dsym_uuid == module->GetUUID())50 return true;51 52 // Emit some warning messages since the UUIDs do not match!53 if (feedback_strm) {54 feedback_strm->PutCString(55 "warning: UUID mismatch detected between modules:\n ");56 module->GetUUID().Dump(*feedback_strm);57 feedback_strm->PutChar(' ');58 module->GetFileSpec().Dump(feedback_strm->AsRawOstream());59 feedback_strm->PutCString("\n ");60 dsym_uuid.Dump(*feedback_strm);61 feedback_strm->PutChar(' ');62 ofile->GetFileSpec().Dump(feedback_strm->AsRawOstream());63 feedback_strm->EOL();64 }65 }66 return false;67}68 69void SymbolVendorMacOSX::Initialize() {70 PluginManager::RegisterPlugin(GetPluginNameStatic(),71 GetPluginDescriptionStatic(), CreateInstance);72}73 74void SymbolVendorMacOSX::Terminate() {75 PluginManager::UnregisterPlugin(CreateInstance);76}77 78llvm::StringRef SymbolVendorMacOSX::GetPluginDescriptionStatic() {79 return "Symbol vendor for MacOSX that looks for dSYM files that match "80 "executables.";81}82 83// CreateInstance84//85// Platforms can register a callback to use when creating symbol vendors to86// allow for complex debug information file setups, and to also allow for87// finding separate debug information files.88SymbolVendor *89SymbolVendorMacOSX::CreateInstance(const lldb::ModuleSP &module_sp,90 lldb_private::Stream *feedback_strm) {91 if (!module_sp)92 return NULL;93 94 ObjectFile *obj_file =95 llvm::dyn_cast_or_null<ObjectFileMachO>(module_sp->GetObjectFile());96 if (!obj_file)97 return NULL;98 99 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);100 Timer scoped_timer(func_cat,101 "SymbolVendorMacOSX::CreateInstance (module = %s)",102 module_sp->GetFileSpec().GetPath().c_str());103 SymbolVendorMacOSX *symbol_vendor = new SymbolVendorMacOSX(module_sp);104 if (symbol_vendor) {105 char path[PATH_MAX];106 path[0] = '\0';107 108 // Try and locate the dSYM file on Mac OS X109 static Timer::Category func_cat2(110 "SymbolVendorMacOSX::CreateInstance() locate dSYM");111 Timer scoped_timer2(112 func_cat2,113 "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM",114 module_sp->GetFileSpec().GetPath().c_str());115 116 // First check to see if the module has a symbol file in mind already. If117 // it does, then we MUST use that.118 FileSpec dsym_fspec(module_sp->GetSymbolFileFileSpec());119 120 ObjectFileSP dsym_objfile_sp;121 // On Darwin, we store the debug information either in object files,122 // using the debug map to tie them to the executable, or in a dSYM. We123 // pass through this routine both for binaries and for .o files, but in the124 // latter case there will never be an external debug file. So we shouldn't125 // do all the stats needed to find it.126 if (!dsym_fspec && module_sp->GetObjectFile()->CalculateType() !=127 ObjectFile::eTypeObjectFile) {128 // No symbol file was specified in the module, lets try and find one129 // ourselves.130 FileSpec file_spec = obj_file->GetFileSpec();131 if (!file_spec)132 file_spec = module_sp->GetFileSpec();133 134 ModuleSpec module_spec(file_spec, module_sp->GetArchitecture());135 module_spec.GetUUID() = module_sp->GetUUID();136 FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();137 138 dsym_fspec = PluginManager::LocateExecutableSymbolFile(139 module_spec, search_paths, module_sp->GetSymbolLocatorStatistics());140 if (module_spec.GetSourceMappingList().GetSize())141 module_sp->GetSourceMappingList().Append(142 module_spec.GetSourceMappingList(), true);143 }144 145 if (dsym_fspec) {146 // Compute dSYM root.147 std::string dsym_root = dsym_fspec.GetPath();148 const size_t pos = dsym_root.find("/Contents/Resources/");149 dsym_root = pos != std::string::npos ? dsym_root.substr(0, pos) : "";150 151 DataBufferSP dsym_file_data_sp;152 lldb::offset_t dsym_file_data_offset = 0;153 dsym_objfile_sp =154 ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0,155 FileSystem::Instance().GetByteSize(dsym_fspec),156 dsym_file_data_sp, dsym_file_data_offset);157 // Important to save the dSYM FileSpec so we don't call158 // PluginManager::LocateExecutableSymbolFile a second time while trying to159 // add the symbol ObjectFile to this Module.160 if (dsym_objfile_sp && !module_sp->GetSymbolFileFileSpec()) {161 module_sp->SetSymbolFileFileSpec(dsym_fspec);162 }163 if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) {164 // We need a XML parser if we hope to parse a plist...165 if (XMLDocument::XMLEnabled()) {166 if (module_sp->GetSourceMappingList().IsEmpty()) {167 lldb_private::UUID dsym_uuid = dsym_objfile_sp->GetUUID();168 if (dsym_uuid) {169 std::string uuid_str = dsym_uuid.GetAsString();170 if (!uuid_str.empty() && !dsym_root.empty()) {171 char dsym_uuid_plist_path[PATH_MAX];172 snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path),173 "%s/Contents/Resources/%s.plist", dsym_root.c_str(),174 uuid_str.c_str());175 FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path);176 if (FileSystem::Instance().Exists(dsym_uuid_plist_spec)) {177 ApplePropertyList plist(dsym_uuid_plist_path);178 if (plist) {179 std::string DBGBuildSourcePath;180 std::string DBGSourcePath;181 182 // DBGSourcePathRemapping is a dictionary in the plist183 // with keys which are DBGBuildSourcePath file paths and184 // values which are DBGSourcePath file paths185 186 StructuredData::ObjectSP plist_sp =187 plist.GetStructuredData();188 if (plist_sp.get() && plist_sp->GetAsDictionary() &&189 plist_sp->GetAsDictionary()->HasKey(190 "DBGSourcePathRemapping") &&191 plist_sp->GetAsDictionary()192 ->GetValueForKey("DBGSourcePathRemapping")193 ->GetAsDictionary()) {194 195 // If DBGVersion 1 or DBGVersion missing, ignore196 // DBGSourcePathRemapping. If DBGVersion 2, strip last two197 // components of path remappings from198 // entries to fix an issue with a199 // specific set of DBGSourcePathRemapping200 // entries that lldb worked with.201 // If DBGVersion 3, trust & use the source path remappings202 // as-is.203 //204 205 bool new_style_source_remapping_dictionary = false;206 bool do_truncate_remapping_names = false;207 std::string original_DBGSourcePath_value = DBGSourcePath;208 if (plist_sp->GetAsDictionary()->HasKey("DBGVersion")) {209 std::string version_string =210 std::string(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,232 do_truncate_remapping_names](233 llvm::StringRef key,234 StructuredData::Object *object) -> bool {235 if (object && object->GetAsString()) {236 237 // key is DBGBuildSourcePath238 // object is DBGSourcePath239 std::string DBGSourcePath =240 std::string(object->GetStringValue());241 if (!new_style_source_remapping_dictionary &&242 !original_DBGSourcePath_value.empty()) {243 DBGSourcePath = original_DBGSourcePath_value;244 }245 module_sp->GetSourceMappingList().Append(246 key, DBGSourcePath, true);247 // With version 2 of DBGSourcePathRemapping, we248 // can chop off the last two filename parts249 // from the source remapping and get a more250 // general source remapping that still works.251 // Add this as another option in addition to252 // the full source path remap.253 if (do_truncate_remapping_names) {254 FileSpec build_path(key);255 FileSpec source_path(DBGSourcePath.c_str());256 build_path.RemoveLastPathComponent();257 build_path.RemoveLastPathComponent();258 source_path.RemoveLastPathComponent();259 source_path.RemoveLastPathComponent();260 module_sp->GetSourceMappingList().Append(261 build_path.GetPath(), source_path.GetPath(),262 true);263 }264 }265 return true;266 });267 }268 269 // If we have a DBGBuildSourcePath + DBGSourcePath pair,270 // append those to the source path remappings.271 272 plist.GetValueAsString("DBGBuildSourcePath",273 DBGBuildSourcePath);274 plist.GetValueAsString("DBGSourcePath", DBGSourcePath);275 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {276 module_sp->GetSourceMappingList().Append(277 DBGBuildSourcePath, DBGSourcePath, true);278 }279 }280 }281 }282 }283 }284 }285 286 symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);287 return symbol_vendor;288 }289 }290 291 // Just create our symbol vendor using the current objfile as this is292 // either an executable with no dSYM (that we could locate), an executable293 // with a dSYM that has a UUID that doesn't match.294 symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this());295 }296 return symbol_vendor;297}298