Fix SignalHandlerTest with ASAN
[folly.git] / folly / experimental / symbolizer / Elf.h
index 256e359c1efc9bae347189bbd4c4a72580b00246..a4f5afd22553ca9045c2571bf5ff98b07dcdaf6e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 // ELF file parser
 
-#ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
+#pragma once
 #define FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_
 
-#include <stdio.h>
 #include <elf.h>
-#include <link.h>  // For ElfW()
+#include <link.h> // For ElfW()
 
+#include <cstdio>
+#include <initializer_list>
 #include <stdexcept>
 #include <system_error>
 
-#include "folly/Likely.h"
-#include "folly/Range.h"
-#include "folly/Conv.h"
+#include <folly/Conv.h>
+#include <folly/Likely.h>
+#include <folly/Range.h>
+#include <folly/lang/SafeAssert.h>
 
 namespace folly {
 namespace symbolizer {
 
-template <class... Args>
-inline void enforce(bool v, Args... args) {
-  if (UNLIKELY(!v)) {
-    throw std::runtime_error(folly::to<std::string>(args...));
-  }
-}
+using ElfAddr = ElfW(Addr);
+using ElfEhdr = ElfW(Ehdr);
+using ElfOff = ElfW(Off);
+using ElfPhdr = ElfW(Phdr);
+using ElfShdr = ElfW(Shdr);
+using ElfSym = ElfW(Sym);
 
 /**
  * ELF file parser.
@@ -49,16 +51,44 @@ inline void enforce(bool v, Args... args) {
  */
 class ElfFile {
  public:
-  ElfFile();
-  explicit ElfFile(const char* name, bool readOnly=true);
+  ElfFile() noexcept;
+
+  // Note: may throw, call openNoThrow() explicitly if you don't want to throw
+  explicit ElfFile(const char* name, bool readOnly = true);
+
+  // Open the ELF file.
+  // Returns 0 on success, kSystemError (guaranteed to be -1) (and sets errno)
+  // on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid
+  // Elf file. On error, if msg is not nullptr, sets *msg to a static string
+  // indicating what failed.
+  enum {
+    kSuccess = 0,
+    kSystemError = -1,
+    kInvalidElfFile = -2,
+  };
+  // Open the ELF file. Does not throw on error.
+  int openNoThrow(
+      const char* name,
+      bool readOnly = true,
+      const char** msg = nullptr) noexcept;
+
+  // Like openNoThrow, but follow .gnu_debuglink if present
+  int openAndFollow(
+      const char* name,
+      bool readOnly = true,
+      const char** msg = nullptr) noexcept;
+
+  // Open the ELF file. Throws on error.
+  void open(const char* name, bool readOnly = true);
+
   ~ElfFile();
 
-  ElfFile(ElfFile&& other);
+  ElfFile(ElfFile&& other) noexcept;
   ElfFile& operator=(ElfFile&& other);
 
   /** Retrieve the ELF header */
-  const ElfW(Ehdr)& elfHeader() const {
-    return at<ElfW(Ehdr)>(0);
+  const ElfEhdr& elfHeader() const {
+    return at<ElfEhdr>(0);
   }
 
   /**
@@ -70,19 +100,19 @@ class ElfFile {
   }
 
   /** Find a section given its name */
-  const ElfW(Shdr)* getSectionByName(const char* name) const;
+  const ElfShdr* getSectionByName(const char* name) const;
 
   /** Find a section given its index in the section header table */
-  const ElfW(Shdr)* getSectionByIndex(size_t idx) const;
+  const ElfShdr* getSectionByIndex(size_t idx) const;
 
   /** Retrieve the name of a section */
-  const char* getSectionName(const ElfW(Shdr)& section) const;
+  const char* getSectionName(const ElfShdr& section) const;
 
   /** Get the actual section body */
-  folly::StringPiece getSectionBody(const ElfW(Shdr)& section) const;
+  folly::StringPiece getSectionBody(const ElfShdr& section) const;
 
   /** Retrieve a string from a string table section */
-  const char* getString(const ElfW(Shdr)& stringTable, size_t offset) const;
+  const char* getString(const ElfShdr& stringTable, size_t offset) const;
 
   /**
    * Iterate over all strings in a string table section for as long as
@@ -91,7 +121,15 @@ class ElfFile {
    * if fn returned false for all strings in the table.
    */
   template <class Fn>
-  const char* iterateStrings(const ElfW(Shdr)& stringTable, Fn fn) const;
+  const char* iterateStrings(const ElfShdr& stringTable, Fn fn) const;
+
+  /**
+   * Iterate over program headers as long as fn(section) returns false.
+   * Returns a pointer to the current ("found") section when fn returned
+   * true, or nullptr if fn returned false for all sections.
+   */
+  template <class Fn>
+  const ElfPhdr* iterateProgramHeaders(Fn fn) const;
 
   /**
    * Iterate over all sections for as long as fn(section) returns false.
@@ -99,14 +137,23 @@ class ElfFile {
    * true, or nullptr if fn returned false for all sections.
    */
   template <class Fn>
-  const ElfW(Shdr)* iterateSections(Fn fn) const;
+  const ElfShdr* iterateSections(Fn fn) const;
 
   /**
-   * Iterate over all sections with a given type.  Similar to
+   * Iterate over all sections with a given type. Similar to
    * iterateSections(), but filtered only for sections with the given type.
    */
   template <class Fn>
-  const ElfW(Shdr)* iterateSectionsWithType(uint32_t type, Fn fn) const;
+  const ElfShdr* iterateSectionsWithType(uint32_t type, Fn fn) const;
+
+  /**
+   * Iterate over all sections with a given types. Similar to
+   * iterateSectionWithTypes(), but filtered on multiple types.
+   */
+  template <class Fn>
+  const ElfShdr* iterateSectionsWithTypes(
+      std::initializer_list<uint32_t> types,
+      Fn fn) const;
 
   /**
    * Iterate over all symbols witin a given section.
@@ -115,17 +162,24 @@ class ElfFile {
    * or nullptr if fn returned false for all symbols.
    */
   template <class Fn>
-  const ElfW(Sym)* iterateSymbols(const ElfW(Shdr)& section, Fn fn) const;
+  const ElfSym* iterateSymbols(const ElfShdr& section, Fn fn) const;
+  template <class Fn>
+  const ElfSym*
+  iterateSymbolsWithType(const ElfShdr& section, uint32_t type, Fn fn) const;
   template <class Fn>
-  const ElfW(Sym)* iterateSymbolsWithType(const ElfW(Shdr)& section,
-                                          uint32_t type, Fn fn) const;
+  const ElfSym* iterateSymbolsWithTypes(
+      const ElfShdr& section,
+      std::initializer_list<uint32_t> types,
+      Fn fn) const;
 
   /**
    * Find symbol definition by address.
    * Note that this is the file virtual address, so you need to undo
    * any relocation that might have happened.
+   *
+   * Returns {nullptr, nullptr} if not found.
    */
-  typedef std::pair<const ElfW(Shdr)*, const ElfW(Sym)*> Symbol;
+  typedef std::pair<const ElfShdr*, const ElfSym*> Symbol;
   Symbol getDefinitionByAddress(uintptr_t address) const;
 
   /**
@@ -133,6 +187,8 @@ class ElfFile {
    *
    * If a symbol with this name cannot be found, a <nullptr, nullptr> Symbol
    * will be returned. This is O(N) in the number of symbols in the file.
+   *
+   * Returns {nullptr, nullptr} if not found.
    */
   Symbol getSymbolByName(const char* name) const;
 
@@ -140,9 +196,9 @@ class ElfFile {
    * Get the value of a symbol.
    */
   template <class T>
-  const T& getSymbolValue(const ElfW(Sym)* symbol) const {
-    const ElfW(Shdr)* section = getSectionByIndex(symbol->st_shndx);
-    enforce(section, "Symbol's section index is invalid");
+  const T& getSymbolValue(const ElfSym* symbol) const {
+    const ElfShdr* section = getSectionByIndex(symbol->st_shndx);
+    FOLLY_SAFE_CHECK(section, "Symbol's section index is invalid");
 
     return valueAt<T>(*section, symbol->st_value);
   }
@@ -155,13 +211,13 @@ class ElfFile {
    * a char* symbol, you'd do something like this:
    *
    *  auto sym = getSymbolByName("someGlobalValue");
-   *  auto addr = getSymbolValue<ElfW(Addr)>(sym.second);
+   *  auto addr = getSymbolValue<ElfAddr>(sym.second);
    *  const char* str = &getSymbolValue<const char>(addr);
    */
   template <class T>
-  const T& getAddressValue(const ElfW(Addr) addr) const {
-    const ElfW(Shdr)* section = getSectionContainingAddress(addr);
-    enforce(section, "Address does not refer to existing section");
+  const T& getAddressValue(const ElfAddr addr) const {
+    const ElfShdr* section = getSectionContainingAddress(addr);
+    FOLLY_SAFE_CHECK(section, "Address does not refer to existing section");
 
     return valueAt<T>(*section, addr);
   }
@@ -172,27 +228,28 @@ class ElfFile {
   const char* getSymbolName(Symbol symbol) const;
 
   /** Find the section containing the given address */
-  const ElfW(Shdr)* getSectionContainingAddress(ElfW(Addr) addr) const;
+  const ElfShdr* getSectionContainingAddress(ElfAddr addr) const;
 
  private:
-  void init();
-  void destroy();
+  bool init(const char** msg);
+  void reset();
   ElfFile(const ElfFile&) = delete;
   ElfFile& operator=(const ElfFile&) = delete;
 
-  void validateStringTable(const ElfW(Shdr)& stringTable) const;
+  void validateStringTable(const ElfShdr& stringTable) const;
 
   template <class T>
-  const typename std::enable_if<std::is_pod<T>::value, T>::type&
-  at(ElfW(Off) offset) const {
-    enforce(offset + sizeof(T) <= length_,
-            "Offset is not contained within our mmapped file");
+  const typename std::enable_if<std::is_pod<T>::value, T>::type& at(
+      ElfOff offset) const {
+    FOLLY_SAFE_CHECK(
+        offset + sizeof(T) <= length_,
+        "Offset is not contained within our mmapped file");
 
     return *reinterpret_cast<T*>(file_ + offset);
   }
 
   template <class T>
-  const T& valueAt(const ElfW(Shdr)& section, const ElfW(Addr) addr) const {
+  const T& valueAt(const ElfShdr& section, const ElfAddr addr) const {
     // For exectuables and shared objects, st_value holds a virtual address
     // that refers to the memory owned by sections. Since we didn't map the
     // sections into the addresses that they're expecting (sh_addr), but
@@ -202,26 +259,25 @@ class ElfFile {
     // TODO: For other file types, st_value holds a file offset directly. Since
     //       I don't have a use-case for that right now, just assert that
     //       nobody wants this. We can always add it later.
-    enforce(elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN,
-            "Only exectuables and shared objects are supported");
-    enforce(addr >= section.sh_addr &&
+    FOLLY_SAFE_CHECK(
+        elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN,
+        "Only exectuables and shared objects are supported");
+    FOLLY_SAFE_CHECK(
+        addr >= section.sh_addr &&
             (addr + sizeof(T)) <= (section.sh_addr + section.sh_size),
-            "Address is not contained within the provided segment");
+        "Address is not contained within the provided segment");
 
     return at<T>(section.sh_offset + (addr - section.sh_addr));
   }
 
   int fd_;
-  char* file_;     // mmap() location
-  size_t length_;  // mmap() length
+  char* file_; // mmap() location
+  size_t length_; // mmap() length
 
   uintptr_t baseAddress_;
 };
 
-}  // namespace symbolizer
-}  // namespace folly
-
-#include "folly/experimental/symbolizer/Elf-inl.h"
-
-#endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_ELF_H_ */
+} // namespace symbolizer
+} // namespace folly
 
+#include <folly/experimental/symbolizer/Elf-inl.h>