Add missing uint32 type to folly::ProgramOptions::gFlagAdders
[folly.git] / folly / experimental / symbolizer / Elf.cpp
index 6b32dc1215bc1b83e2ea8664dd59a374b58124d8..cc643866d432a78528ad48ee757cc1029100dd2a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 #include <folly/experimental/symbolizer/Elf.h>
 
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
+#include <folly/portability/SysMman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include <string>
 
@@ -28,6 +26,7 @@
 
 #include <folly/Conv.h>
 #include <folly/Exception.h>
+#include <folly/ScopeGuard.h>
 
 namespace folly {
 namespace symbolizer {
@@ -57,15 +56,19 @@ void ElfFile::open(const char* name, bool readOnly) {
   }
 }
 
-int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
-  noexcept {
+int ElfFile::openNoThrow(const char* name,
+                         bool readOnly,
+                         const char** msg) noexcept {
   FOLLY_SAFE_CHECK(fd_ == -1, "File already open");
   fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
   if (fd_ == -1) {
     if (msg) *msg = "open";
     return kSystemError;
   }
-
+  // Always close fd and unmap in case of failure along the way to avoid
+  // check failure above if we leave fd != -1 and the object is recycled
+  // like it is inside SignalSafeElfCache
+  ScopeGuard guard = makeGuard([&]{ reset(); });
   struct stat st;
   int r = fstat(fd_, &st);
   if (r == -1) {
@@ -87,12 +90,49 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
     errno = EINVAL;
     return kInvalidElfFile;
   }
-
+  guard.dismiss();
   return kSuccess;
 }
 
+int ElfFile::openAndFollow(const char* name,
+                           bool readOnly,
+                           const char** msg) noexcept {
+  auto result = openNoThrow(name, readOnly, msg);
+  if (!readOnly || result != kSuccess) return result;
+
+  /* NOTE .gnu_debuglink specifies only the name of the debugging info file
+   * (with no directory components). GDB checks 3 different directories, but
+   * ElfFile only supports the first version:
+   *     - dirname(name)
+   *     - dirname(name) + /.debug/
+   *     - X/dirname(name)/ - where X is set in gdb's `debug-file-directory`.
+   */
+  auto dirend = strrchr(name, '/');
+  // include ending '/' if any.
+  auto dirlen = dirend != nullptr ? dirend + 1 - name : 0;
+
+  auto debuginfo = getSectionByName(".gnu_debuglink");
+  if (!debuginfo) return result;
+
+  // The section starts with the filename, with any leading directory
+  // components removed, followed by a zero byte.
+  auto debugFileName = getSectionBody(*debuginfo);
+  auto debugFileLen = strlen(debugFileName.begin());
+  if (dirlen + debugFileLen >= PATH_MAX) {
+    return result;
+  }
+
+  char linkname[PATH_MAX];
+  memcpy(linkname, name, dirlen);
+  memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1);
+  reset();
+  result = openNoThrow(linkname, readOnly, msg);
+  if (result == kSuccess) return result;
+  return openNoThrow(name, readOnly, msg);
+}
+
 ElfFile::~ElfFile() {
-  destroy();
+  reset();
 }
 
 ElfFile::ElfFile(ElfFile&& other) noexcept
@@ -108,7 +148,7 @@ ElfFile::ElfFile(ElfFile&& other) noexcept
 
 ElfFile& ElfFile::operator=(ElfFile&& other) {
   assert(this != &other);
-  destroy();
+  reset();
 
   fd_ = other.fd_;
   file_ = other.file_;
@@ -123,13 +163,15 @@ ElfFile& ElfFile::operator=(ElfFile&& other) {
   return *this;
 }
 
-void ElfFile::destroy() {
+void ElfFile::reset() {
   if (file_ != MAP_FAILED) {
     munmap(file_, length_);
+    file_ = static_cast<char*>(MAP_FAILED);
   }
 
   if (fd_ != -1) {
     close(fd_);
+    fd_ = -1;
   }
 }
 
@@ -158,18 +200,12 @@ bool ElfFile::init(const char** msg) {
 #undef EXPECTED_CLASS
 
   // Validate ELF data encoding (LSB/MSB)
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-# define EXPECTED_ENCODING ELFDATA2LSB
-#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-# define EXPECTED_ENCODING ELFDATA2MSB
-#else
-# error Unsupported byte order
-#endif
-  if (elfHeader.e_ident[EI_DATA] != EXPECTED_ENCODING) {
+  static constexpr auto kExpectedEncoding =
+      kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB;
+  if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) {
     if (msg) *msg = "invalid ELF encoding";
     return false;
   }
-#undef EXPECTED_ENCODING
 
   // Validate ELF version (1)
   if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||