[Zucchini] Create elf disassembler.

Creates Disassembler that recognises and parses ELF format. For now, it only supports Intel architeture. Support for Arm will be added in follow-up CLs.

Change-Id: Ibdcf113b573f22844b6a1611c5ff6df46829b9b3
Reviewed-on: https://chromium-review.googlesource.com/1136841
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: Greg Thompson <grt@chromium.org>
Reviewed-by: Samuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582233}
NOKEYCHECK=True
GitOrigin-RevId: 3c64e078fea9f23e44939c25ca02cf05b72b2c40
diff --git a/BUILD.gn b/BUILD.gn
index 4e289e3..b15da9c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -11,12 +11,14 @@
 
   # Disable DEX on Windows Official Builds.
   _enable_dex = !(is_win && is_official_build)
+  _enable_elf = true
   _enable_win = true
 
   # Disable ZTF (Zucchini Text Format) on official builds it is for testing only.
   _enable_ztf = !is_official_build
   flags = [
     "ENABLE_DEX=$_enable_dex",
+    "ENABLE_ELF=$_enable_elf",
     "ENABLE_WIN=$_enable_win",
     "ENABLE_ZTF=$_enable_ztf",
   ]
@@ -42,6 +44,8 @@
     "disassembler.h",
     "disassembler_dex.cc",
     "disassembler_dex.h",
+    "disassembler_elf.cc",
+    "disassembler_elf.h",
     "disassembler_no_op.cc",
     "disassembler_no_op.h",
     "disassembler_win32.cc",
@@ -158,6 +162,7 @@
     "buffer_view_unittest.cc",
     "crc32_unittest.cc",
     "disassembler_dex_unittest.cc",
+    "disassembler_elf_unittest.cc",
     "disassembler_ztf_unittest.cc",
     "element_detection_unittest.cc",
     "encoded_view_unittest.cc",
diff --git a/abs32_utils.cc b/abs32_utils.cc
index ab4372c..b3fd9f4 100644
--- a/abs32_utils.cc
+++ b/abs32_utils.cc
@@ -136,10 +136,12 @@
   for (auto unit = abs32_rva_extractor_.GetNext(); unit.has_value();
        unit = abs32_rva_extractor_.GetNext()) {
     offset_t location = unit->location;
-    offset_t target = target_rva_to_offset_.Convert(unit->target_rva);
-    if (target == kInvalidOffset)
-      continue;
-    return Reference{location, target};
+    // |target| will not be dereferenced, so we don't worry about it
+    // exceeding |image_.size()| (in fact, there are valid cases where it
+    // does).
+    offset_t unsafe_target = target_rva_to_offset_.Convert(unit->target_rva);
+    if (unsafe_target < kOffsetBound)
+      return Reference{location, unsafe_target};
   }
   return base::nullopt;
 }
diff --git a/disassembler.cc b/disassembler.cc
index 012e5ef..b6a2748 100644
--- a/disassembler.cc
+++ b/disassembler.cc
@@ -8,6 +8,12 @@
 
 namespace zucchini {
 
+/******** EmptyReferenceReader ********/
+
+base::Optional<Reference> EmptyReferenceReader::GetNext() {
+  return base::nullopt;
+}
+
 /******** ReferenceGroup ********/
 
 std::unique_ptr<ReferenceReader> ReferenceGroup::GetReader(
diff --git a/disassembler.h b/disassembler.h
index 4f528ed..ab1dafb 100644
--- a/disassembler.h
+++ b/disassembler.h
@@ -17,6 +17,12 @@
 
 namespace zucchini {
 
+// A vacuous ReferenceReader that produces no references.
+class EmptyReferenceReader : public ReferenceReader {
+ public:
+  base::Optional<Reference> GetNext() override;
+};
+
 // Disassembler needs to be declared before ReferenceGroup because the latter
 // contains member pointers based on the former, and we use a compiler flag,
 // -fcomplete-member-pointers, which enforces that member pointer base types are
diff --git a/disassembler_elf.cc b/disassembler_elf.cc
new file mode 100644
index 0000000..75690e9
--- /dev/null
+++ b/disassembler_elf.cc
@@ -0,0 +1,424 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/zucchini/disassembler_elf.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "components/zucchini/abs32_utils.h"
+#include "components/zucchini/algorithm.h"
+#include "components/zucchini/buffer_source.h"
+
+namespace zucchini {
+
+namespace {
+
+// Determines whether |section| is a reloc section.
+template <class Traits>
+bool IsRelocSection(const typename Traits::Elf_Shdr& section) {
+  if (section.sh_size == 0)
+    return false;
+  if (section.sh_type == elf::SHT_REL) {
+    // Also validate |section.sh_entsize|, which gets used later.
+    return section.sh_entsize == sizeof(typename Traits::Elf_Rel);
+  }
+  if (section.sh_type == elf::SHT_RELA)
+    return section.sh_entsize == sizeof(typename Traits::Elf_Rela);
+  return false;
+}
+
+// Determines whether |section| is a section with executable code.
+template <class Traits>
+bool IsExecSection(const typename Traits::Elf_Shdr& section) {
+  return (section.sh_flags & elf::SHF_EXECINSTR) != 0;
+}
+
+}  // namespace
+
+/******** ELF32Traits ********/
+
+// static
+constexpr Bitness Elf32Traits::kBitness;
+constexpr elf::FileClass Elf32Traits::kIdentificationClass;
+
+/******** ELF32IntelTraits ********/
+
+// static
+constexpr ExecutableType Elf32IntelTraits::kExeType;
+const char Elf32IntelTraits::kExeTypeString[] = "ELF x86";
+constexpr elf::MachineArchitecture Elf32IntelTraits::kMachineValue;
+constexpr uint32_t Elf32IntelTraits::kRelType;
+
+/******** ELF64Traits ********/
+
+// static
+constexpr Bitness Elf64Traits::kBitness;
+constexpr elf::FileClass Elf64Traits::kIdentificationClass;
+
+/******** ELF64IntelTraits ********/
+
+// static
+constexpr ExecutableType Elf64IntelTraits::kExeType;
+const char Elf64IntelTraits::kExeTypeString[] = "ELF x64";
+constexpr elf::MachineArchitecture Elf64IntelTraits::kMachineValue;
+constexpr uint32_t Elf64IntelTraits::kRelType;
+
+/******** DisassemblerElf ********/
+
+// static.
+template <class Traits>
+bool DisassemblerElf<Traits>::QuickDetect(ConstBufferView image) {
+  BufferSource source(image);
+
+  // Do not consume the bytes for the magic value, as they are part of the
+  // header.
+  if (!source.CheckNextBytes({0x7F, 'E', 'L', 'F'}))
+    return false;
+
+  auto* header = source.GetPointer<typename Traits::Elf_Ehdr>();
+  if (!header)
+    return false;
+
+  if (header->e_ident[elf::EI_CLASS] != Traits::kIdentificationClass)
+    return false;
+
+  if (header->e_ident[elf::EI_DATA] != 1)  // Only ELFDATA2LSB is supported.
+    return false;
+
+  if (header->e_type != elf::ET_EXEC && header->e_type != elf::ET_DYN)
+    return false;
+
+  if (header->e_version != 1 || header->e_ident[elf::EI_VERSION] != 1)
+    return false;
+
+  if (header->e_machine != supported_architecture())
+    return false;
+
+  if (header->e_shentsize != sizeof(typename Traits::Elf_Shdr))
+    return false;
+
+  return true;
+}
+
+template <class Traits>
+DisassemblerElf<Traits>::~DisassemblerElf() = default;
+
+template <class Traits>
+ExecutableType DisassemblerElf<Traits>::GetExeType() const {
+  return Traits::kExeType;
+}
+
+template <class Traits>
+std::string DisassemblerElf<Traits>::GetExeTypeString() const {
+  return Traits::kExeTypeString;
+}
+
+// |num_equivalence_iterations_| = 2 for reloc -> abs32.
+template <class Traits>
+DisassemblerElf<Traits>::DisassemblerElf() : Disassembler(2) {}
+
+template <class Traits>
+bool DisassemblerElf<Traits>::Parse(ConstBufferView image) {
+  image_ = image;
+  if (!ParseHeader())
+    return false;
+  ParseSections();
+  return true;
+}
+
+template <class Traits>
+std::unique_ptr<ReferenceReader> DisassemblerElf<Traits>::MakeReadRelocs(
+    offset_t lo,
+    offset_t hi) {
+  DCHECK_LE(lo, hi);
+  DCHECK_LE(hi, image_.size());
+
+  if (reloc_section_dims_.empty())
+    return std::make_unique<EmptyReferenceReader>();
+
+  return std::make_unique<RelocReaderElf>(
+      image_, Traits::kBitness, reloc_section_dims_,
+      supported_relocation_type(), lo, hi, translator_);
+}
+
+template <class Traits>
+std::unique_ptr<ReferenceWriter> DisassemblerElf<Traits>::MakeWriteRelocs(
+    MutableBufferView image) {
+  return std::make_unique<RelocWriterElf>(image, Traits::kBitness, translator_);
+}
+
+template <class Traits>
+std::unique_ptr<ReferenceReader> DisassemblerElf<Traits>::MakeReadAbs32(
+    offset_t lo,
+    offset_t hi) {
+  Abs32RvaExtractorWin32 abs_rva_extractor(image_, {Traits::kBitness, 0},
+                                           abs32_locations_, lo, hi);
+  return std::make_unique<Abs32ReaderWin32>(std::move(abs_rva_extractor),
+                                            translator_);
+}
+
+template <class Traits>
+std::unique_ptr<ReferenceWriter> DisassemblerElf<Traits>::MakeWriteAbs32(
+    MutableBufferView image) {
+  return std::make_unique<Abs32WriterWin32>(
+      image, AbsoluteAddress(Traits::kBitness, 0), translator_);
+}
+
+template <class Traits>
+bool DisassemblerElf<Traits>::ParseHeader() {
+  BufferSource source(image_);
+
+  // Ensures |header_| is valid later on.
+  if (!QuickDetect(image_))
+    return false;
+
+  header_ = source.GetPointer<typename Traits::Elf_Ehdr>();
+
+  sections_count_ = header_->e_shnum;
+  source = std::move(BufferSource(image_).Skip(header_->e_shoff));
+  sections_ = source.GetArray<typename Traits::Elf_Shdr>(sections_count_);
+  if (!sections_)
+    return false;
+  offset_t section_table_end =
+      base::checked_cast<offset_t>(source.begin() - image_.begin());
+
+  segments_count_ = header_->e_phnum;
+  source = std::move(BufferSource(image_).Skip(header_->e_phoff));
+  segments_ = source.GetArray<typename Traits::Elf_Phdr>(segments_count_);
+  if (!segments_)
+    return false;
+  offset_t segment_table_end =
+      base::checked_cast<offset_t>(source.begin() - image_.begin());
+
+  // Check string section -- even though we've stopped using them.
+  elf::Elf32_Half string_section_id = header_->e_shstrndx;
+  if (string_section_id >= sections_count_)
+    return false;
+  size_t section_names_size = sections_[string_section_id].sh_size;
+  if (section_names_size > 0) {
+    // If nonempty, then last byte of string section must be null.
+    const char* section_names = nullptr;
+    source = std::move(
+        BufferSource(image_).Skip(sections_[string_section_id].sh_offset));
+    section_names = source.GetArray<char>(section_names_size);
+    if (!section_names || section_names[section_names_size - 1] != '\0')
+      return false;
+  }
+
+  // Establish bound on encountered offsets.
+  offset_t offset_bound = std::max(section_table_end, segment_table_end);
+
+  // Visit each section, validate, and add address translation data to |units|.
+  std::vector<AddressTranslator::Unit> units;
+  units.reserve(sections_count_);
+
+  for (int i = 0; i < sections_count_; ++i) {
+    const typename Traits::Elf_Shdr* section = &sections_[i];
+
+    // Skip empty sections. These don't affect |offset_bound|, and don't
+    // contribute to RVA-offset mapping.
+    if (section->sh_size == 0)
+      continue;
+
+    // Be lax with RVAs: Assume they fit in int32_t, even for 64-bit. If
+    // assumption fails, simply skip the section with warning.
+    if (!RangeIsBounded(section->sh_addr, section->sh_size, kRvaBound) ||
+        !RangeIsBounded(section->sh_offset, section->sh_size, kOffsetBound)) {
+      LOG(WARNING) << "Section " << i << " does not fit in int32_t.";
+      continue;
+    }
+
+    // Extract dimensions to 32-bit integers to facilitate conversion. Range of
+    // values was ensured above when checking that the section is bounded.
+    uint32_t sh_size = base::checked_cast<uint32_t>(section->sh_size);
+    offset_t sh_offset = base::checked_cast<offset_t>(section->sh_offset);
+    rva_t sh_addr = base::checked_cast<rva_t>(section->sh_addr);
+
+    // Update |offset_bound|.
+    if (section->sh_type != elf::SHT_NOBITS) {
+      // Be strict with offsets: Any size overflow invalidates the file.
+      if (!image_.covers({sh_offset, sh_size}))
+        return false;
+
+      offset_t section_end = sh_offset + sh_size;
+      offset_bound = std::max(offset_bound, section_end);
+    }
+
+    // Compute mappings to translate between RVA and offset. As a heuristic,
+    // sections with RVA == 0 (i.e., |sh_addr == 0|) are ignored because these
+    // tend to be duplicates (which cause problems during lookup), and tend to
+    // be uninteresting.
+    if (section->sh_addr > 0) {
+      // Add |section| data for offset-RVA translation.
+      units.push_back({sh_offset, sh_size, sh_addr, sh_size});
+    }
+  }
+
+  // Initialize |translator_| for offset-RVA translations. Any inconsistency
+  // (e.g., 2 offsets correspond to the same RVA) would invalidate the ELF file.
+  if (translator_.Initialize(std::move(units)) != AddressTranslator::kSuccess)
+    return false;
+
+  // Visits |segments_| to get better estimate on |offset_bound|.
+  for (const typename Traits::Elf_Phdr* segment = segments_;
+       segment != segments_ + segments_count_; ++segment) {
+    if (!RangeIsBounded(segment->p_offset, segment->p_filesz, kOffsetBound))
+      return false;
+    offset_t segment_end = segment->p_offset + segment->p_filesz;
+    offset_bound = std::max(offset_bound, segment_end);
+  }
+
+  if (offset_bound > image_.size())
+    return false;
+  image_.shrink(offset_bound);
+
+  return true;
+}
+
+template <class Traits>
+void DisassemblerElf<Traits>::ExtractInterestingSectionHeaders() {
+  DCHECK(reloc_section_dims_.empty());
+  DCHECK(exec_headers_.empty());
+  for (elf::Elf32_Half i = 0; i < sections_count_; ++i) {
+    const typename Traits::Elf_Shdr* section = sections_ + i;
+    if (IsRelocSection<Traits>(*section))
+      reloc_section_dims_.emplace_back(*section);
+    else if (IsExecSection<Traits>(*section))
+      exec_headers_.push_back(section);
+  }
+  auto comp = [](const typename Traits::Elf_Shdr* a,
+                 const typename Traits::Elf_Shdr* b) {
+    return a->sh_offset < b->sh_offset;
+  };
+  std::sort(reloc_section_dims_.begin(), reloc_section_dims_.end());
+  std::sort(exec_headers_.begin(), exec_headers_.end(), comp);
+}
+
+template <class Traits>
+void DisassemblerElf<Traits>::GetAbs32FromRelocSections() {
+  constexpr int kAbs32Width = 4;
+  DCHECK(abs32_locations_.empty());
+  auto relocs = MakeReadRelocs(0, offset_t(size()));
+  for (auto ref = relocs->GetNext(); ref; ref = relocs->GetNext()) {
+    // Reject null targets and targets outside |image_|. Note that here we
+    // assume abs32 targets are never "fake offsets".
+    if (ref->target > 0 && image_.covers({ref->target, kAbs32Width}))
+      abs32_locations_.push_back(ref->target);
+  }
+  abs32_locations_.shrink_to_fit();
+  std::sort(abs32_locations_.begin(), abs32_locations_.end());
+
+  // Abs32 reference bodies must not overlap. If found, simply remove them.
+  size_t num_removed =
+      RemoveOverlappingAbs32Locations(Traits::kBitness, &abs32_locations_);
+  if (num_removed) {
+    LOG(WARNING) << "Warning: Found and removed " << num_removed
+                 << " abs32 locations with overlapping bodies.";
+  }
+}
+
+template <class Traits>
+void DisassemblerElf<Traits>::GetRel32FromCodeSections() {
+  for (const typename Traits::Elf_Shdr* section : exec_headers_)
+    ParseExecSection(*section);
+  PostProcessRel32();
+}
+
+template <class Traits>
+void DisassemblerElf<Traits>::ParseSections() {
+  ExtractInterestingSectionHeaders();
+  GetAbs32FromRelocSections();
+  GetRel32FromCodeSections();
+}
+
+/******** DisassemblerElfIntel ********/
+
+template <class Traits>
+DisassemblerElfIntel<Traits>::DisassemblerElfIntel() = default;
+
+template <class Traits>
+DisassemblerElfIntel<Traits>::~DisassemblerElfIntel() = default;
+
+template <class Traits>
+std::vector<ReferenceGroup> DisassemblerElfIntel<Traits>::MakeReferenceGroups()
+    const {
+  return {{ReferenceTypeTraits{4, TypeTag(kReloc), PoolTag(kReloc)},
+           &DisassemblerElfIntel<Traits>::MakeReadRelocs,
+           &DisassemblerElfIntel<Traits>::MakeWriteRelocs},
+          {ReferenceTypeTraits{4, TypeTag(kAbs32), PoolTag(kAbs32)},
+           &DisassemblerElfIntel<Traits>::MakeReadAbs32,
+           &DisassemblerElfIntel<Traits>::MakeWriteAbs32},
+          {ReferenceTypeTraits{4, TypeTag(kRel32), PoolTag(kRel32)},
+           &DisassemblerElfIntel<Traits>::MakeReadRel32,
+           &DisassemblerElfIntel<Traits>::MakeWriteRel32}};
+}
+
+template <class Traits>
+void DisassemblerElfIntel<Traits>::ParseExecSection(
+    const typename Traits::Elf_Shdr& section) {
+  ConstBufferView& image_ = this->image_;
+  auto& abs32_locations_ = this->abs32_locations_;
+
+  std::ptrdiff_t from_offset_to_rva = section.sh_addr - section.sh_offset;
+  rva_t start_rva = section.sh_addr;
+  rva_t end_rva = start_rva + section.sh_size;
+
+  AddressTranslator::RvaToOffsetCache target_rva_checker(this->translator_);
+
+  ConstBufferView region(image_.begin() + section.sh_offset, section.sh_size);
+  Abs32GapFinder gap_finder(image_, region, abs32_locations_, 4);
+  std::unique_ptr<Rel32FinderIntel> finder =
+      std::make_unique<typename Traits::Rel32FinderUse>(image_);
+  for (auto gap = gap_finder.GetNext(); gap.has_value();
+       gap = gap_finder.GetNext()) {
+    finder->Reset(gap.value());
+    for (auto rel32 = finder->GetNext(); rel32.has_value();
+         rel32 = finder->GetNext()) {
+      offset_t rel32_offset = offset_t(rel32->location - image_.begin());
+      rva_t rel32_rva = rva_t(rel32_offset + from_offset_to_rva);
+      rva_t target_rva = rel32_rva + 4 + image_.read<uint32_t>(rel32_offset);
+      if (target_rva_checker.IsValid(target_rva) &&
+          (rel32->can_point_outside_section ||
+           (start_rva <= target_rva && target_rva < end_rva))) {
+        finder->Accept();
+        rel32_locations_.push_back(rel32_offset);
+      }
+    }
+  }
+}
+
+template <class Traits>
+void DisassemblerElfIntel<Traits>::PostProcessRel32() {
+  rel32_locations_.shrink_to_fit();
+  std::sort(rel32_locations_.begin(), rel32_locations_.end());
+}
+
+template <class Traits>
+std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadRel32(
+    offset_t lo,
+    offset_t hi) {
+  return std::make_unique<Rel32ReaderX86>(this->image_, lo, hi,
+                                          &rel32_locations_, this->translator_);
+}
+
+template <class Traits>
+std::unique_ptr<ReferenceWriter> DisassemblerElfIntel<Traits>::MakeWriteRel32(
+    MutableBufferView image) {
+  return std::make_unique<Rel32WriterX86>(image, this->translator_);
+}
+
+// Explicit instantiation for supported classes.
+template class DisassemblerElfIntel<Elf32IntelTraits>;
+template class DisassemblerElfIntel<Elf64IntelTraits>;
+template bool DisassemblerElf<Elf32IntelTraits>::QuickDetect(
+    ConstBufferView image);
+template bool DisassemblerElf<Elf64IntelTraits>::QuickDetect(
+    ConstBufferView image);
+
+}  // namespace zucchini
diff --git a/disassembler_elf.h b/disassembler_elf.h
new file mode 100644
index 0000000..a5b0a72
--- /dev/null
+++ b/disassembler_elf.h
@@ -0,0 +1,189 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_
+#define COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/zucchini/address_translator.h"
+#include "components/zucchini/buffer_view.h"
+#include "components/zucchini/disassembler.h"
+#include "components/zucchini/image_utils.h"
+#include "components/zucchini/rel32_finder.h"
+#include "components/zucchini/rel32_utils.h"
+#include "components/zucchini/reloc_elf.h"
+#include "components/zucchini/type_elf.h"
+
+namespace zucchini {
+
+struct Elf32Traits {
+  static constexpr Bitness kBitness = kBit32;
+  static constexpr elf::FileClass kIdentificationClass = elf::ELFCLASS32;
+  using Elf_Shdr = elf::Elf32_Shdr;
+  using Elf_Phdr = elf::Elf32_Phdr;
+  using Elf_Ehdr = elf::Elf32_Ehdr;
+  using Elf_Rel = elf::Elf32_Rel;
+  using Elf_Rela = elf::Elf32_Rela;
+};
+
+// Architecture-specific definitions.
+struct Elf32IntelTraits : public Elf32Traits {
+  static constexpr ExecutableType kExeType = kExeTypeElfX86;
+  static const char kExeTypeString[];
+  static constexpr elf::MachineArchitecture kMachineValue = elf::EM_386;
+  static constexpr uint32_t kRelType = elf::R_386_RELATIVE;
+  using Rel32FinderUse = Rel32FinderX86;
+};
+
+struct Elf64Traits {
+  static constexpr Bitness kBitness = kBit64;
+  static constexpr elf::FileClass kIdentificationClass = elf::ELFCLASS64;
+  using Elf_Shdr = elf::Elf64_Shdr;
+  using Elf_Phdr = elf::Elf64_Phdr;
+  using Elf_Ehdr = elf::Elf64_Ehdr;
+  using Elf_Rel = elf::Elf64_Rel;
+  using Elf_Rela = elf::Elf64_Rela;
+};
+
+// Architecture-specific definitions.
+struct Elf64IntelTraits : public Elf64Traits {
+  static constexpr ExecutableType kExeType = kExeTypeElfX64;
+  static const char kExeTypeString[];
+  static constexpr elf::MachineArchitecture kMachineValue = elf::EM_X86_64;
+  static constexpr uint32_t kRelType = elf::R_X86_64_RELATIVE;
+  using Rel32FinderUse = Rel32FinderX64;
+};
+
+// Disassembler for ELF.
+template <class Traits>
+class DisassemblerElf : public Disassembler {
+ public:
+  using HeaderVector = std::vector<const typename Traits::Elf_Shdr*>;
+
+  // Applies quick checks to determine whether |image| *may* point to the start
+  // of an executable. Returns true iff the check passes.
+  static bool QuickDetect(ConstBufferView image);
+
+  ~DisassemblerElf() override;
+
+  // Disassembler:
+  ExecutableType GetExeType() const override;
+  std::string GetExeTypeString() const override;
+  std::vector<ReferenceGroup> MakeReferenceGroups() const override = 0;
+
+  // Find/Receive functions that are common among different architectures.
+  std::unique_ptr<ReferenceReader> MakeReadRelocs(offset_t lo, offset_t hi);
+  std::unique_ptr<ReferenceWriter> MakeWriteRelocs(MutableBufferView image);
+  std::unique_ptr<ReferenceReader> MakeReadAbs32(offset_t lo, offset_t hi);
+  std::unique_ptr<ReferenceWriter> MakeWriteAbs32(MutableBufferView image);
+
+  const AddressTranslator& translator() const { return translator_; }
+
+ protected:
+  friend Disassembler;
+
+  DisassemblerElf();
+
+  bool Parse(ConstBufferView image) override;
+
+  // Returns the supported Elf_Ehdr::e_machine enum.
+  static constexpr elf::MachineArchitecture supported_architecture() {
+    return Traits::kMachineValue;
+  }
+
+  // Returns the type to look for in the reloc section.
+  static constexpr uint32_t supported_relocation_type() {
+    return Traits::kRelType;
+  }
+
+  // Performs architecture-specific parsing of an executable section, to extract
+  // rel32 references.
+  virtual void ParseExecSection(const typename Traits::Elf_Shdr& section) = 0;
+
+  // Processes rel32 data after they are extracted from executable sections.
+  virtual void PostProcessRel32() = 0;
+
+  // The parsing routines below return true on success, and false on failure.
+
+  // Parses ELF header and section headers, and performs basic validation.
+  bool ParseHeader();
+
+  // Extracts and stores section headers that we need.
+  void ExtractInterestingSectionHeaders();
+
+  // Parsing functions that extract references from various sections.
+  void GetAbs32FromRelocSections();
+  void GetRel32FromCodeSections();
+  void ParseSections();
+
+  // Main ELF header.
+  const typename Traits::Elf_Ehdr* header_ = nullptr;
+
+  // Section header table, ordered by section id.
+  elf::Elf32_Half sections_count_ = 0;
+  const typename Traits::Elf_Shdr* sections_ = nullptr;
+
+  // Program header table.
+  elf::Elf32_Half segments_count_ = 0;
+  const typename Traits::Elf_Phdr* segments_ = nullptr;
+
+  // Translator between offsets and RVAs.
+  AddressTranslator translator_;
+
+  // Identity translator for abs32 translation.
+  AddressTranslator identity_translator_;
+
+  // Extracted relocation section dimensions data, sorted by file offsets.
+  std::vector<SectionDimensionsElf> reloc_section_dims_;
+
+  // Headers of executable sections, sorted by file offsets of the data each
+  // header points to.
+  std::vector<const typename Traits::Elf_Shdr*> exec_headers_;
+
+  // Sorted file offsets of abs32 locations.
+  std::vector<offset_t> abs32_locations_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DisassemblerElf);
+};
+
+// Disassembler for ELF with Intel architectures.
+template <class Traits>
+class DisassemblerElfIntel : public DisassemblerElf<Traits> {
+ public:
+  enum ReferenceType : uint8_t { kReloc, kAbs32, kRel32, kTypeCount };
+
+  DisassemblerElfIntel();
+  ~DisassemblerElfIntel() override;
+
+  // Disassembler:
+  std::vector<ReferenceGroup> MakeReferenceGroups() const override;
+
+  // DisassemblerElf:
+  void ParseExecSection(const typename Traits::Elf_Shdr& section) override;
+  void PostProcessRel32() override;
+
+  // Specialized Find/Receive functions.
+  std::unique_ptr<ReferenceReader> MakeReadRel32(offset_t lo, offset_t hi);
+  std::unique_ptr<ReferenceWriter> MakeWriteRel32(MutableBufferView image);
+
+ private:
+  // Sorted file offsets of rel32 locations.
+  std::vector<offset_t> rel32_locations_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisassemblerElfIntel);
+};
+
+using DisassemblerElfX86 = DisassemblerElfIntel<Elf32IntelTraits>;
+using DisassemblerElfX64 = DisassemblerElfIntel<Elf64IntelTraits>;
+
+}  // namespace zucchini
+
+#endif  // COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_
diff --git a/disassembler_elf_unittest.cc b/disassembler_elf_unittest.cc
new file mode 100644
index 0000000..2db8635
--- /dev/null
+++ b/disassembler_elf_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/zucchini/disassembler_elf.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <random>
+#include <vector>
+
+#include "components/zucchini/test_utils.h"
+#include "components/zucchini/type_elf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace zucchini {
+
+TEST(DisassemblerElfTest, QuickDetect) {
+  std::vector<uint8_t> image_data;
+  ConstBufferView image;
+
+  // Empty.
+  EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+  EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+
+  // Unrelated.
+  image_data = ParseHexString("DE AD");
+  image = {image_data.data(), image_data.size()};
+  EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+  EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+
+  // Only Magic.
+  image_data = ParseHexString("7F 45 4C 46");
+  image = {image_data.data(), image_data.size()};
+  EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+  EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+
+  // Only identification.
+  image_data =
+      ParseHexString("7F 45 4C 46 01 01 01 00 00 00 00 00 00 00 00 00");
+  image = {image_data.data(), image_data.size()};
+  EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+  EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+
+  // Large enough, filled with zeros.
+  image_data.assign(sizeof(elf::Elf32_Ehdr), 0);
+  image = {image_data.data(), image_data.size()};
+  EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+  EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+
+  // Random.
+  std::random_device rd;
+  std::mt19937 gen{rd()};
+  std::generate(image_data.begin(), image_data.end(), gen);
+  image = {image_data.data(), image_data.size()};
+  EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+  EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+
+  // Typical x86 elf header.
+  {
+    elf::Elf32_Ehdr header = {};
+    auto e_ident =
+        ParseHexString("7F 45 4C 46 01 01 01 00 00 00 00 00 00 00 00 00");
+    std::copy(e_ident.begin(), e_ident.end(), header.e_ident);
+    header.e_type = elf::ET_EXEC;
+    header.e_machine = elf::EM_386;
+    header.e_version = 1;
+    header.e_shentsize = sizeof(elf::Elf32_Shdr);
+    ConstBufferView image(reinterpret_cast<const uint8_t*>(&header),
+                          sizeof(header));
+    EXPECT_TRUE(DisassemblerElfX86::QuickDetect(image));
+    EXPECT_FALSE(DisassemblerElfX64::QuickDetect(image));
+  }
+
+  // Typical x64 elf header.
+  {
+    elf::Elf64_Ehdr header = {};
+    auto e_ident =
+        ParseHexString("7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00");
+    std::copy(e_ident.begin(), e_ident.end(), header.e_ident);
+    header.e_type = elf::ET_EXEC;
+    header.e_machine = elf::EM_X86_64;
+    header.e_version = 1;
+    header.e_shentsize = sizeof(elf::Elf64_Shdr);
+    ConstBufferView image(reinterpret_cast<const uint8_t*>(&header),
+                          sizeof(header));
+    EXPECT_FALSE(DisassemblerElfX86::QuickDetect(image));
+    EXPECT_TRUE(DisassemblerElfX64::QuickDetect(image));
+  }
+}
+
+}  // namespace zucchini
diff --git a/element_detection.cc b/element_detection.cc
index 6b31f61..fa5adf4 100644
--- a/element_detection.cc
+++ b/element_detection.cc
@@ -19,6 +19,10 @@
 #include "components/zucchini/disassembler_win32.h"
 #endif  // BUILDFLAG(ENABLE_WIN)
 
+#if BUILDFLAG(ENABLE_ELF)
+#include "components/zucchini/disassembler_elf.h"
+#endif  // BUILDFLAG(ENABLE_ELF)
+
 #if BUILDFLAG(ENABLE_ZTF)
 #include "components/zucchini/disassembler_ztf.h"
 #endif  // BUILDFLAG(ENABLE_ZTF)
@@ -50,6 +54,20 @@
   }
 #endif  // BUILDFLAG(ENABLE_WIN)
 
+#if BUILDFLAG(ENABLE_ELF)
+  if (DisassemblerElfX86::QuickDetect(image)) {
+    auto disasm = Disassembler::Make<DisassemblerElfX86>(image);
+    if (disasm && disasm->size() >= kMinProgramSize)
+      return disasm;
+  }
+
+  if (DisassemblerElfX64::QuickDetect(image)) {
+    auto disasm = Disassembler::Make<DisassemblerElfX64>(image);
+    if (disasm && disasm->size() >= kMinProgramSize)
+      return disasm;
+  }
+#endif  // BUILDFLAG(ENABLE_ELF)
+
 #if BUILDFLAG(ENABLE_DEX)
   if (DisassemblerDex::QuickDetect(image)) {
     auto disasm = Disassembler::Make<DisassemblerDex>(image);
@@ -79,6 +97,12 @@
     case kExeTypeWin32X64:
       return Disassembler::Make<DisassemblerWin32X64>(image);
 #endif  // BUILDFLAG(ENABLE_WIN)
+#if BUILDFLAG(ENABLE_ELF)
+    case kExeTypeElfX86:
+      return Disassembler::Make<DisassemblerElfX86>(image);
+    case kExeTypeElfX64:
+      return Disassembler::Make<DisassemblerElfX64>(image);
+#endif  // BUILDFLAG(ENABLE_ELF)
 #if BUILDFLAG(ENABLE_DEX)
     case kExeTypeDex:
       return Disassembler::Make<DisassemblerDex>(image);
diff --git a/type_elf.h b/type_elf.h
index 99dcba2..2bbe6dc 100644
--- a/type_elf.h
+++ b/type_elf.h
@@ -67,8 +67,30 @@
   Elf64_Half e_shstrndx;
 };
 
+// Identification Indexes in header->e_ident.
+enum IdentificationIndex {
+  EI_MAG0 = 0,        // File identification.
+  EI_MAG1 = 1,        // File identification.
+  EI_MAG2 = 2,        // File identification.
+  EI_MAG3 = 3,        // File identification.
+  EI_CLASS = 4,       // File class.
+  EI_DATA = 5,        // Data encoding.
+  EI_VERSION = 6,     // File version.
+  EI_OSABI = 7,       // Operating system/ABI identification.
+  EI_ABIVERSION = 8,  // ABI version.
+  EI_PAD = 9,         // Start of padding bytes.
+  EI_NIDENT = 16      // Size of e_ident[].
+};
+
+// Values for header->e_ident[EI_CLASS].
+enum FileClass {
+  ELFCLASSNONE = 0,  // Invalid class.
+  ELFCLASS32 = 1,    // 32-bit objects.
+  ELFCLASS64 = 2     // 64-bit objects.
+};
+
 // Values for header->e_type.
-enum e_type_values {
+enum FileType {
   ET_NONE = 0,         // No file type
   ET_REL = 1,          // Relocatable file
   ET_EXEC = 2,         // Executable file
@@ -79,7 +101,7 @@
 };
 
 // Values for header->e_machine.
-enum e_machine_values {
+enum MachineArchitecture {
   EM_NONE = 0,       // No machine.
   EM_386 = 3,        // Intel Architecture.
   EM_ARM = 40,       // ARM Architecture.