2 * Copyright 2017 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.
20 #define FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
23 #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 {
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);
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
55 // Note: may throw, call openNoThrow() explicitly if you don't want to throw
56 explicit ElfFile(const char* name, bool readOnly = true);
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.
68 // Open the ELF file. Does not throw on error.
72 const char** msg = nullptr) noexcept;
74 // Like openNoThrow, but follow .gnu_debuglink if present
78 const char** msg = nullptr) noexcept;
80 // Open the ELF file. Throws on error.
81 void open(const char* name, bool readOnly = true);
85 ElfFile(ElfFile&& other) noexcept;
86 ElfFile& operator=(ElfFile&& other);
88 /** Retrieve the ELF header */
89 const ElfEhdr& elfHeader() const {
90 return at<ElfEhdr>(0);
94 * Get the base address, the address where the file should be loaded if
95 * no relocations happened.
97 uintptr_t getBaseAddress() const {
101 /** Find a section given its name */
102 const ElfShdr* getSectionByName(const char* name) const;
104 /** Find a section given its index in the section header table */
105 const ElfShdr* getSectionByIndex(size_t idx) const;
107 /** Retrieve the name of a section */
108 const char* getSectionName(const ElfShdr& section) const;
110 /** Get the actual section body */
111 folly::StringPiece getSectionBody(const ElfShdr& section) const;
113 /** Retrieve a string from a string table section */
114 const char* getString(const ElfShdr& stringTable, size_t offset) const;
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.
123 const char* iterateStrings(const ElfShdr& stringTable, Fn fn) const;
126 * Iterate over all sections for 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.
131 const ElfShdr* iterateSections(Fn fn) const;
134 * Iterate over all sections with a given type. Similar to
135 * iterateSections(), but filtered only for sections with the given type.
138 const ElfShdr* iterateSectionsWithType(uint32_t type, Fn fn) const;
141 * Iterate over all symbols witin a given section.
143 * Returns a pointer to the current ("found") symbol when fn returned true,
144 * or nullptr if fn returned false for all symbols.
147 const ElfSym* iterateSymbols(const ElfShdr& section, Fn fn) const;
150 iterateSymbolsWithType(const ElfShdr& section, uint32_t type, Fn fn) const;
153 * Find symbol definition by address.
154 * Note that this is the file virtual address, so you need to undo
155 * any relocation that might have happened.
157 * Returns {nullptr, nullptr} if not found.
159 typedef std::pair<const ElfShdr*, const ElfSym*> Symbol;
160 Symbol getDefinitionByAddress(uintptr_t address) const;
163 * Find symbol definition by name.
165 * If a symbol with this name cannot be found, a <nullptr, nullptr> Symbol
166 * will be returned. This is O(N) in the number of symbols in the file.
168 * Returns {nullptr, nullptr} if not found.
170 Symbol getSymbolByName(const char* name) const;
173 * Get the value of a symbol.
176 const T& getSymbolValue(const ElfSym* symbol) const {
177 const ElfShdr* section = getSectionByIndex(symbol->st_shndx);
178 FOLLY_SAFE_CHECK(section, "Symbol's section index is invalid");
180 return valueAt<T>(*section, symbol->st_value);
184 * Get the value of the object stored at the given address.
186 * This is the function that you want to use in conjunction with
187 * getSymbolValue() to follow pointers. For example, to get the value of
188 * a char* symbol, you'd do something like this:
190 * auto sym = getSymbolByName("someGlobalValue");
191 * auto addr = getSymbolValue<ElfAddr>(sym.second);
192 * const char* str = &getSymbolValue<const char>(addr);
195 const T& getAddressValue(const ElfAddr addr) const {
196 const ElfShdr* section = getSectionContainingAddress(addr);
197 FOLLY_SAFE_CHECK(section, "Address does not refer to existing section");
199 return valueAt<T>(*section, addr);
203 * Retrieve symbol name.
205 const char* getSymbolName(Symbol symbol) const;
207 /** Find the section containing the given address */
208 const ElfShdr* getSectionContainingAddress(ElfAddr addr) const;
211 bool init(const char** msg);
213 ElfFile(const ElfFile&) = delete;
214 ElfFile& operator=(const ElfFile&) = delete;
216 void validateStringTable(const ElfShdr& stringTable) const;
219 const typename std::enable_if<std::is_pod<T>::value, T>::type& at(
220 ElfOff offset) const {
222 offset + sizeof(T) <= length_,
223 "Offset is not contained within our mmapped file");
225 return *reinterpret_cast<T*>(file_ + offset);
229 const T& valueAt(const ElfShdr& section, const ElfAddr addr) const {
230 // For exectuables and shared objects, st_value holds a virtual address
231 // that refers to the memory owned by sections. Since we didn't map the
232 // sections into the addresses that they're expecting (sh_addr), but
233 // instead just mmapped the entire file directly, we need to translate
234 // between addresses and offsets into the file.
236 // TODO: For other file types, st_value holds a file offset directly. Since
237 // I don't have a use-case for that right now, just assert that
238 // nobody wants this. We can always add it later.
240 elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN,
241 "Only exectuables and shared objects are supported");
243 addr >= section.sh_addr &&
244 (addr + sizeof(T)) <= (section.sh_addr + section.sh_size),
245 "Address is not contained within the provided segment");
247 return at<T>(section.sh_offset + (addr - section.sh_addr));
251 char* file_; // mmap() location
252 size_t length_; // mmap() length
254 uintptr_t baseAddress_;
257 } // namespace symbolizer
260 #include <folly/experimental/symbolizer/Elf-inl.h>