318 lines · cpp
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-exception6//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 34namespace llvm {35 36namespace dsymutil {37 38using namespace llvm::object;39 40DebugMapObject::DebugMapObject(StringRef ObjectFilename,41 sys::TimePoint<std::chrono::seconds> Timestamp,42 uint8_t Type)43 : Filename(std::string(ObjectFilename)), Timestamp(Timestamp), Type(Type) {}44 45bool 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 61void DebugMapObject::setRelocationMap(dsymutil::RelocationMap &RM) {62 RelocMap.emplace(RM);63}64 65void DebugMapObject::setInstallName(StringRef IN) { InstallName.emplace(IN); }66 67void DebugMapObject::print(raw_ostream &OS) const {68 OS << getObjectFilename() << ":\n";69 // Sort the symbols in alphabetical order, like llvm-nm (and to get70 // 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 else81 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 NDEBUG90void DebugMapObject::dump() const { print(errs()); }91#endif92 93DebugMapObject &94DebugMap::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 101const DebugMapObject::DebugMapEntry *102DebugMapObject::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 109const DebugMapObject::DebugMapEntry *110DebugMapObject::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 117void 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 NDEBUG123void DebugMap::dump() const { print(errs()); }124#endif125 126namespace {127 128struct 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 namespace137 138DebugMap::DebugMap(const Triple &BinaryTriple, StringRef BinaryPath,139 ArrayRef<uint8_t> BinaryUUID)140 : BinaryTriple(BinaryTriple), BinaryPath(std::string(BinaryPath)),141 BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {}142 143ErrorOr<std::vector<std::unique_ptr<DebugMap>>>144DebugMap::parseYAMLDebugMap(BinaryHolder &BinHolder, StringRef InputFile,145 StringRef PrependPath, bool Verbose) {146 auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);147 if (auto Err = ErrOrFile.getError())148 return Err;149 150 YAMLContext Ctxt(BinHolder, PrependPath);151 152 std::unique_ptr<DebugMap> Res;153 yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt);154 yin >> Res;155 156 if (auto EC = yin.error())157 return EC;158 std::vector<std::unique_ptr<DebugMap>> Result;159 Result.push_back(std::move(Res));160 return std::move(Result);161}162 163} // end namespace dsymutil164 165namespace yaml {166 167// Normalize/Denormalize between YAML and a DebugMapObject.168struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {169 YamlDMO(IO &io) {}170 YamlDMO(IO &io, dsymutil::DebugMapObject &Obj);171 dsymutil::DebugMapObject denormalize(IO &IO);172 173 std::string Filename;174 int64_t Timestamp = 0;175 uint8_t Type = MachO::N_OSO;176 std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;177};178 179void MappingTraits<std::pair<std::string, SymbolMapping>>::mapping(180 IO &io, std::pair<std::string, SymbolMapping> &s) {181 io.mapRequired("sym", s.first);182 io.mapOptional("objAddr", s.second.ObjectAddress);183 io.mapRequired("binAddr", s.second.BinaryAddress);184 io.mapOptional("size", s.second.Size);185}186 187void MappingTraits<dsymutil::DebugMapObject>::mapping(188 IO &io, dsymutil::DebugMapObject &DMO) {189 MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO);190 io.mapRequired("filename", Norm->Filename);191 io.mapOptional("timestamp", Norm->Timestamp);192 io.mapOptional("type", Norm->Type);193 io.mapRequired("symbols", Norm->Entries);194}195 196void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) {197 out << val.str();198}199 200StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) {201 value = Triple(scalar);202 return StringRef();203}204 205size_t206SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size(207 IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) {208 return seq.size();209}210 211dsymutil::DebugMapObject &212SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element(213 IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq,214 size_t index) {215 if (index >= seq.size()) {216 seq.resize(index + 1);217 seq[index].reset(new dsymutil::DebugMapObject);218 }219 return *seq[index];220}221 222void MappingTraits<dsymutil::DebugMap>::mapping(IO &io,223 dsymutil::DebugMap &DM) {224 io.mapRequired("triple", DM.BinaryTriple);225 io.mapOptional("binary-path", DM.BinaryPath);226 if (void *Ctxt = io.getContext())227 reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple;228 io.mapOptional("objects", DM.Objects);229}230 231void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(232 IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) {233 if (!DM)234 DM.reset(new DebugMap());235 io.mapRequired("triple", DM->BinaryTriple);236 io.mapOptional("binary-path", DM->BinaryPath);237 if (void *Ctxt = io.getContext())238 reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple;239 io.mapOptional("objects", DM->Objects);240}241 242MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO(243 IO &io, dsymutil::DebugMapObject &Obj) {244 Filename = Obj.Filename;245 Timestamp = sys::toTimeT(Obj.getTimestamp());246 Type = Obj.getType();247 Entries.reserve(Obj.Symbols.size());248 for (auto &Entry : Obj.Symbols)249 Entries.push_back(250 std::make_pair(std::string(Entry.getKey()), Entry.getValue()));251 llvm::sort(Entries, llvm::less_first());252}253 254dsymutil::DebugMapObject255MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {256 const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext());257 SmallString<80> Path(Ctxt.PrependPath);258 StringMap<uint64_t> SymbolAddresses;259 260 sys::path::append(Path, Filename);261 262 auto ObjectEntry = Ctxt.BinHolder.getObjectEntry(Path);263 if (!ObjectEntry) {264 auto Err = ObjectEntry.takeError();265 WithColor::warning() << "Unable to open " << Path << " "266 << toString(std::move(Err)) << '\n';267 } else {268 auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple);269 if (!Object) {270 auto Err = Object.takeError();271 WithColor::warning() << "Unable to open " << Path << " "272 << toString(std::move(Err)) << '\n';273 } else {274 for (const auto &Sym : Object->symbols()) {275 Expected<uint64_t> AddressOrErr = Sym.getValue();276 if (!AddressOrErr) {277 // TODO: Actually report errors helpfully.278 consumeError(AddressOrErr.takeError());279 continue;280 }281 Expected<StringRef> Name = Sym.getName();282 Expected<uint32_t> FlagsOrErr = Sym.getFlags();283 if (!Name || !FlagsOrErr ||284 (*FlagsOrErr & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) {285 // TODO: Actually report errors helpfully.286 if (!FlagsOrErr)287 consumeError(FlagsOrErr.takeError());288 if (!Name)289 consumeError(Name.takeError());290 continue;291 }292 SymbolAddresses[*Name] = *AddressOrErr;293 }294 }295 }296 297 if (Path.ends_with(".dylib")) {298 // FIXME: find a more resilient way299 Type = MachO::N_LIB;300 }301 dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), Type);302 303 for (auto &Entry : Entries) {304 auto &Mapping = Entry.second;305 std::optional<uint64_t> ObjAddress;306 if (Mapping.ObjectAddress)307 ObjAddress = *Mapping.ObjectAddress;308 auto AddressIt = SymbolAddresses.find(Entry.first);309 if (AddressIt != SymbolAddresses.end())310 ObjAddress = AddressIt->getValue();311 Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size);312 }313 return Res;314}315 316} // end namespace yaml317} // end namespace llvm318