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