Dwarf.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. * Copyright 2012-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. // DWARF record parser
  17. #pragma once
  18. #include <boost/variant.hpp>
  19. #include <folly/Range.h>
  20. #include <folly/experimental/symbolizer/Elf.h>
  21. namespace folly {
  22. namespace symbolizer {
  23. /**
  24. * DWARF record parser.
  25. *
  26. * We only implement enough DWARF functionality to convert from PC address
  27. * to file and line number information.
  28. *
  29. * This means (although they're not part of the public API of this class), we
  30. * can parse Debug Information Entries (DIEs), abbreviations, attributes (of
  31. * all forms), and we can interpret bytecode for the line number VM.
  32. *
  33. * We can interpret DWARF records of version 2, 3, or 4, although we don't
  34. * actually support many of the version 4 features (such as VLIW, multiple
  35. * operations per instruction)
  36. *
  37. * Note that the DWARF record parser does not allocate heap memory at all.
  38. * This is on purpose: you can use the parser from
  39. * memory-constrained situations (such as an exception handler for
  40. * std::out_of_memory) If it weren't for this requirement, some things would
  41. * be much simpler: the Path class would be unnecessary and would be replaced
  42. * with a std::string; the list of file names in the line number VM would be
  43. * kept as a vector of strings instead of re-executing the program to look for
  44. * DW_LNE_define_file instructions, etc.
  45. */
  46. class Dwarf {
  47. // Note that Dwarf uses (and returns) StringPiece a lot.
  48. // The StringPieces point within sections in the ELF file, and so will
  49. // be live for as long as the passed-in ElfFile is live.
  50. public:
  51. /** Create a DWARF parser around an ELF file. */
  52. explicit Dwarf(const ElfFile* elf);
  53. /**
  54. * Represent a file path a s collection of three parts (base directory,
  55. * subdirectory, and file).
  56. */
  57. class Path {
  58. public:
  59. Path() {}
  60. Path(
  61. folly::StringPiece baseDir,
  62. folly::StringPiece subDir,
  63. folly::StringPiece file);
  64. folly::StringPiece baseDir() const {
  65. return baseDir_;
  66. }
  67. folly::StringPiece subDir() const {
  68. return subDir_;
  69. }
  70. folly::StringPiece file() const {
  71. return file_;
  72. }
  73. size_t size() const;
  74. /**
  75. * Copy the Path to a buffer of size bufSize.
  76. *
  77. * toBuffer behaves like snprintf: It will always null-terminate the
  78. * buffer (so it will copy at most bufSize-1 bytes), and it will return
  79. * the number of bytes that would have been written if there had been
  80. * enough room, so, if toBuffer returns a value >= bufSize, the output
  81. * was truncated.
  82. */
  83. size_t toBuffer(char* buf, size_t bufSize) const;
  84. void toString(std::string& dest) const;
  85. std::string toString() const {
  86. std::string s;
  87. toString(s);
  88. return s;
  89. }
  90. // TODO(tudorb): Implement operator==, operator!=; not as easy as it
  91. // seems as the same path can be represented in multiple ways
  92. private:
  93. folly::StringPiece baseDir_;
  94. folly::StringPiece subDir_;
  95. folly::StringPiece file_;
  96. };
  97. enum class LocationInfoMode {
  98. // Don't resolve location info.
  99. DISABLED,
  100. // Perform CU lookup using .debug_aranges (might be incomplete).
  101. FAST,
  102. // Scan all CU in .debug_info (slow!) on .debug_aranges lookup failure.
  103. FULL,
  104. };
  105. struct LocationInfo {
  106. bool hasMainFile = false;
  107. Path mainFile;
  108. bool hasFileAndLine = false;
  109. Path file;
  110. uint64_t line = 0;
  111. };
  112. /**
  113. * Find the file and line number information corresponding to address.
  114. */
  115. bool findAddress(uintptr_t address, LocationInfo& info, LocationInfoMode mode)
  116. const;
  117. private:
  118. static bool
  119. findDebugInfoOffset(uintptr_t address, StringPiece aranges, uint64_t& offset);
  120. void init();
  121. bool findLocation(
  122. uintptr_t address,
  123. StringPiece& infoEntry,
  124. LocationInfo& info) const;
  125. const ElfFile* elf_;
  126. // DWARF section made up of chunks, each prefixed with a length header.
  127. // The length indicates whether the chunk is DWARF-32 or DWARF-64, which
  128. // guides interpretation of "section offset" records.
  129. // (yes, DWARF-32 and DWARF-64 sections may coexist in the same file)
  130. class Section {
  131. public:
  132. Section() : is64Bit_(false) {}
  133. explicit Section(folly::StringPiece d);
  134. // Return next chunk, if any; the 4- or 12-byte length was already
  135. // parsed and isn't part of the chunk.
  136. bool next(folly::StringPiece& chunk);
  137. // Is the current chunk 64 bit?
  138. bool is64Bit() const {
  139. return is64Bit_;
  140. }
  141. private:
  142. // Yes, 32- and 64- bit sections may coexist. Yikes!
  143. bool is64Bit_;
  144. folly::StringPiece data_;
  145. };
  146. // Abbreviation for a Debugging Information Entry.
  147. struct DIEAbbreviation {
  148. uint64_t code;
  149. uint64_t tag;
  150. bool hasChildren;
  151. struct Attribute {
  152. uint64_t name;
  153. uint64_t form;
  154. };
  155. folly::StringPiece attributes;
  156. };
  157. // Interpreter for the line number bytecode VM
  158. class LineNumberVM {
  159. public:
  160. LineNumberVM(
  161. folly::StringPiece data,
  162. folly::StringPiece compilationDirectory);
  163. bool findAddress(uintptr_t address, Path& file, uint64_t& line);
  164. private:
  165. void init();
  166. void reset();
  167. // Execute until we commit one new row to the line number matrix
  168. bool next(folly::StringPiece& program);
  169. enum StepResult {
  170. CONTINUE, // Continue feeding opcodes
  171. COMMIT, // Commit new <address, file, line> tuple
  172. END, // End of sequence
  173. };
  174. // Execute one opcode
  175. StepResult step(folly::StringPiece& program);
  176. struct FileName {
  177. folly::StringPiece relativeName;
  178. // 0 = current compilation directory
  179. // otherwise, 1-based index in the list of include directories
  180. uint64_t directoryIndex;
  181. };
  182. // Read one FileName object, advance sp
  183. static bool readFileName(folly::StringPiece& sp, FileName& fn);
  184. // Get file name at given index; may be in the initial table
  185. // (fileNames_) or defined using DW_LNE_define_file (and we reexecute
  186. // enough of the program to find it, if so)
  187. FileName getFileName(uint64_t index) const;
  188. // Get include directory at given index
  189. folly::StringPiece getIncludeDirectory(uint64_t index) const;
  190. // Execute opcodes until finding a DW_LNE_define_file and return true;
  191. // return file at the end.
  192. bool nextDefineFile(folly::StringPiece& program, FileName& fn) const;
  193. // Initialization
  194. bool is64Bit_;
  195. folly::StringPiece data_;
  196. folly::StringPiece compilationDirectory_;
  197. // Header
  198. uint16_t version_;
  199. uint8_t minLength_;
  200. bool defaultIsStmt_;
  201. int8_t lineBase_;
  202. uint8_t lineRange_;
  203. uint8_t opcodeBase_;
  204. const uint8_t* standardOpcodeLengths_;
  205. folly::StringPiece includeDirectories_;
  206. size_t includeDirectoryCount_;
  207. folly::StringPiece fileNames_;
  208. size_t fileNameCount_;
  209. // State machine registers
  210. uint64_t address_;
  211. uint64_t file_;
  212. uint64_t line_;
  213. uint64_t column_;
  214. bool isStmt_;
  215. bool basicBlock_;
  216. bool endSequence_;
  217. bool prologueEnd_;
  218. bool epilogueBegin_;
  219. uint64_t isa_;
  220. uint64_t discriminator_;
  221. };
  222. // Read an abbreviation from a StringPiece, return true if at end; advance sp
  223. static bool readAbbreviation(folly::StringPiece& sp, DIEAbbreviation& abbr);
  224. // Get abbreviation corresponding to a code, in the chunk starting at
  225. // offset in the .debug_abbrev section
  226. DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const;
  227. // Read one attribute <name, form> pair, advance sp; returns <0, 0> at end.
  228. static DIEAbbreviation::Attribute readAttribute(folly::StringPiece& sp);
  229. // Read one attribute value, advance sp
  230. typedef boost::variant<uint64_t, folly::StringPiece> AttributeValue;
  231. AttributeValue
  232. readAttributeValue(folly::StringPiece& sp, uint64_t form, bool is64Bit) const;
  233. // Get an ELF section by name, return true if found
  234. bool getSection(const char* name, folly::StringPiece* section) const;
  235. // Get a string from the .debug_str section
  236. folly::StringPiece getStringFromStringSection(uint64_t offset) const;
  237. folly::StringPiece info_; // .debug_info
  238. folly::StringPiece abbrev_; // .debug_abbrev
  239. folly::StringPiece aranges_; // .debug_aranges
  240. folly::StringPiece line_; // .debug_line
  241. folly::StringPiece strings_; // .debug_str
  242. };
  243. inline std::ostream& operator<<(std::ostream& out, const Dwarf::Path& path) {
  244. return out << path.toString();
  245. }
  246. } // namespace symbolizer
  247. } // namespace folly