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