2 * Copyright 2013 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
20 #define FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
24 #include <link.h> // For ElfW()
27 #include <system_error>
29 #include "folly/Conv.h"
30 #include "folly/Likely.h"
31 #include "folly/Range.h"
32 #include "folly/SafeAssert.h"
35 namespace symbolizer {
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
48 // Note: may throw, call openNoThrow() explicitly if you don't want to throw
49 explicit ElfFile(const char* name, bool readOnly=true);
52 // Returns 0 on success, -1 (and sets errno) on failure and (if msg is not
53 // NULL) sets *msg to a static string indicating what failed.
54 int openNoThrow(const char* name, bool readOnly=true,
55 const char** msg=nullptr) noexcept;
57 // Open the ELF file. Throws on error.
58 void open(const char* name, bool readOnly=true);
62 ElfFile(ElfFile&& other);
63 ElfFile& operator=(ElfFile&& other);
65 /** Retrieve the ELF header */
66 const ElfW(Ehdr)& elfHeader() const {
67 return at<ElfW(Ehdr)>(0);
71 * Get the base address, the address where the file should be loaded if
72 * no relocations happened.
74 uintptr_t getBaseAddress() const {
78 /** Find a section given its name */
79 const ElfW(Shdr)* getSectionByName(const char* name) const;
81 /** Find a section given its index in the section header table */
82 const ElfW(Shdr)* getSectionByIndex(size_t idx) const;
84 /** Retrieve the name of a section */
85 const char* getSectionName(const ElfW(Shdr)& section) const;
87 /** Get the actual section body */
88 folly::StringPiece getSectionBody(const ElfW(Shdr)& section) const;
90 /** Retrieve a string from a string table section */
91 const char* getString(const ElfW(Shdr)& stringTable, size_t offset) const;
94 * Iterate over all strings in a string table section for as long as
95 * fn(str) returns false.
96 * Returns the current ("found") string when fn returned true, or nullptr
97 * if fn returned false for all strings in the table.
100 const char* iterateStrings(const ElfW(Shdr)& stringTable, Fn fn) const;
103 * Iterate over all sections for as long as fn(section) returns false.
104 * Returns a pointer to the current ("found") section when fn returned
105 * true, or nullptr if fn returned false for all sections.
108 const ElfW(Shdr)* iterateSections(Fn fn) const;
111 * Iterate over all sections with a given type. Similar to
112 * iterateSections(), but filtered only for sections with the given type.
115 const ElfW(Shdr)* iterateSectionsWithType(uint32_t type, Fn fn) const;
118 * Iterate over all symbols witin a given section.
120 * Returns a pointer to the current ("found") symbol when fn returned true,
121 * or nullptr if fn returned false for all symbols.
124 const ElfW(Sym)* iterateSymbols(const ElfW(Shdr)& section, Fn fn) const;
126 const ElfW(Sym)* iterateSymbolsWithType(const ElfW(Shdr)& section,
127 uint32_t type, Fn fn) const;
130 * Find symbol definition by address.
131 * Note that this is the file virtual address, so you need to undo
132 * any relocation that might have happened.
134 * Returns {nullptr, nullptr} if not found.
136 typedef std::pair<const ElfW(Shdr)*, const ElfW(Sym)*> Symbol;
137 Symbol getDefinitionByAddress(uintptr_t address) const;
140 * Find symbol definition by name.
142 * If a symbol with this name cannot be found, a <nullptr, nullptr> Symbol
143 * will be returned. This is O(N) in the number of symbols in the file.
145 * Returns {nullptr, nullptr} if not found.
147 Symbol getSymbolByName(const char* name) const;
150 * Get the value of a symbol.
153 const T& getSymbolValue(const ElfW(Sym)* symbol) const {
154 const ElfW(Shdr)* section = getSectionByIndex(symbol->st_shndx);
155 FOLLY_SAFE_CHECK(section, "Symbol's section index is invalid");
157 return valueAt<T>(*section, symbol->st_value);
161 * Get the value of the object stored at the given address.
163 * This is the function that you want to use in conjunction with
164 * getSymbolValue() to follow pointers. For example, to get the value of
165 * a char* symbol, you'd do something like this:
167 * auto sym = getSymbolByName("someGlobalValue");
168 * auto addr = getSymbolValue<ElfW(Addr)>(sym.second);
169 * const char* str = &getSymbolValue<const char>(addr);
172 const T& getAddressValue(const ElfW(Addr) addr) const {
173 const ElfW(Shdr)* section = getSectionContainingAddress(addr);
174 FOLLY_SAFE_CHECK(section, "Address does not refer to existing section");
176 return valueAt<T>(*section, addr);
180 * Retrieve symbol name.
182 const char* getSymbolName(Symbol symbol) const;
184 /** Find the section containing the given address */
185 const ElfW(Shdr)* getSectionContainingAddress(ElfW(Addr) addr) const;
190 ElfFile(const ElfFile&) = delete;
191 ElfFile& operator=(const ElfFile&) = delete;
193 void validateStringTable(const ElfW(Shdr)& stringTable) const;
196 const typename std::enable_if<std::is_pod<T>::value, T>::type&
197 at(ElfW(Off) offset) const {
198 FOLLY_SAFE_CHECK(offset + sizeof(T) <= length_,
199 "Offset is not contained within our mmapped file");
201 return *reinterpret_cast<T*>(file_ + offset);
205 const T& valueAt(const ElfW(Shdr)& section, const ElfW(Addr) addr) const {
206 // For exectuables and shared objects, st_value holds a virtual address
207 // that refers to the memory owned by sections. Since we didn't map the
208 // sections into the addresses that they're expecting (sh_addr), but
209 // instead just mmapped the entire file directly, we need to translate
210 // between addresses and offsets into the file.
212 // TODO: For other file types, st_value holds a file offset directly. Since
213 // I don't have a use-case for that right now, just assert that
214 // nobody wants this. We can always add it later.
216 elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN,
217 "Only exectuables and shared objects are supported");
219 addr >= section.sh_addr &&
220 (addr + sizeof(T)) <= (section.sh_addr + section.sh_size),
221 "Address is not contained within the provided segment");
223 return at<T>(section.sh_offset + (addr - section.sh_addr));
227 char* file_; // mmap() location
228 size_t length_; // mmap() length
230 uintptr_t baseAddress_;
233 } // namespace symbolizer
236 #include "folly/experimental/symbolizer/Elf-inl.h"
238 #endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_ */