ElfFile::OpenResult ElfFile::init()

in folly/experimental/symbolizer/Elf.cpp [206:290]


ElfFile::OpenResult ElfFile::init() noexcept {
  if (length_ < 4) {
    return {kInvalidElfFile, "not an ELF file (too short)"};
  }

  std::array<char, 5> elfMagBuf = {{0, 0, 0, 0, 0}};
  if (::lseek(fd_, 0, SEEK_SET) != 0 || ::read(fd_, elfMagBuf.data(), 4) != 4) {
    return {kInvalidElfFile, "unable to read ELF file for magic number"};
  }
  if (std::strncmp(elfMagBuf.data(), ELFMAG, sizeof(ELFMAG)) != 0) {
    return {kInvalidElfFile, "invalid ELF magic"};
  }
  char c;
  if (::pread(fd_, &c, 1, length_ - 1) != 1) {
    auto msg =
        "The last bit of the mmaped memory is no longer valid. This may be "
        "caused by the original file being resized, "
        "deleted or otherwise modified.";
    return {kInvalidElfFile, msg};
  }

  if (::lseek(fd_, 0, SEEK_SET) != 0) {
    return {
        kInvalidElfFile,
        "unable to reset file descriptor after reading ELF magic number"};
  }

  auto& elfHeader = this->elfHeader();

#define EXPECTED_CLASS P1(ELFCLASS, FOLLY_ELF_NATIVE_CLASS)
#define P1(a, b) P2(a, b)
#define P2(a, b) a##b
  // Validate ELF class (32/64 bits)
  if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) {
    return {kInvalidElfFile, "invalid ELF class"};
  }
#undef P1
#undef P2
#undef EXPECTED_CLASS

  // Validate ELF data encoding (LSB/MSB)
  static constexpr auto kExpectedEncoding =
      kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB;
  if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) {
    return {kInvalidElfFile, "invalid ELF encoding"};
  }

  // Validate ELF version (1)
  if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||
      elfHeader.e_version != EV_CURRENT) {
    return {kInvalidElfFile, "invalid ELF version"};
  }

  // We only support executable and shared object files
  if (elfHeader.e_type != ET_EXEC && elfHeader.e_type != ET_DYN &&
      elfHeader.e_type != ET_CORE) {
    return {kInvalidElfFile, "invalid ELF file type"};
  }

  if (elfHeader.e_phnum == 0) {
    return {kInvalidElfFile, "no program header!"};
  }

  if (elfHeader.e_phentsize != sizeof(ElfPhdr)) {
    return {kInvalidElfFile, "invalid program header entry size"};
  }

  if (elfHeader.e_shentsize != sizeof(ElfShdr)) {
    if (elfHeader.e_shentsize != 0 || elfHeader.e_type != ET_CORE) {
      return {kInvalidElfFile, "invalid section header entry size"};
    }
  }

  // Program headers are sorted by load address, so the first PT_LOAD
  // header gives us the base address.
  const ElfPhdr* programHeader =
      iterateProgramHeaders([](auto& h) { return h.p_type == PT_LOAD; });

  if (!programHeader) {
    return {kInvalidElfFile, "could not find base address"};
  }
  baseAddress_ = programHeader->p_vaddr;

  return {kSuccess, nullptr};
}