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