33e20ddefa11745d28c13a4222c8903ccdef49f2
[folly.git] / folly / experimental / symbolizer / Elf.cpp
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
18 #include "folly/experimental/symbolizer/Elf.h"
19
20 #include <sys/mman.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24
25 #include <string>
26
27 #include <glog/logging.h>
28
29 #include "folly/Conv.h"
30 #include "folly/Exception.h"
31
32 namespace folly {
33 namespace symbolizer {
34
35 ElfFile::ElfFile()
36   : fd_(-1),
37     file_(static_cast<char*>(MAP_FAILED)),
38     length_(0),
39     baseAddress_(0) {
40 }
41
42 ElfFile::ElfFile(const char* name, bool readOnly)
43   : fd_(open(name, (readOnly) ? O_RDONLY : O_RDWR)),
44     file_(static_cast<char*>(MAP_FAILED)),
45     length_(0),
46     baseAddress_(0) {
47   if (fd_ == -1) {
48     folly::throwSystemError("open ", name);
49   }
50
51   struct stat st;
52   int r = fstat(fd_, &st);
53   if (r == -1) {
54     folly::throwSystemError("fstat");
55   }
56
57   length_ = st.st_size;
58   int prot = PROT_READ;
59   if (!readOnly) {
60     prot |= PROT_WRITE;
61   }
62   file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
63   if (file_ == MAP_FAILED) {
64     folly::throwSystemError("mmap");
65   }
66   init();
67 }
68
69 ElfFile::~ElfFile() {
70   destroy();
71 }
72
73 ElfFile::ElfFile(ElfFile&& other)
74   : fd_(other.fd_),
75     file_(other.file_),
76     length_(other.length_),
77     baseAddress_(other.baseAddress_) {
78   other.fd_ = -1;
79   other.file_ = static_cast<char*>(MAP_FAILED);
80   other.length_ = 0;
81   other.baseAddress_ = 0;
82 }
83
84 ElfFile& ElfFile::operator=(ElfFile&& other) {
85   assert(this != &other);
86   destroy();
87
88   fd_ = other.fd_;
89   file_ = other.file_;
90   length_ = other.length_;
91   baseAddress_ = other.baseAddress_;
92
93   other.fd_ = -1;
94   other.file_ = static_cast<char*>(MAP_FAILED);
95   other.length_ = 0;
96   other.baseAddress_ = 0;
97
98   return *this;
99 }
100
101 void ElfFile::destroy() {
102   if (file_ != MAP_FAILED) {
103     munmap(file_, length_);
104   }
105
106   if (fd_ != -1) {
107     close(fd_);
108   }
109 }
110
111 void ElfFile::init() {
112   auto& elfHeader = this->elfHeader();
113
114   // Validate ELF magic numbers
115   enforce(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
116           elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
117           elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
118           elfHeader.e_ident[EI_MAG3] == ELFMAG3,
119           "invalid ELF magic");
120
121   // Validate ELF class (32/64 bits)
122 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
123 #define P1(a, b) P2(a, b)
124 #define P2(a, b) a ## b
125   enforce(elfHeader.e_ident[EI_CLASS] == EXPECTED_CLASS,
126           "invalid ELF class");
127 #undef P1
128 #undef P2
129 #undef EXPECTED_CLASS
130
131   // Validate ELF data encoding (LSB/MSB)
132 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
133 # define EXPECTED_ENCODING ELFDATA2LSB
134 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
135 # define EXPECTED_ENCODING ELFDATA2MSB
136 #else
137 # error Unsupported byte order
138 #endif
139   enforce(elfHeader.e_ident[EI_DATA] == EXPECTED_ENCODING,
140           "invalid ELF encoding");
141 #undef EXPECTED_ENCODING
142
143   // Validate ELF version (1)
144   enforce(elfHeader.e_ident[EI_VERSION] == EV_CURRENT &&
145           elfHeader.e_version == EV_CURRENT,
146           "invalid ELF version");
147
148   // We only support executable and shared object files
149   enforce(elfHeader.e_type == ET_EXEC || elfHeader.e_type == ET_DYN,
150           "invalid ELF file type");
151
152   enforce(elfHeader.e_phnum != 0, "no program header!");
153   enforce(elfHeader.e_phentsize == sizeof(ElfW(Phdr)),
154           "invalid program header entry size");
155   enforce(elfHeader.e_shentsize == sizeof(ElfW(Shdr)),
156           "invalid section header entry size");
157
158   const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
159   bool foundBase = false;
160   for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
161     // Program headers are sorted by load address, so the first PT_LOAD
162     // header gives us the base address.
163     if (programHeader->p_type == PT_LOAD) {
164       baseAddress_ = programHeader->p_vaddr;
165       foundBase = true;
166       break;
167     }
168   }
169
170   enforce(foundBase, "could not find base address");
171 }
172
173 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
174   enforce(idx < elfHeader().e_shnum, "invalid section index");
175   return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
176 }
177
178 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
179   return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
180 }
181
182 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
183   enforce(stringTable.sh_type == SHT_STRTAB, "invalid type for string table");
184
185   const char* start = file_ + stringTable.sh_offset;
186   // First and last bytes must be 0
187   enforce(stringTable.sh_size == 0 ||
188           (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
189           "invalid string table");
190 }
191
192 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
193   const {
194   validateStringTable(stringTable);
195   enforce(offset < stringTable.sh_size, "invalid offset in string table");
196
197   return file_ + stringTable.sh_offset + offset;
198 }
199
200 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
201   if (elfHeader().e_shstrndx == SHN_UNDEF) {
202     return nullptr;  // no section name string table
203   }
204
205   const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
206   return getString(sectionNames, section.sh_name);
207 }
208
209 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
210   if (elfHeader().e_shstrndx == SHN_UNDEF) {
211     return nullptr;  // no section name string table
212   }
213
214   // Find offset in the section name string table of the requested name
215   const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
216   const char* foundName = iterateStrings(
217       sectionNames,
218       [&] (const char* s) { return !strcmp(name, s); });
219   if (foundName == nullptr) {
220     return nullptr;
221   }
222
223   size_t offset = foundName - (file_ + sectionNames.sh_offset);
224
225   // Find section with the appropriate sh_name offset
226   const ElfW(Shdr)* foundSection = iterateSections(
227     [&](const ElfW(Shdr)& sh) {
228       if (sh.sh_name == offset) {
229         return true;
230       }
231       return false;
232     });
233   return foundSection;
234 }
235
236 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
237   Symbol foundSymbol {nullptr, nullptr};
238
239   auto findSection = [&](const ElfW(Shdr)& section) {
240     auto findSymbols = [&](const ElfW(Sym)& sym) {
241       if (sym.st_shndx == SHN_UNDEF) {
242         return false;  // not a definition
243       }
244       if (address >= sym.st_value && address < sym.st_value + sym.st_size) {
245         foundSymbol.first = &section;
246         foundSymbol.second = &sym;
247         return true;
248       }
249
250       return false;
251     };
252
253     return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
254       iterateSymbolsWithType(section, STT_FUNC, findSymbols);
255   };
256
257   // Try the .dynsym section first if it exists, it's smaller.
258   (iterateSectionsWithType(SHT_DYNSYM, findSection) ||
259    iterateSectionsWithType(SHT_SYMTAB, findSection));
260
261   return foundSymbol;
262 }
263
264 ElfFile::Symbol ElfFile::getSymbolByName(const char* name) const {
265   Symbol foundSymbol{nullptr, nullptr};
266
267   auto findSection = [&](const ElfW(Shdr)& section) -> bool {
268     // This section has no string table associated w/ its symbols; hence we
269     // can't get names for them
270     if (section.sh_link == SHN_UNDEF) {
271       return false;
272     }
273
274     auto findSymbols = [&](const ElfW(Sym)& sym) -> bool {
275       if (sym.st_shndx == SHN_UNDEF) {
276         return false;  // not a definition
277       }
278       if (sym.st_name == 0) {
279         return false;  // no name for this symbol
280       }
281       const char* sym_name = getString(
282         *getSectionByIndex(section.sh_link), sym.st_name);
283       if (strcmp(sym_name, name) == 0) {
284         foundSymbol.first = &section;
285         foundSymbol.second = &sym;
286         return true;
287       }
288
289       return false;
290     };
291
292     return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
293       iterateSymbolsWithType(section, STT_FUNC, findSymbols);
294   };
295
296   // Try the .dynsym section first if it exists, it's smaller.
297   iterateSectionsWithType(SHT_DYNSYM, findSection) ||
298     iterateSectionsWithType(SHT_SYMTAB, findSection);
299
300   return foundSymbol;
301 }
302
303 const ElfW(Shdr)* ElfFile::getSectionContainingAddress(ElfW(Addr) addr) const {
304   return iterateSections([&](const ElfW(Shdr)& sh) -> bool {
305     return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));
306   });
307 }
308
309 const char* ElfFile::getSymbolName(Symbol symbol) const {
310   if (!symbol.first || !symbol.second) {
311     return nullptr;
312   }
313
314   if (symbol.second->st_name == 0) {
315     return nullptr;  // symbol has no name
316   }
317
318   if (symbol.first->sh_link == SHN_UNDEF) {
319     return nullptr;  // symbol table has no strings
320   }
321
322   return getString(*getSectionByIndex(symbol.first->sh_link),
323                    symbol.second->st_name);
324 }
325
326 }  // namespace symbolizer
327 }  // namespace folly
328