1 //===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// 2 // 3 // The LLVM Linker 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "DebugMap.h" 11 #include "BinaryHolder.h" 12 #include "llvm/ADT/Optional.h" 13 #include "llvm/ADT/SmallString.h" 14 #include "llvm/ADT/StringMap.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/ADT/Triple.h" 17 #include "llvm/ADT/iterator_range.h" 18 #include "llvm/BinaryFormat/MachO.h" 19 #include "llvm/Object/ObjectFile.h" 20 #include "llvm/Support/Chrono.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/Format.h" 23 #include "llvm/Support/MemoryBuffer.h" 24 #include "llvm/Support/Path.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(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 : make_range(Symbols.begin(), Symbols.end())) 64 Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue())); 65 std::sort( 66 Entries.begin(), Entries.end(), 67 [](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; }); 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(std::make_pair(Entry.getKey(), Entry.getValue())); 232 } 233 234 dsymutil::DebugMapObject 235 MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { 236 BinaryHolder BinHolder(/* 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 auto ErrOrObjectFiles = BinHolder.GetObjectFiles(Path); 243 if (auto EC = ErrOrObjectFiles.getError()) { 244 errs() << "warning: Unable to open " << Path << " " << EC.message() << '\n'; 245 } else if (auto ErrOrObjectFile = BinHolder.Get(Ctxt.BinaryTriple)) { 246 // Rewrite the object file symbol addresses in the debug map. The 247 // YAML input is mainly used to test llvm-dsymutil without 248 // requiring binaries checked-in. If we generate the object files 249 // during the test, we can't hardcode the symbols addresses, so 250 // look them up here and rewrite them. 251 for (const auto &Sym : ErrOrObjectFile->symbols()) { 252 uint64_t Address = Sym.getValue(); 253 Expected<StringRef> Name = Sym.getName(); 254 if (!Name || 255 (Sym.getFlags() & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { 256 // TODO: Actually report errors helpfully. 257 if (!Name) 258 consumeError(Name.takeError()); 259 continue; 260 } 261 SymbolAddresses[*Name] = Address; 262 } 263 } 264 265 dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO); 266 for (auto &Entry : Entries) { 267 auto &Mapping = Entry.second; 268 Optional<uint64_t> ObjAddress; 269 if (Mapping.ObjectAddress) 270 ObjAddress = *Mapping.ObjectAddress; 271 auto AddressIt = SymbolAddresses.find(Entry.first); 272 if (AddressIt != SymbolAddresses.end()) 273 ObjAddress = AddressIt->getValue(); 274 Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size); 275 } 276 return Res; 277 } 278 279 } // end namespace yaml 280 281 } // end namespace llvm 282