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