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