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