logging: fix compiler compatibility for one more constexpr function
[folly.git] / folly / experimental / symbolizer / Elf.cpp
1 /*
2  * Copyright 2017-present 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 #include <folly/experimental/symbolizer/Elf.h>
17
18 #include <fcntl.h>
19 #include <folly/portability/SysMman.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22
23 #include <string>
24
25 #include <glog/logging.h>
26
27 #include <folly/Conv.h>
28 #include <folly/Exception.h>
29 #include <folly/ScopeGuard.h>
30
31 namespace folly {
32 namespace symbolizer {
33
34 ElfFile::ElfFile() noexcept
35   : fd_(-1),
36     file_(static_cast<char*>(MAP_FAILED)),
37     length_(0),
38     baseAddress_(0) {
39 }
40
41 ElfFile::ElfFile(const char* name, bool readOnly)
42   : fd_(-1),
43     file_(static_cast<char*>(MAP_FAILED)),
44     length_(0),
45     baseAddress_(0) {
46   open(name, readOnly);
47 }
48
49 void ElfFile::open(const char* name, bool readOnly) {
50   const char* msg = "";
51   int r = openNoThrow(name, readOnly, &msg);
52   if (r == kSystemError) {
53     throwSystemError(msg);
54   } else {
55     CHECK_EQ(r, kSuccess) << msg;
56   }
57 }
58
59 int ElfFile::openNoThrow(const char* name,
60                          bool readOnly,
61                          const char** msg) noexcept {
62   FOLLY_SAFE_CHECK(fd_ == -1, "File already open");
63   fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
64   if (fd_ == -1) {
65     if (msg) *msg = "open";
66     return kSystemError;
67   }
68   // Always close fd and unmap in case of failure along the way to avoid
69   // check failure above if we leave fd != -1 and the object is recycled
70   // like it is inside SignalSafeElfCache
71   ScopeGuard guard = makeGuard([&]{ reset(); });
72   struct stat st;
73   int r = fstat(fd_, &st);
74   if (r == -1) {
75     if (msg) *msg = "fstat";
76     return kSystemError;
77   }
78
79   length_ = st.st_size;
80   int prot = PROT_READ;
81   if (!readOnly) {
82     prot |= PROT_WRITE;
83   }
84   file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
85   if (file_ == MAP_FAILED) {
86     if (msg) *msg = "mmap";
87     return kSystemError;
88   }
89   if (!init(msg)) {
90     errno = EINVAL;
91     return kInvalidElfFile;
92   }
93   guard.dismiss();
94   return kSuccess;
95 }
96
97 int ElfFile::openAndFollow(const char* name,
98                            bool readOnly,
99                            const char** msg) noexcept {
100   auto result = openNoThrow(name, readOnly, msg);
101   if (!readOnly || result != kSuccess) return result;
102
103   /* NOTE .gnu_debuglink specifies only the name of the debugging info file
104    * (with no directory components). GDB checks 3 different directories, but
105    * ElfFile only supports the first version:
106    *     - dirname(name)
107    *     - dirname(name) + /.debug/
108    *     - X/dirname(name)/ - where X is set in gdb's `debug-file-directory`.
109    */
110   auto dirend = strrchr(name, '/');
111   // include ending '/' if any.
112   auto dirlen = dirend != nullptr ? dirend + 1 - name : 0;
113
114   auto debuginfo = getSectionByName(".gnu_debuglink");
115   if (!debuginfo) return result;
116
117   // The section starts with the filename, with any leading directory
118   // components removed, followed by a zero byte.
119   auto debugFileName = getSectionBody(*debuginfo);
120   auto debugFileLen = strlen(debugFileName.begin());
121   if (dirlen + debugFileLen >= PATH_MAX) {
122     return result;
123   }
124
125   char linkname[PATH_MAX];
126   memcpy(linkname, name, dirlen);
127   memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1);
128   reset();
129   result = openNoThrow(linkname, readOnly, msg);
130   if (result == kSuccess) return result;
131   return openNoThrow(name, readOnly, msg);
132 }
133
134 ElfFile::~ElfFile() {
135   reset();
136 }
137
138 ElfFile::ElfFile(ElfFile&& other) noexcept
139   : fd_(other.fd_),
140     file_(other.file_),
141     length_(other.length_),
142     baseAddress_(other.baseAddress_) {
143   other.fd_ = -1;
144   other.file_ = static_cast<char*>(MAP_FAILED);
145   other.length_ = 0;
146   other.baseAddress_ = 0;
147 }
148
149 ElfFile& ElfFile::operator=(ElfFile&& other) {
150   assert(this != &other);
151   reset();
152
153   fd_ = other.fd_;
154   file_ = other.file_;
155   length_ = other.length_;
156   baseAddress_ = other.baseAddress_;
157
158   other.fd_ = -1;
159   other.file_ = static_cast<char*>(MAP_FAILED);
160   other.length_ = 0;
161   other.baseAddress_ = 0;
162
163   return *this;
164 }
165
166 void ElfFile::reset() {
167   if (file_ != MAP_FAILED) {
168     munmap(file_, length_);
169     file_ = static_cast<char*>(MAP_FAILED);
170   }
171
172   if (fd_ != -1) {
173     close(fd_);
174     fd_ = -1;
175   }
176 }
177
178 bool ElfFile::init(const char** msg) {
179   auto& elfHeader = this->elfHeader();
180
181   // Validate ELF magic numbers
182   if (!(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
183         elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
184         elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
185         elfHeader.e_ident[EI_MAG3] == ELFMAG3)) {
186     if (msg) *msg = "invalid ELF magic";
187     return false;
188   }
189
190   // Validate ELF class (32/64 bits)
191 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
192 #define P1(a, b) P2(a, b)
193 #define P2(a, b) a ## b
194   if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) {
195     if (msg) *msg = "invalid ELF class";
196     return false;
197   }
198 #undef P1
199 #undef P2
200 #undef EXPECTED_CLASS
201
202   // Validate ELF data encoding (LSB/MSB)
203   static constexpr auto kExpectedEncoding =
204       kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB;
205   if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) {
206     if (msg) *msg = "invalid ELF encoding";
207     return false;
208   }
209
210   // Validate ELF version (1)
211   if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||
212       elfHeader.e_version != EV_CURRENT) {
213     if (msg) *msg = "invalid ELF version";
214     return false;
215   }
216
217   // We only support executable and shared object files
218   if (elfHeader.e_type != ET_EXEC && elfHeader.e_type != ET_DYN) {
219     if (msg) *msg = "invalid ELF file type";
220     return false;
221   }
222
223   if (elfHeader.e_phnum == 0) {
224     if (msg) *msg = "no program header!";
225     return false;
226   }
227
228   if (elfHeader.e_phentsize != sizeof(ElfW(Phdr))) {
229     if (msg) *msg = "invalid program header entry size";
230     return false;
231   }
232
233   if (elfHeader.e_shentsize != sizeof(ElfW(Shdr))) {
234     if (msg) *msg = "invalid section header entry size";
235   }
236
237   const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
238   bool foundBase = false;
239   for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
240     // Program headers are sorted by load address, so the first PT_LOAD
241     // header gives us the base address.
242     if (programHeader->p_type == PT_LOAD) {
243       baseAddress_ = programHeader->p_vaddr;
244       foundBase = true;
245       break;
246     }
247   }
248
249   if (!foundBase) {
250     if (msg) *msg =  "could not find base address";
251     return false;
252   }
253
254   return true;
255 }
256
257 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
258   FOLLY_SAFE_CHECK(idx < elfHeader().e_shnum, "invalid section index");
259   return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
260 }
261
262 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
263   return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
264 }
265
266 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
267   FOLLY_SAFE_CHECK(stringTable.sh_type == SHT_STRTAB,
268                    "invalid type for string table");
269
270   const char* start = file_ + stringTable.sh_offset;
271   // First and last bytes must be 0
272   FOLLY_SAFE_CHECK(stringTable.sh_size == 0 ||
273                    (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
274                    "invalid string table");
275 }
276
277 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
278   const {
279   validateStringTable(stringTable);
280   FOLLY_SAFE_CHECK(offset < stringTable.sh_size,
281                    "invalid offset in string table");
282
283   return file_ + stringTable.sh_offset + offset;
284 }
285
286 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
287   if (elfHeader().e_shstrndx == SHN_UNDEF) {
288     return nullptr;  // no section name string table
289   }
290
291   const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
292   return getString(sectionNames, section.sh_name);
293 }
294
295 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
296   if (elfHeader().e_shstrndx == SHN_UNDEF) {
297     return nullptr;  // no section name string table
298   }
299
300   // Find offset in the section name string table of the requested name
301   const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
302   const char* foundName = iterateStrings(
303       sectionNames,
304       [&] (const char* s) { return !strcmp(name, s); });
305   if (foundName == nullptr) {
306     return nullptr;
307   }
308
309   size_t offset = foundName - (file_ + sectionNames.sh_offset);
310
311   // Find section with the appropriate sh_name offset
312   const ElfW(Shdr)* foundSection = iterateSections(
313     [&](const ElfW(Shdr)& sh) {
314       if (sh.sh_name == offset) {
315         return true;
316       }
317       return false;
318     });
319   return foundSection;
320 }
321
322 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
323   Symbol foundSymbol {nullptr, nullptr};
324
325   auto findSection = [&](const ElfW(Shdr)& section) {
326     auto findSymbols = [&](const ElfW(Sym)& sym) {
327       if (sym.st_shndx == SHN_UNDEF) {
328         return false;  // not a definition
329       }
330       if (address >= sym.st_value && address < sym.st_value + sym.st_size) {
331         foundSymbol.first = &section;
332         foundSymbol.second = &sym;
333         return true;
334       }
335
336       return false;
337     };
338
339     return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
340       iterateSymbolsWithType(section, STT_FUNC, findSymbols);
341   };
342
343   // Try the .dynsym section first if it exists, it's smaller.
344   (iterateSectionsWithType(SHT_DYNSYM, findSection) ||
345    iterateSectionsWithType(SHT_SYMTAB, findSection));
346
347   return foundSymbol;
348 }
349
350 ElfFile::Symbol ElfFile::getSymbolByName(const char* name) const {
351   Symbol foundSymbol{nullptr, nullptr};
352
353   auto findSection = [&](const ElfW(Shdr)& section) -> bool {
354     // This section has no string table associated w/ its symbols; hence we
355     // can't get names for them
356     if (section.sh_link == SHN_UNDEF) {
357       return false;
358     }
359
360     auto findSymbols = [&](const ElfW(Sym)& sym) -> bool {
361       if (sym.st_shndx == SHN_UNDEF) {
362         return false;  // not a definition
363       }
364       if (sym.st_name == 0) {
365         return false;  // no name for this symbol
366       }
367       const char* sym_name = getString(
368         *getSectionByIndex(section.sh_link), sym.st_name);
369       if (strcmp(sym_name, name) == 0) {
370         foundSymbol.first = &section;
371         foundSymbol.second = &sym;
372         return true;
373       }
374
375       return false;
376     };
377
378     return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
379       iterateSymbolsWithType(section, STT_FUNC, findSymbols);
380   };
381
382   // Try the .dynsym section first if it exists, it's smaller.
383   iterateSectionsWithType(SHT_DYNSYM, findSection) ||
384     iterateSectionsWithType(SHT_SYMTAB, findSection);
385
386   return foundSymbol;
387 }
388
389 const ElfW(Shdr)* ElfFile::getSectionContainingAddress(ElfW(Addr) addr) const {
390   return iterateSections([&](const ElfW(Shdr)& sh) -> bool {
391     return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));
392   });
393 }
394
395 const char* ElfFile::getSymbolName(Symbol symbol) const {
396   if (!symbol.first || !symbol.second) {
397     return nullptr;
398   }
399
400   if (symbol.second->st_name == 0) {
401     return nullptr;  // symbol has no name
402   }
403
404   if (symbol.first->sh_link == SHN_UNDEF) {
405     return nullptr;  // symbol table has no strings
406   }
407
408   return getString(*getSectionByIndex(symbol.first->sh_link),
409                    symbol.second->st_name);
410 }
411
412 }  // namespace symbolizer
413 }  // namespace folly