1 //===- DIContext.h ----------------------------------------------*- C++ -*-===//
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 // This file defines DIContext, an abstract data structure that holds
10 // debug information data.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_DEBUGINFO_DICONTEXT_H
15 #define LLVM_DEBUGINFO_DICONTEXT_H
16 
17 #include "llvm/ADT/SmallVector.h"
18 #include "llvm/Object/ObjectFile.h"
19 #include "llvm/Support/WithColor.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include <cassert>
22 #include <cstdint>
23 #include <memory>
24 #include <string>
25 #include <tuple>
26 #include <utility>
27 
28 namespace llvm {
29 
30 /// A format-neutral container for source line information.
31 struct DILineInfo {
32   // DILineInfo contains "<invalid>" for function/filename it cannot fetch.
33   static constexpr const char *const BadString = "<invalid>";
34   // Use "??" instead of "<invalid>" to make our output closer to addr2line.
35   static constexpr const char *const Addr2LineBadString = "??";
36   std::string FileName;
37   std::string FunctionName;
38   std::string StartFileName;
39   Optional<StringRef> Source;
40   uint32_t Line = 0;
41   uint32_t Column = 0;
42   uint32_t StartLine = 0;
43   Optional<uint64_t> StartAddress;
44 
45   // DWARF-specific.
46   uint32_t Discriminator = 0;
47 
DILineInfoDILineInfo48   DILineInfo()
49       : FileName(BadString), FunctionName(BadString), StartFileName(BadString) {
50   }
51 
52   bool operator==(const DILineInfo &RHS) const {
53     return Line == RHS.Line && Column == RHS.Column &&
54            FileName == RHS.FileName && FunctionName == RHS.FunctionName &&
55            StartFileName == RHS.StartFileName && StartLine == RHS.StartLine &&
56            Discriminator == RHS.Discriminator;
57   }
58 
59   bool operator!=(const DILineInfo &RHS) const { return !(*this == RHS); }
60 
61   bool operator<(const DILineInfo &RHS) const {
62     return std::tie(FileName, FunctionName, StartFileName, Line, Column,
63                     StartLine, Discriminator) <
64            std::tie(RHS.FileName, RHS.FunctionName, RHS.StartFileName, RHS.Line,
65                     RHS.Column, RHS.StartLine, RHS.Discriminator);
66   }
67 
68   explicit operator bool() const { return *this != DILineInfo(); }
69 
dumpDILineInfo70   void dump(raw_ostream &OS) {
71     OS << "Line info: ";
72     if (FileName != BadString)
73       OS << "file '" << FileName << "', ";
74     if (FunctionName != BadString)
75       OS << "function '" << FunctionName << "', ";
76     OS << "line " << Line << ", ";
77     OS << "column " << Column << ", ";
78     if (StartFileName != BadString)
79       OS << "start file '" << StartFileName << "', ";
80     OS << "start line " << StartLine << '\n';
81   }
82 };
83 
84 using DILineInfoTable = SmallVector<std::pair<uint64_t, DILineInfo>, 16>;
85 
86 /// A format-neutral container for inlined code description.
87 class DIInliningInfo {
88   SmallVector<DILineInfo, 4> Frames;
89 
90 public:
91   DIInliningInfo() = default;
92 
getFrame(unsigned Index)93   const DILineInfo &getFrame(unsigned Index) const {
94     assert(Index < Frames.size());
95     return Frames[Index];
96   }
97 
getMutableFrame(unsigned Index)98   DILineInfo *getMutableFrame(unsigned Index) {
99     assert(Index < Frames.size());
100     return &Frames[Index];
101   }
102 
getNumberOfFrames()103   uint32_t getNumberOfFrames() const { return Frames.size(); }
104 
addFrame(const DILineInfo & Frame)105   void addFrame(const DILineInfo &Frame) { Frames.push_back(Frame); }
106 
resize(unsigned i)107   void resize(unsigned i) { Frames.resize(i); }
108 };
109 
110 /// Container for description of a global variable.
111 struct DIGlobal {
112   std::string Name;
113   uint64_t Start = 0;
114   uint64_t Size = 0;
115 
DIGlobalDIGlobal116   DIGlobal() : Name(DILineInfo::BadString) {}
117 };
118 
119 struct DILocal {
120   std::string FunctionName;
121   std::string Name;
122   std::string DeclFile;
123   uint64_t DeclLine = 0;
124   Optional<int64_t> FrameOffset;
125   Optional<uint64_t> Size;
126   Optional<uint64_t> TagOffset;
127 };
128 
129 /// A DINameKind is passed to name search methods to specify a
130 /// preference regarding the type of name resolution the caller wants.
131 enum class DINameKind { None, ShortName, LinkageName };
132 
133 /// Controls which fields of DILineInfo container should be filled
134 /// with data.
135 struct DILineInfoSpecifier {
136   enum class FileLineInfoKind {
137     None,
138     // RawValue is whatever the compiler stored in the filename table.  Could be
139     // a full path, could be something else.
140     RawValue,
141     BaseNameOnly,
142     // Relative to the compilation directory.
143     RelativeFilePath,
144     AbsoluteFilePath
145   };
146   using FunctionNameKind = DINameKind;
147 
148   FileLineInfoKind FLIKind;
149   FunctionNameKind FNKind;
150 
151   DILineInfoSpecifier(FileLineInfoKind FLIKind = FileLineInfoKind::RawValue,
152                       FunctionNameKind FNKind = FunctionNameKind::None)
FLIKindDILineInfoSpecifier153       : FLIKind(FLIKind), FNKind(FNKind) {}
154 };
155 
156 /// This is just a helper to programmatically construct DIDumpType.
157 enum DIDumpTypeCounter {
158 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \
159   DIDT_ID_##ENUM_NAME,
160 #include "llvm/BinaryFormat/Dwarf.def"
161 #undef HANDLE_DWARF_SECTION
162   DIDT_ID_UUID,
163   DIDT_ID_Count
164 };
165 static_assert(DIDT_ID_Count <= 32, "section types overflow storage");
166 
167 /// Selects which debug sections get dumped.
168 enum DIDumpType : unsigned {
169   DIDT_Null,
170   DIDT_All = ~0U,
171 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \
172   DIDT_##ENUM_NAME = 1U << DIDT_ID_##ENUM_NAME,
173 #include "llvm/BinaryFormat/Dwarf.def"
174 #undef HANDLE_DWARF_SECTION
175   DIDT_UUID = 1 << DIDT_ID_UUID,
176 };
177 
178 /// Container for dump options that control which debug information will be
179 /// dumped.
180 struct DIDumpOptions {
181   unsigned DumpType = DIDT_All;
182   unsigned ChildRecurseDepth = -1U;
183   unsigned ParentRecurseDepth = -1U;
184   uint16_t Version = 0; // DWARF version to assume when extracting.
185   uint8_t AddrSize = 4; // Address byte size to assume when extracting.
186   bool ShowAddresses = true;
187   bool ShowChildren = false;
188   bool ShowParents = false;
189   bool ShowForm = false;
190   bool SummarizeTypes = false;
191   bool Verbose = false;
192   bool DisplayRawContents = false;
193 
194   /// Return default option set for printing a single DIE without children.
getForSingleDIEDIDumpOptions195   static DIDumpOptions getForSingleDIE() {
196     DIDumpOptions Opts;
197     Opts.ChildRecurseDepth = 0;
198     Opts.ParentRecurseDepth = 0;
199     return Opts;
200   }
201 
202   /// Return the options with RecurseDepth set to 0 unless explicitly required.
noImplicitRecursionDIDumpOptions203   DIDumpOptions noImplicitRecursion() const {
204     DIDumpOptions Opts = *this;
205     if (ChildRecurseDepth == -1U && !ShowChildren)
206       Opts.ChildRecurseDepth = 0;
207     if (ParentRecurseDepth == -1U && !ShowParents)
208       Opts.ParentRecurseDepth = 0;
209     return Opts;
210   }
211 
212   std::function<void(Error)> RecoverableErrorHandler =
213       WithColor::defaultErrorHandler;
214   std::function<void(Error)> WarningHandler = WithColor::defaultWarningHandler;
215 };
216 
217 class DIContext {
218 public:
219   enum DIContextKind { CK_DWARF, CK_PDB };
220 
DIContext(DIContextKind K)221   DIContext(DIContextKind K) : Kind(K) {}
222   virtual ~DIContext() = default;
223 
getKind()224   DIContextKind getKind() const { return Kind; }
225 
226   virtual void dump(raw_ostream &OS, DIDumpOptions DumpOpts) = 0;
227 
228   virtual bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) {
229     // No verifier? Just say things went well.
230     return true;
231   }
232 
233   virtual DILineInfo getLineInfoForAddress(
234       object::SectionedAddress Address,
235       DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
236   virtual DILineInfoTable getLineInfoForAddressRange(
237       object::SectionedAddress Address, uint64_t Size,
238       DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
239   virtual DIInliningInfo getInliningInfoForAddress(
240       object::SectionedAddress Address,
241       DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
242 
243   virtual std::vector<DILocal>
244   getLocalsForAddress(object::SectionedAddress Address) = 0;
245 
246 private:
247   const DIContextKind Kind;
248 };
249 
250 /// An inferface for inquiring the load address of a loaded object file
251 /// to be used by the DIContext implementations when applying relocations
252 /// on the fly.
253 class LoadedObjectInfo {
254 protected:
255   LoadedObjectInfo() = default;
256   LoadedObjectInfo(const LoadedObjectInfo &) = default;
257 
258 public:
259   virtual ~LoadedObjectInfo() = default;
260 
261   /// Obtain the Load Address of a section by SectionRef.
262   ///
263   /// Calculate the address of the given section.
264   /// The section need not be present in the local address space. The addresses
265   /// need to be consistent with the addresses used to query the DIContext and
266   /// the output of this function should be deterministic, i.e. repeated calls
267   /// with the same Sec should give the same address.
getSectionLoadAddress(const object::SectionRef & Sec)268   virtual uint64_t getSectionLoadAddress(const object::SectionRef &Sec) const {
269     return 0;
270   }
271 
272   /// If conveniently available, return the content of the given Section.
273   ///
274   /// When the section is available in the local address space, in relocated
275   /// (loaded) form, e.g. because it was relocated by a JIT for execution, this
276   /// function should provide the contents of said section in `Data`. If the
277   /// loaded section is not available, or the cost of retrieving it would be
278   /// prohibitive, this function should return false. In that case, relocations
279   /// will be read from the local (unrelocated) object file and applied on the
280   /// fly. Note that this method is used purely for optimzation purposes in the
281   /// common case of JITting in the local address space, so returning false
282   /// should always be correct.
getLoadedSectionContents(const object::SectionRef & Sec,StringRef & Data)283   virtual bool getLoadedSectionContents(const object::SectionRef &Sec,
284                                         StringRef &Data) const {
285     return false;
286   }
287 
288   // FIXME: This is untested and unused anywhere in the LLVM project, it's
289   // used/needed by Julia (an external project). It should have some coverage
290   // (at least tests, but ideally example functionality).
291   /// Obtain a copy of this LoadedObjectInfo.
292   virtual std::unique_ptr<LoadedObjectInfo> clone() const = 0;
293 };
294 
295 template <typename Derived, typename Base = LoadedObjectInfo>
296 struct LoadedObjectInfoHelper : Base {
297 protected:
298   LoadedObjectInfoHelper(const LoadedObjectInfoHelper &) = default;
299   LoadedObjectInfoHelper() = default;
300 
301 public:
302   template <typename... Ts>
LoadedObjectInfoHelperLoadedObjectInfoHelper303   LoadedObjectInfoHelper(Ts &&...Args) : Base(std::forward<Ts>(Args)...) {}
304 
cloneLoadedObjectInfoHelper305   std::unique_ptr<llvm::LoadedObjectInfo> clone() const override {
306     return std::make_unique<Derived>(static_cast<const Derived &>(*this));
307   }
308 };
309 
310 } // end namespace llvm
311 
312 #endif // LLVM_DEBUGINFO_DICONTEXT_H
313