Elf.h 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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. // ELF file parser
  17. #pragma once
  18. #define FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
  19. #include <elf.h>
  20. #include <link.h> // For ElfW()
  21. #include <cstdio>
  22. #include <initializer_list>
  23. #include <stdexcept>
  24. #include <system_error>
  25. #include <folly/Conv.h>
  26. #include <folly/Likely.h>
  27. #include <folly/Range.h>
  28. #include <folly/lang/SafeAssert.h>
  29. namespace folly {
  30. namespace symbolizer {
  31. using ElfAddr = ElfW(Addr);
  32. using ElfEhdr = ElfW(Ehdr);
  33. using ElfOff = ElfW(Off);
  34. using ElfPhdr = ElfW(Phdr);
  35. using ElfShdr = ElfW(Shdr);
  36. using ElfSym = ElfW(Sym);
  37. /**
  38. * ELF file parser.
  39. *
  40. * We handle native files only (32-bit files on a 32-bit platform, 64-bit files
  41. * on a 64-bit platform), and only executables (ET_EXEC) and shared objects
  42. * (ET_DYN).
  43. */
  44. class ElfFile {
  45. public:
  46. ElfFile() noexcept;
  47. // Note: may throw, call openNoThrow() explicitly if you don't want to throw
  48. explicit ElfFile(const char* name, bool readOnly = true);
  49. // Open the ELF file.
  50. // Returns 0 on success, kSystemError (guaranteed to be -1) (and sets errno)
  51. // on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid
  52. // Elf file. On error, if msg is not nullptr, sets *msg to a static string
  53. // indicating what failed.
  54. enum {
  55. kSuccess = 0,
  56. kSystemError = -1,
  57. kInvalidElfFile = -2,
  58. };
  59. // Open the ELF file. Does not throw on error.
  60. int openNoThrow(
  61. const char* name,
  62. bool readOnly = true,
  63. const char** msg = nullptr) noexcept;
  64. // Like openNoThrow, but follow .gnu_debuglink if present
  65. int openAndFollow(
  66. const char* name,
  67. bool readOnly = true,
  68. const char** msg = nullptr) noexcept;
  69. // Open the ELF file. Throws on error.
  70. void open(const char* name, bool readOnly = true);
  71. ~ElfFile();
  72. ElfFile(ElfFile&& other) noexcept;
  73. ElfFile& operator=(ElfFile&& other);
  74. /** Retrieve the ELF header */
  75. const ElfEhdr& elfHeader() const {
  76. return at<ElfEhdr>(0);
  77. }
  78. /**
  79. * Get the base address, the address where the file should be loaded if
  80. * no relocations happened.
  81. */
  82. uintptr_t getBaseAddress() const {
  83. return baseAddress_;
  84. }
  85. /** Find a section given its name */
  86. const ElfShdr* getSectionByName(const char* name) const;
  87. /** Find a section given its index in the section header table */
  88. const ElfShdr* getSectionByIndex(size_t idx) const;
  89. /** Retrieve the name of a section */
  90. const char* getSectionName(const ElfShdr& section) const;
  91. /** Get the actual section body */
  92. folly::StringPiece getSectionBody(const ElfShdr& section) const;
  93. /** Retrieve a string from a string table section */
  94. const char* getString(const ElfShdr& stringTable, size_t offset) const;
  95. /**
  96. * Iterate over all strings in a string table section for as long as
  97. * fn(str) returns false.
  98. * Returns the current ("found") string when fn returned true, or nullptr
  99. * if fn returned false for all strings in the table.
  100. */
  101. template <class Fn>
  102. const char* iterateStrings(const ElfShdr& stringTable, Fn fn) const;
  103. /**
  104. * Iterate over program headers as long as fn(section) returns false.
  105. * Returns a pointer to the current ("found") section when fn returned
  106. * true, or nullptr if fn returned false for all sections.
  107. */
  108. template <class Fn>
  109. const ElfPhdr* iterateProgramHeaders(Fn fn) const;
  110. /**
  111. * Iterate over all sections for as long as fn(section) returns false.
  112. * Returns a pointer to the current ("found") section when fn returned
  113. * true, or nullptr if fn returned false for all sections.
  114. */
  115. template <class Fn>
  116. const ElfShdr* iterateSections(Fn fn) const;
  117. /**
  118. * Iterate over all sections with a given type. Similar to
  119. * iterateSections(), but filtered only for sections with the given type.
  120. */
  121. template <class Fn>
  122. const ElfShdr* iterateSectionsWithType(uint32_t type, Fn fn) const;
  123. /**
  124. * Iterate over all sections with a given types. Similar to
  125. * iterateSectionWithTypes(), but filtered on multiple types.
  126. */
  127. template <class Fn>
  128. const ElfShdr* iterateSectionsWithTypes(
  129. std::initializer_list<uint32_t> types,
  130. Fn fn) const;
  131. /**
  132. * Iterate over all symbols witin a given section.
  133. *
  134. * Returns a pointer to the current ("found") symbol when fn returned true,
  135. * or nullptr if fn returned false for all symbols.
  136. */
  137. template <class Fn>
  138. const ElfSym* iterateSymbols(const ElfShdr& section, Fn fn) const;
  139. template <class Fn>
  140. const ElfSym*
  141. iterateSymbolsWithType(const ElfShdr& section, uint32_t type, Fn fn) const;
  142. template <class Fn>
  143. const ElfSym* iterateSymbolsWithTypes(
  144. const ElfShdr& section,
  145. std::initializer_list<uint32_t> types,
  146. Fn fn) const;
  147. /**
  148. * Find symbol definition by address.
  149. * Note that this is the file virtual address, so you need to undo
  150. * any relocation that might have happened.
  151. *
  152. * Returns {nullptr, nullptr} if not found.
  153. */
  154. typedef std::pair<const ElfShdr*, const ElfSym*> Symbol;
  155. Symbol getDefinitionByAddress(uintptr_t address) const;
  156. /**
  157. * Find symbol definition by name.
  158. *
  159. * If a symbol with this name cannot be found, a <nullptr, nullptr> Symbol
  160. * will be returned. This is O(N) in the number of symbols in the file.
  161. *
  162. * Returns {nullptr, nullptr} if not found.
  163. */
  164. Symbol getSymbolByName(const char* name) const;
  165. /**
  166. * Get the value of a symbol.
  167. */
  168. template <class T>
  169. const T& getSymbolValue(const ElfSym* symbol) const {
  170. const ElfShdr* section = getSectionByIndex(symbol->st_shndx);
  171. FOLLY_SAFE_CHECK(section, "Symbol's section index is invalid");
  172. return valueAt<T>(*section, symbol->st_value);
  173. }
  174. /**
  175. * Get the value of the object stored at the given address.
  176. *
  177. * This is the function that you want to use in conjunction with
  178. * getSymbolValue() to follow pointers. For example, to get the value of
  179. * a char* symbol, you'd do something like this:
  180. *
  181. * auto sym = getSymbolByName("someGlobalValue");
  182. * auto addr = getSymbolValue<ElfAddr>(sym.second);
  183. * const char* str = &getSymbolValue<const char>(addr);
  184. */
  185. template <class T>
  186. const T& getAddressValue(const ElfAddr addr) const {
  187. const ElfShdr* section = getSectionContainingAddress(addr);
  188. FOLLY_SAFE_CHECK(section, "Address does not refer to existing section");
  189. return valueAt<T>(*section, addr);
  190. }
  191. /**
  192. * Retrieve symbol name.
  193. */
  194. const char* getSymbolName(Symbol symbol) const;
  195. /** Find the section containing the given address */
  196. const ElfShdr* getSectionContainingAddress(ElfAddr addr) const;
  197. private:
  198. bool init(const char** msg);
  199. void reset();
  200. ElfFile(const ElfFile&) = delete;
  201. ElfFile& operator=(const ElfFile&) = delete;
  202. void validateStringTable(const ElfShdr& stringTable) const;
  203. template <class T>
  204. const typename std::enable_if<std::is_pod<T>::value, T>::type& at(
  205. ElfOff offset) const {
  206. if (offset + sizeof(T) > length_) {
  207. char msg[kFilepathMaxLen + 128];
  208. snprintf(
  209. msg,
  210. sizeof(msg),
  211. "Offset (%zu + %zu) is not contained within our mmapped"
  212. " file (%s) of length %zu",
  213. offset,
  214. sizeof(T),
  215. filepath_,
  216. length_);
  217. FOLLY_SAFE_CHECK(offset + sizeof(T) <= length_, msg);
  218. }
  219. return *reinterpret_cast<T*>(file_ + offset);
  220. }
  221. template <class T>
  222. const T& valueAt(const ElfShdr& section, const ElfAddr addr) const {
  223. // For exectuables and shared objects, st_value holds a virtual address
  224. // that refers to the memory owned by sections. Since we didn't map the
  225. // sections into the addresses that they're expecting (sh_addr), but
  226. // instead just mmapped the entire file directly, we need to translate
  227. // between addresses and offsets into the file.
  228. //
  229. // TODO: For other file types, st_value holds a file offset directly. Since
  230. // I don't have a use-case for that right now, just assert that
  231. // nobody wants this. We can always add it later.
  232. FOLLY_SAFE_CHECK(
  233. elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN,
  234. "Only exectuables and shared objects are supported");
  235. FOLLY_SAFE_CHECK(
  236. addr >= section.sh_addr &&
  237. (addr + sizeof(T)) <= (section.sh_addr + section.sh_size),
  238. "Address is not contained within the provided segment");
  239. return at<T>(section.sh_offset + (addr - section.sh_addr));
  240. }
  241. static constexpr size_t kFilepathMaxLen = 512;
  242. char filepath_[kFilepathMaxLen] = {};
  243. int fd_;
  244. char* file_; // mmap() location
  245. size_t length_; // mmap() length
  246. uintptr_t baseAddress_;
  247. };
  248. } // namespace symbolizer
  249. } // namespace folly
  250. #include <folly/experimental/symbolizer/Elf-inl.h>