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