1//===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// 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-exception 6// 7//===----------------------------------------------------------------------===// 8 9#include "DebugMap.h" 10#include "BinaryHolder.h" 11#include "llvm/ADT/SmallString.h" 12#include "llvm/ADT/StringMap.h" 13#include "llvm/ADT/StringRef.h" 14#include "llvm/ADT/Triple.h" 15#include "llvm/ADT/iterator_range.h" 16#include "llvm/BinaryFormat/MachO.h" 17#include "llvm/Object/ObjectFile.h" 18#include "llvm/Support/Chrono.h" 19#include "llvm/Support/Error.h" 20#include "llvm/Support/Format.h" 21#include "llvm/Support/MemoryBuffer.h" 22#include "llvm/Support/Path.h" 23#include "llvm/Support/WithColor.h" 24#include "llvm/Support/YAMLTraits.h" 25#include "llvm/Support/raw_ostream.h" 26#include <algorithm> 27#include <cinttypes> 28#include <cstdint> 29#include <memory> 30#include <optional> 31#include <string> 32#include <utility> 33#include <vector> 34 35namespace llvm { 36 37namespace dsymutil { 38 39using namespace llvm::object; 40 41DebugMapObject::DebugMapObject(StringRef ObjectFilename, 42 sys::TimePoint<std::chrono::seconds> Timestamp, 43 uint8_t Type) 44 : Filename(std::string(ObjectFilename)), Timestamp(Timestamp), Type(Type) {} 45 46bool DebugMapObject::addSymbol(StringRef Name, 47 std::optional<uint64_t> ObjectAddress, 48 uint64_t LinkedAddress, uint32_t Size) { 49 auto InsertResult = Symbols.insert( 50 std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size))); 51 52 if (ObjectAddress && InsertResult.second) 53 AddressToMapping[*ObjectAddress] = &*InsertResult.first; 54 return InsertResult.second; 55} 56 57void DebugMapObject::print(raw_ostream &OS) const { 58 OS << getObjectFilename() << ":\n"; 59 // Sort the symbols in alphabetical order, like llvm-nm (and to get 60 // deterministic output for testing). 61 using Entry = std::pair<StringRef, SymbolMapping>; 62 std::vector<Entry> Entries; 63 Entries.reserve(Symbols.getNumItems()); 64 for (const auto &Sym : Symbols) 65 Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue())); 66 llvm::sort(Entries, llvm::less_first()); 67 for (const auto &Sym : Entries) { 68 if (Sym.second.ObjectAddress) 69 OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress)); 70 else 71 OS << "\t????????????????"; 72 OS << format(" => %016" PRIx64 "+0x%x\t%s\n", 73 uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size), 74 Sym.first.data()); 75 } 76 OS << '\n'; 77} 78 79#ifndef NDEBUG 80void DebugMapObject::dump() const { print(errs()); } 81#endif 82 83DebugMapObject & 84DebugMap::addDebugMapObject(StringRef ObjectFilePath, 85 sys::TimePoint<std::chrono::seconds> Timestamp, 86 uint8_t Type) { 87 Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp, Type)); 88 return *Objects.back(); 89} 90 91const DebugMapObject::DebugMapEntry * 92DebugMapObject::lookupSymbol(StringRef SymbolName) const { 93 StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName); 94 if (Sym == Symbols.end()) 95 return nullptr; 96 return &*Sym; 97} 98 99const DebugMapObject::DebugMapEntry * 100DebugMapObject::lookupObjectAddress(uint64_t Address) const { 101 auto Mapping = AddressToMapping.find(Address); 102 if (Mapping == AddressToMapping.end()) 103 return nullptr; 104 return Mapping->getSecond(); 105} 106 107void DebugMap::print(raw_ostream &OS) const { 108 yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0); 109 yout << const_cast<DebugMap &>(*this); 110} 111 112#ifndef NDEBUG 113void DebugMap::dump() const { print(errs()); } 114#endif 115 116namespace { 117 118struct YAMLContext { 119 StringRef PrependPath; 120 Triple BinaryTriple; 121}; 122 123} // end anonymous namespace 124 125ErrorOr<std::vector<std::unique_ptr<DebugMap>>> 126DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, 127 bool Verbose) { 128 auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); 129 if (auto Err = ErrOrFile.getError()) 130 return Err; 131 132 YAMLContext Ctxt; 133 134 Ctxt.PrependPath = PrependPath; 135 136 std::unique_ptr<DebugMap> Res; 137 yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt); 138 yin >> Res; 139 140 if (auto EC = yin.error()) 141 return EC; 142 std::vector<std::unique_ptr<DebugMap>> Result; 143 Result.push_back(std::move(Res)); 144 return std::move(Result); 145} 146 147} // end namespace dsymutil 148 149namespace yaml { 150 151// Normalize/Denormalize between YAML and a DebugMapObject. 152struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { 153 YamlDMO(IO &io) { Timestamp = 0; } 154 YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); 155 dsymutil::DebugMapObject denormalize(IO &IO); 156 157 std::string Filename; 158 int64_t Timestamp; 159 std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; 160}; 161 162void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>:: 163 mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) { 164 io.mapRequired("sym", s.first); 165 io.mapOptional("objAddr", s.second.ObjectAddress); 166 io.mapRequired("binAddr", s.second.BinaryAddress); 167 io.mapOptional("size", s.second.Size); 168} 169 170void MappingTraits<dsymutil::DebugMapObject>::mapping( 171 IO &io, dsymutil::DebugMapObject &DMO) { 172 MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); 173 io.mapRequired("filename", Norm->Filename); 174 io.mapOptional("timestamp", Norm->Timestamp); 175 io.mapRequired("symbols", Norm->Entries); 176} 177 178void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) { 179 out << val.str(); 180} 181 182StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) { 183 value = Triple(scalar); 184 return StringRef(); 185} 186 187size_t 188SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size( 189 IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) { 190 return seq.size(); 191} 192 193dsymutil::DebugMapObject & 194SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element( 195 IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, 196 size_t index) { 197 if (index >= seq.size()) { 198 seq.resize(index + 1); 199 seq[index].reset(new dsymutil::DebugMapObject); 200 } 201 return *seq[index]; 202} 203 204void MappingTraits<dsymutil::DebugMap>::mapping(IO &io, 205 dsymutil::DebugMap &DM) { 206 io.mapRequired("triple", DM.BinaryTriple); 207 io.mapOptional("binary-path", DM.BinaryPath); 208 if (void *Ctxt = io.getContext()) 209 reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple; 210 io.mapOptional("objects", DM.Objects); 211} 212 213void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( 214 IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) { 215 if (!DM) 216 DM.reset(new DebugMap()); 217 io.mapRequired("triple", DM->BinaryTriple); 218 io.mapOptional("binary-path", DM->BinaryPath); 219 if (void *Ctxt = io.getContext()) 220 reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple; 221 io.mapOptional("objects", DM->Objects); 222} 223 224MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( 225 IO &io, dsymutil::DebugMapObject &Obj) { 226 Filename = Obj.Filename; 227 Timestamp = sys::toTimeT(Obj.getTimestamp()); 228 Entries.reserve(Obj.Symbols.size()); 229 for (auto &Entry : Obj.Symbols) 230 Entries.push_back( 231 std::make_pair(std::string(Entry.getKey()), Entry.getValue())); 232} 233 234dsymutil::DebugMapObject 235MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { 236 BinaryHolder BinHolder(vfs::getRealFileSystem(), /* Verbose =*/false); 237 const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext()); 238 SmallString<80> Path(Ctxt.PrependPath); 239 StringMap<uint64_t> SymbolAddresses; 240 241 sys::path::append(Path, Filename); 242 243 auto ObjectEntry = BinHolder.getObjectEntry(Path); 244 if (!ObjectEntry) { 245 auto Err = ObjectEntry.takeError(); 246 WithColor::warning() << "Unable to open " << Path << " " 247 << toString(std::move(Err)) << '\n'; 248 } else { 249 auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple); 250 if (!Object) { 251 auto Err = Object.takeError(); 252 WithColor::warning() << "Unable to open " << Path << " " 253 << toString(std::move(Err)) << '\n'; 254 } else { 255 for (const auto &Sym : Object->symbols()) { 256 Expected<uint64_t> AddressOrErr = Sym.getValue(); 257 if (!AddressOrErr) { 258 // TODO: Actually report errors helpfully. 259 consumeError(AddressOrErr.takeError()); 260 continue; 261 } 262 Expected<StringRef> Name = Sym.getName(); 263 Expected<uint32_t> FlagsOrErr = Sym.getFlags(); 264 if (!Name || !FlagsOrErr || 265 (*FlagsOrErr & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { 266 // TODO: Actually report errors helpfully. 267 if (!FlagsOrErr) 268 consumeError(FlagsOrErr.takeError()); 269 if (!Name) 270 consumeError(Name.takeError()); 271 continue; 272 } 273 SymbolAddresses[*Name] = *AddressOrErr; 274 } 275 } 276 } 277 278 dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO); 279 for (auto &Entry : Entries) { 280 auto &Mapping = Entry.second; 281 std::optional<uint64_t> ObjAddress; 282 if (Mapping.ObjectAddress) 283 ObjAddress = *Mapping.ObjectAddress; 284 auto AddressIt = SymbolAddresses.find(Entry.first); 285 if (AddressIt != SymbolAddresses.end()) 286 ObjAddress = AddressIt->getValue(); 287 Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size); 288 } 289 return Res; 290} 291 292} // end namespace yaml 293} // end namespace llvm 294