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