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