text: //===--- StaticBinaryELF.cpp ----------------------------------------------===//
  //
  // This source file is part of the Swift.org open source project
  //
  // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
  // Licensed under Apache License v2.0 with Runtime Library Exception
  //
  // See https://swift.org/LICENSE.txt for license information
  // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  //
  //===----------------------------------------------------------------------===//
  //
  // Parse a static ELF binary to implement lookupSymbol() address lookup.
  //
  //===----------------------------------------------------------------------===//
  
  #if defined(__ELF__) && defined(__linux__)
  
  #include "ImageInspection.h"
  #include "llvm/ADT/ArrayRef.h"
  #include "llvm/ADT/Optional.h"
  #include "llvm/ADT/StringRef.h"
  #include "swift/Basic/Lazy.h"
  #include <cassert>
  #include <string>
  #include <vector>
  #include <elf.h>
  #include <fcntl.h>
  #include <pthread.h>
  #include <unistd.h>
  #include <linux/limits.h>
  #include <sys/auxv.h>
  #include <sys/mman.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  
  using namespace std;
  using namespace llvm;
  
  #ifdef __LP64__
  #define ELFCLASS ELFCLASS64
  typedef Elf64_Ehdr Elf_Ehdr;
  typedef Elf64_Shdr Elf_Shdr;
  typedef Elf64_Phdr Elf_Phdr;
  typedef Elf64_Addr Elf_Addr;
  typedef Elf64_Word Elf_Word;
  typedef Elf64_Sym Elf_Sym;
  typedef Elf64_Section Elf_Section;
  #define ELF_ST_TYPE(x) ELF64_ST_TYPE(x)
  #else
  #define ELFCLASS ELFCLASS32
  typedef Elf32_Ehdr Elf_Ehdr;
  typedef Elf32_Shdr Elf_Shdr;
  typedef Elf32_Phdr Elf_Phdr;
  typedef Elf32_Addr Elf_Addr;
  typedef Elf32_Word Elf_Word;
  typedef Elf32_Sym Elf_Sym;
  typedef Elf32_Section Elf_Section;
  #define ELF_ST_TYPE(x) ELF32_ST_TYPE(x)
  #endif
  
  extern const Elf_Ehdr elfHeader asm("__ehdr_start");
  
  class StaticBinaryELF {
  
  private:
    // mmap a section of a file that might not be page aligned.
    class Mapping {
    public:
      void *mapping;
      size_t mapLength;
      off_t diff;
  
      Mapping(int fd, size_t fileSize, off_t offset, size_t length) {
        if (fd < 0 || offset + length > fileSize) {
          mapping = nullptr;
          mapLength = 0;
          diff = 0;
          return;
        }
        long pageSize = sysconf(_SC_PAGESIZE);
        long pageMask = ~(pageSize - 1);
  
        off_t alignedOffset = offset & pageMask;
        diff = (offset - alignedOffset);
        mapLength = diff + length;
        mapping = mmap(nullptr, mapLength, PROT_READ, MAP_PRIVATE, fd,
                       alignedOffset);
        if (mapping == MAP_FAILED) {
          mapping = nullptr;
          mapLength = 0;
          diff = 0;
        }
      }
  
      template<typename T>
      ArrayRef<T> data() {
        size_t elements = (mapLength - diff) / sizeof(T);
        const T *data = reinterpret_cast<T *>(reinterpret_cast<char *>(mapping)
                                              + diff);
        return ArrayRef<T>(data, elements);
      }
  
      ~Mapping() {
        if (mapping && mapLength > 0) {
          munmap(mapping, mapLength);
        }
      }
    };
  
    string fullPathName;
    const Elf_Phdr *programHeaders = nullptr;
    Optional<Mapping> symbolTable;
    Optional<Mapping> stringTable;
  
  public:
    StaticBinaryELF() {
      getExecutablePathName();
  
      programHeaders = reinterpret_cast<const Elf_Phdr *>(getauxval(AT_PHDR));
      if (programHeaders == nullptr) {
        return;
      }
      // If an interpreter is set in the program headers then this is a
      // dynamic executable and therefore not valid.
      for (size_t idx = 0; idx < elfHeader.e_phnum; idx++) {
        if (programHeaders[idx].p_type == PT_INTERP) {
          programHeaders = nullptr;
          return;
        }
      }
  
      if (!fullPathName.empty()) {
        mmapExecutable();
      }
    }
  
    const char *getPathName() {
      return fullPathName.empty() ? nullptr : fullPathName.c_str();
    }
  
    void *getSectionLoadAddress(const void *addr) {
      if (programHeaders) {
        auto searchAddr = reinterpret_cast<Elf_Addr>(addr);
  
        for (size_t idx = 0; idx < elfHeader.e_phnum; idx++) {
          auto header = &programHeaders[idx];
          if (header->p_type == PT_LOAD && searchAddr >= header->p_vaddr
              && searchAddr <= (header->p_vaddr + header->p_memsz)) {
            return reinterpret_cast<void *>(header->p_vaddr);
          }
        }
      }
      return nullptr;
    }
  
    // Lookup a function symbol by address.
    const Elf_Sym *findSymbol(const void *addr) {
      if (symbolTable.hasValue()) {
        auto searchAddr = reinterpret_cast<Elf_Addr>(addr);
        auto symbols = symbolTable->data<Elf_Sym>();
  
        for (size_t idx = 0; idx < symbols.size(); idx++) {
          auto symbol = &symbols[idx];
          if (ELF_ST_TYPE(symbol->st_info) == STT_FUNC
              && searchAddr >= symbol->st_value
              && searchAddr < (symbol->st_value + symbol->st_size)) {
            return symbol;
          }
        }
      }
      return nullptr;
    }
  
    const char *symbolName(const Elf_Sym *symbol) {
      if (stringTable.hasValue()) {
        auto strings = stringTable->data<char>();
        if (symbol->st_name < strings.size()) {
          return &strings[symbol->st_name];
        }
      }
      return nullptr;
    }
  
  private:
    // This is Linux specific - find the full path of the executable
    // by looking in /proc/self/maps for a mapping holding the current
    // address space. For a static binary it should only be mapping one
    // file anyway. Don't use /proc/self/exe as the symlink will be removed
    // if the main thread terminates - see proc(5).
    void getExecutablePathName() {
      uintptr_t address = (uintptr_t)&elfHeader;
  
      FILE *fp = fopen("/proc/self/maps", "r");
      if (!fp) {
        perror("Unable to open /proc/self/maps");
      } else {
        char *line = nullptr;
        size_t size = 0;
        // Format is: addrLo-addrHi perms offset dev inode pathname.
        // If the executable has been deleted the last column will be '(deleted)'.
        StringRef deleted = StringRef("(deleted)");
  
        while(getdelim(&line, &size, '\n', fp) != -1) {
          StringRef entry = StringRef(line).rsplit('\n').first;
          auto addrRange = entry.split(' ').first.split('-');
          unsigned long long low = strtoull(addrRange.first.str().c_str(),
                                            nullptr, 16);
          if (low == 0 || low > UINTPTR_MAX || address < (uintptr_t)low) {
            continue;
          }
  
          unsigned long long high = strtoull(addrRange.second.str().c_str(),
                                             nullptr, 16);
          if (high == 0 || high > UINTPTR_MAX || address > (uintptr_t)high) {
            continue;
          }
  
          auto fname = entry.split('/').second;
          if (fname.empty() || fname.endswith(deleted)) {
            continue;
          }
  
          fullPathName = "/" + fname.str();
          break;
        }
        if (line) {
          free(line);
        }
        fclose(fp);
      }
    }
  
  
    // Parse the ELF binary using mmap for the section headers, symbol table and
    // string table.
    void mmapExecutable() {
      struct stat buf;
      int fd = open(fullPathName.c_str(), O_RDONLY);
      if (fd < 0) {
        return;
      }
  
      if (fstat(fd, &buf) != 0) {
        close(fd);
        return;
      }
  
      // Map in the section headers.
      size_t sectionHeadersSize = (elfHeader.e_shentsize * elfHeader.e_shnum);
      Mapping sectionHeaders = Mapping(fd, buf.st_size, elfHeader.e_shoff,
                                       sectionHeadersSize);
      if (sectionHeaders.mapping) {
        auto headers = sectionHeaders.data<Elf_Shdr>();
        auto section = findSectionHeader(headers, SHT_SYMTAB);
        if (section) {
          assert(section->sh_entsize == sizeof(Elf_Sym));
          symbolTable.emplace(fd, buf.st_size, section->sh_offset,
                              section->sh_size);
          if (symbolTable->mapping == nullptr) {
            symbolTable.reset();
          }
        }
        section = findSectionHeader(headers, SHT_STRTAB);
        if (section) {
          stringTable.emplace(fd, buf.st_size, section->sh_offset,
                              section->sh_size);
          if (stringTable->mapping == nullptr) {
            stringTable.reset();
          }
        }
      }
      close(fd);
      return;
    }
  
    // Find the section header of a specified type in the section headers table.
    const Elf_Shdr *findSectionHeader(ArrayRef<Elf_Shdr> headers,
                                      Elf_Word sectionType) {
      assert(elfHeader.e_shnum == headers.size());
      for (size_t idx = 0; idx < headers.size(); idx++) {
        if (idx == elfHeader.e_shstrndx) {
          continue;
        }
        auto header = &headers[idx];
        if (header->sh_type == sectionType) {
          if (header->sh_entsize > 0 && header->sh_size % header->sh_entsize) {
            fprintf(stderr,
                    "section size is not a multiple of entrysize (%ld/%ld)\n",
                    header->sh_size, header->sh_entsize);
            return nullptr;
          }
          return header;
        }
      }
      return nullptr;
    }
  };
  
  
  static swift::Lazy<StaticBinaryELF> TheBinary;
  
  int
  swift::lookupSymbol(const void *address, SymbolInfo *info) {
    // The pointers returned point into the mmap()'d binary so keep the
    // object once instantiated.
    auto &binary = TheBinary.get();
  
    info->fileName = binary.getPathName();
    info->baseAddress = binary.getSectionLoadAddress(address);
  
    auto symbol = binary.findSymbol(address);
    if (symbol != nullptr) {
      info->symbolAddress = reinterpret_cast<void *>(symbol->st_value);
      info->symbolName = binary.symbolName(symbol);
    } else {
      info->symbolAddress = nullptr;
      info->symbolName = nullptr;
    }
  
    return 1;
  }
  
  #endif // defined(__ELF__) && defined(__linux__)
  