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