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