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