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