Merge "Change invalid root from LOG(ERROR) to LOG(INTERNAL_FATAL)"
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h
index 99b8e79..230ebe3 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/dwarf/dwarf_test.h
@@ -57,44 +57,41 @@
 
   // Pretty-print the generated DWARF data using objdump.
   template<typename ElfTypes>
-  std::vector<std::string> Objdump(bool is64bit, const char* args) {
+  std::vector<std::string> Objdump(const char* args) {
     // Write simple elf file with just the DWARF sections.
+    InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
     class NoCode : public CodeOutput {
-      virtual void SetCodeOffset(size_t) { }
-      virtual bool Write(OutputStream*) { return true; }
-    } code;
-    ScratchFile file;
-    InstructionSet isa = is64bit ? kX86_64 : kX86;
-    ElfBuilder<ElfTypes> builder(
-        &code, file.GetFile(), isa, 0, 0, 0, 0, 0, 0, false, false);
-    typedef ElfRawSectionBuilder<ElfTypes> Section;
-    Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    Section eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
+      bool Write(OutputStream*) OVERRIDE { return true; }  // NOLINT
+    } no_code;
+    ElfBuilder<ElfTypes> builder(isa, 0, &no_code, 0, &no_code, 0);
+    typedef typename ElfBuilder<ElfTypes>::RawSection RawSection;
+    RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+    RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+    RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+    RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+    RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0);
     if (!debug_info_data_.empty()) {
       debug_info.SetBuffer(debug_info_data_);
-      builder.RegisterRawSection(&debug_info);
+      builder.RegisterSection(&debug_info);
     }
     if (!debug_abbrev_data_.empty()) {
       debug_abbrev.SetBuffer(debug_abbrev_data_);
-      builder.RegisterRawSection(&debug_abbrev);
+      builder.RegisterSection(&debug_abbrev);
     }
     if (!debug_str_data_.empty()) {
       debug_str.SetBuffer(debug_str_data_);
-      builder.RegisterRawSection(&debug_str);
+      builder.RegisterSection(&debug_str);
     }
     if (!debug_line_data_.empty()) {
       debug_line.SetBuffer(debug_line_data_);
-      builder.RegisterRawSection(&debug_line);
+      builder.RegisterSection(&debug_line);
     }
     if (!eh_frame_data_.empty()) {
       eh_frame.SetBuffer(eh_frame_data_);
-      builder.RegisterRawSection(&eh_frame);
+      builder.RegisterSection(&eh_frame);
     }
-    builder.Init();
-    builder.Write();
+    ScratchFile file;
+    builder.Write(file.GetFile());
 
     // Read the elf file back using objdump.
     std::vector<std::string> lines;
@@ -123,9 +120,9 @@
 
   std::vector<std::string> Objdump(bool is64bit, const char* args) {
     if (is64bit) {
-      return Objdump<ElfTypes64>(is64bit, args);
+      return Objdump<ElfTypes64>(args);
     } else {
-      return Objdump<ElfTypes32>(is64bit, args);
+      return Objdump<ElfTypes32>(args);
     }
   }
 
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 32c8cce..07976e8 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,535 +17,32 @@
 #ifndef ART_COMPILER_ELF_BUILDER_H_
 #define ART_COMPILER_ELF_BUILDER_H_
 
+#include <vector>
+
 #include "arch/instruction_set.h"
-#include "base/stl_util.h"
-#include "base/value_object.h"
+#include "base/unix_file/fd_file.h"
 #include "buffered_output_stream.h"
 #include "elf_utils.h"
 #include "file_output_stream.h"
 
 namespace art {
 
-template <typename ElfTypes>
-class ElfSectionBuilder : public ValueObject {
- public:
-  using Elf_Word = typename ElfTypes::Word;
-  using Elf_Shdr = typename ElfTypes::Shdr;
-
-  ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
-                    const ElfSectionBuilder<ElfTypes> *link, Elf_Word info,
-                    Elf_Word align, Elf_Word entsize)
-      : section_index_(0), name_(sec_name), link_(link) {
-    memset(&section_, 0, sizeof(section_));
-    section_.sh_type = type;
-    section_.sh_flags = flags;
-    section_.sh_info = info;
-    section_.sh_addralign = align;
-    section_.sh_entsize = entsize;
-  }
-  ElfSectionBuilder(const ElfSectionBuilder&) = default;
-
-  ~ElfSectionBuilder() {}
-
-  Elf_Word GetLink() const {
-    return (link_ != nullptr) ? link_->section_index_ : 0;
-  }
-
-  const Elf_Shdr* GetSection() const {
-    return &section_;
-  }
-
-  Elf_Shdr* GetSection() {
-    return &section_;
-  }
-
-  Elf_Word GetSectionIndex() const {
-    return section_index_;
-  }
-
-  void SetSectionIndex(Elf_Word section_index) {
-    section_index_ = section_index;
-  }
-
-  const std::string& GetName() const {
-    return name_;
-  }
-
- private:
-  Elf_Shdr section_;
-  Elf_Word section_index_;
-  const std::string name_;
-  const ElfSectionBuilder* const link_;
-};
-
-template <typename ElfTypes>
-class ElfDynamicBuilder FINAL : public ElfSectionBuilder<ElfTypes> {
- public:
-  using Elf_Word = typename ElfTypes::Word;
-  using Elf_Sword = typename ElfTypes::Sword;
-  using Elf_Shdr = typename ElfTypes::Shdr;
-  using Elf_Dyn = typename ElfTypes::Dyn;
-
-  void AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
-    if (tag == DT_NULL) {
-      return;
-    }
-    dynamics_.push_back({nullptr, tag, d_un});
-  }
-
-  void AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
-                     const ElfSectionBuilder<ElfTypes>* section) {
-    if (tag == DT_NULL) {
-      return;
-    }
-    dynamics_.push_back({section, tag, d_un});
-  }
-
-  ElfDynamicBuilder(const std::string& sec_name,
-                    ElfSectionBuilder<ElfTypes> *link)
-  : ElfSectionBuilder<ElfTypes>(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC,
-                                link, 0, kPageSize, sizeof(Elf_Dyn)) {}
-  ~ElfDynamicBuilder() {}
-
-  Elf_Word GetSize() const {
-    // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
-    // these must be added when we actually put the file together because
-    // their values are very dependent on state.
-    return dynamics_.size() + 3;
-  }
-
-  // Create the actual dynamic vector. strsz should be the size of the .dynstr
-  // table and soname_off should be the offset of the soname in .dynstr.
-  // Since niether can be found prior to final layout we will wait until here
-  // to add them.
-  std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname) const {
-    std::vector<Elf_Dyn> ret;
-    for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
-      if (it->section_ != nullptr) {
-        // We are adding an address relative to a section.
-        ret.push_back(
-            {it->tag_, {it->off_ + it->section_->GetSection()->sh_addr}});
-      } else {
-        ret.push_back({it->tag_, {it->off_}});
-      }
-    }
-    ret.push_back({DT_STRSZ, {strsz}});
-    ret.push_back({DT_SONAME, {soname}});
-    ret.push_back({DT_NULL, {0}});
-    return ret;
-  }
-
- private:
-  struct ElfDynamicState {
-    const ElfSectionBuilder<ElfTypes>* section_;
-    Elf_Sword tag_;
-    Elf_Word off_;
-  };
-  std::vector<ElfDynamicState> dynamics_;
-};
-
-template <typename ElfTypes>
-class ElfRawSectionBuilder FINAL : public ElfSectionBuilder<ElfTypes> {
- public:
-  using Elf_Word = typename ElfTypes::Word;
-
-  ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
-                       const ElfSectionBuilder<ElfTypes>* link, Elf_Word info,
-                       Elf_Word align, Elf_Word entsize)
-    : ElfSectionBuilder<ElfTypes>(sec_name, type, flags, link, info, align, entsize) {
-  }
-  ElfRawSectionBuilder(const ElfRawSectionBuilder&) = default;
-
-  ~ElfRawSectionBuilder() {}
-
-  std::vector<uint8_t>* GetBuffer() {
-    return &buf_;
-  }
-
-  void SetBuffer(const std::vector<uint8_t>& buf) {
-    buf_ = buf;
-  }
-
- private:
-  std::vector<uint8_t> buf_;
-};
-
-template <typename ElfTypes>
-class ElfOatSectionBuilder FINAL : public ElfSectionBuilder<ElfTypes> {
- public:
-  using Elf_Word = typename ElfTypes::Word;
-
-  ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
-                       Elf_Word type, Elf_Word flags)
-    : ElfSectionBuilder<ElfTypes>(sec_name, type, flags, nullptr, 0, kPageSize, 0),
-      offset_(offset), size_(size) {
-  }
-
-  ~ElfOatSectionBuilder() {}
-
-  Elf_Word GetOffset() const {
-    return offset_;
-  }
-
-  Elf_Word GetSize() const {
-    return size_;
-  }
-
- private:
-  // Offset of the content within the file.
-  Elf_Word offset_;
-  // Size of the content within the file.
-  Elf_Word size_;
-};
-
-static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
-  return ((binding) << 4) + ((type) & 0xf);
-}
-
-// from bionic
-static inline unsigned elfhash(const char *_name) {
-  const unsigned char *name = (const unsigned char *) _name;
-  unsigned h = 0, g;
-
-  while (*name) {
-    h = (h << 4) + *name++;
-    g = h & 0xf0000000;
-    h ^= g;
-    h ^= g >> 24;
-  }
-  return h;
-}
-
-template <typename ElfTypes>
-class ElfSymtabBuilder FINAL : public ElfSectionBuilder<ElfTypes> {
- public:
-  using Elf_Addr = typename ElfTypes::Addr;
-  using Elf_Word = typename ElfTypes::Word;
-  using Elf_Sym = typename ElfTypes::Sym;
-
-  // Add a symbol with given name to this symtab. The symbol refers to
-  // 'relative_addr' within the given section and has the given attributes.
-  void AddSymbol(const std::string& name,
-                 const ElfSectionBuilder<ElfTypes>* section,
-                 Elf_Addr addr,
-                 bool is_relative,
-                 Elf_Word size,
-                 uint8_t binding,
-                 uint8_t type,
-                 uint8_t other = 0) {
-    CHECK(section);
-    ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
-                                            MakeStInfo(binding, type), other, 0};
-    symbols_.push_back(state);
-  }
-
-  ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
-                   const std::string& str_name, Elf_Word str_type, bool alloc)
-  : ElfSectionBuilder<ElfTypes>(sec_name, type, ((alloc) ? SHF_ALLOC : 0U),
-                                &strtab_, 0, sizeof(Elf_Word),
-                                sizeof(Elf_Sym)), str_name_(str_name),
-                                str_type_(str_type),
-                                strtab_(str_name,
-                                        str_type,
-                                        ((alloc) ? SHF_ALLOC : 0U),
-                                        nullptr, 0, 1, 1) {
-  }
-
-  ~ElfSymtabBuilder() {}
-
-  std::vector<Elf_Word> GenerateHashContents() const {
-    // Here is how The ELF hash table works.
-    // There are 3 arrays to worry about.
-    // * The symbol table where the symbol information is.
-    // * The bucket array which is an array of indexes into the symtab and chain.
-    // * The chain array which is also an array of indexes into the symtab and chain.
-    //
-    // Lets say the state is something like this.
-    // +--------+       +--------+      +-----------+
-    // | symtab |       | bucket |      |   chain   |
-    // |  null  |       | 1      |      | STN_UNDEF |
-    // | <sym1> |       | 4      |      | 2         |
-    // | <sym2> |       |        |      | 5         |
-    // | <sym3> |       |        |      | STN_UNDEF |
-    // | <sym4> |       |        |      | 3         |
-    // | <sym5> |       |        |      | STN_UNDEF |
-    // +--------+       +--------+      +-----------+
-    //
-    // The lookup process (in python psudocode) is
-    //
-    // def GetSym(name):
-    //     # NB STN_UNDEF == 0
-    //     indx = bucket[elfhash(name) % num_buckets]
-    //     while indx != STN_UNDEF:
-    //         if GetSymbolName(symtab[indx]) == name:
-    //             return symtab[indx]
-    //         indx = chain[indx]
-    //     return SYMBOL_NOT_FOUND
-    //
-    // Between bucket and chain arrays every symtab index must be present exactly
-    // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
-
-    // Select number of buckets.
-    // This is essentially arbitrary.
-    Elf_Word nbuckets;
-    Elf_Word chain_size = GetSize();
-    if (symbols_.size() < 8) {
-      nbuckets = 2;
-    } else if (symbols_.size() < 32) {
-      nbuckets = 4;
-    } else if (symbols_.size() < 256) {
-      nbuckets = 16;
-    } else {
-      // Have about 32 ids per bucket.
-      nbuckets = RoundUp(symbols_.size()/32, 2);
-    }
-    std::vector<Elf_Word> hash;
-    hash.push_back(nbuckets);
-    hash.push_back(chain_size);
-    uint32_t bucket_offset = hash.size();
-    uint32_t chain_offset = bucket_offset + nbuckets;
-    hash.resize(hash.size() + nbuckets + chain_size, 0);
-
-    Elf_Word* buckets = hash.data() + bucket_offset;
-    Elf_Word* chain   = hash.data() + chain_offset;
-
-    // Set up the actual hash table.
-    for (Elf_Word i = 0; i < symbols_.size(); i++) {
-      // Add 1 since we need to have the null symbol that is not in the symbols
-      // list.
-      Elf_Word index = i + 1;
-      Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
-      if (buckets[hash_val] == 0) {
-        buckets[hash_val] = index;
-      } else {
-        hash_val = buckets[hash_val];
-        CHECK_LT(hash_val, chain_size);
-        while (chain[hash_val] != 0) {
-          hash_val = chain[hash_val];
-          CHECK_LT(hash_val, chain_size);
-        }
-        chain[hash_val] = index;
-        // Check for loops. Works because if this is non-empty then there must be
-        // another cell which already contains the same symbol index as this one,
-        // which means some symbol has more then one name, which isn't allowed.
-        CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
-      }
-    }
-
-    return hash;
-  }
-
-  std::string GenerateStrtab() {
-    std::string tab;
-    tab += '\0';
-    for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
-      it->name_idx_ = tab.size();
-      tab += it->name_;
-      tab += '\0';
-    }
-    strtab_.GetSection()->sh_size = tab.size();
-    return tab;
-  }
-
-  std::vector<Elf_Sym> GenerateSymtab() {
-    std::vector<Elf_Sym> ret;
-    Elf_Sym undef_sym;
-    memset(&undef_sym, 0, sizeof(undef_sym));
-    undef_sym.st_shndx = SHN_UNDEF;
-    ret.push_back(undef_sym);
-
-    for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
-      Elf_Sym sym;
-      memset(&sym, 0, sizeof(sym));
-      sym.st_name = it->name_idx_;
-      if (it->is_relative_) {
-        sym.st_value = it->addr_ + it->section_->GetSection()->sh_offset;
-      } else {
-        sym.st_value = it->addr_;
-      }
-      sym.st_size = it->size_;
-      sym.st_other = it->other_;
-      sym.st_shndx = it->section_->GetSectionIndex();
-      sym.st_info = it->info_;
-
-      ret.push_back(sym);
-    }
-    return ret;
-  }
-
-  Elf_Word GetSize() const {
-    // 1 is for the implicit null symbol.
-    return symbols_.size() + 1;
-  }
-
-  ElfSectionBuilder<ElfTypes>* GetStrTab() {
-    return &strtab_;
-  }
-
- private:
-  struct ElfSymbolState {
-    const std::string name_;
-    const ElfSectionBuilder<ElfTypes>* section_;
-    Elf_Addr addr_;
-    Elf_Word size_;
-    bool is_relative_;
-    uint8_t info_;
-    uint8_t other_;
-    // Used during Write() to temporarially hold name index in the strtab.
-    Elf_Word name_idx_;
-  };
-
-  // Information for the strsym for dynstr sections.
-  const std::string str_name_;
-  Elf_Word str_type_;
-  // The symbols in the same order they will be in the symbol table.
-  std::vector<ElfSymbolState> symbols_;
-  ElfSectionBuilder<ElfTypes> strtab_;
-};
-
-template <typename Elf_Word>
-class ElfFilePiece {
- public:
-  virtual ~ElfFilePiece() {}
-
-  virtual bool Write(File* elf_file) {
-    if (static_cast<off_t>(offset_) != lseek(elf_file->Fd(), offset_, SEEK_SET)) {
-      PLOG(ERROR) << "Failed to seek to " << GetDescription() << " offset " << offset_ << " for "
-          << elf_file->GetPath();
-      return false;
-    }
-
-    return DoActualWrite(elf_file);
-  }
-
-  static bool Compare(ElfFilePiece* a, ElfFilePiece* b) {
-    return a->offset_ < b->offset_;
-  }
-
- protected:
-  explicit ElfFilePiece(Elf_Word offset) : offset_(offset) {}
-
-  Elf_Word GetOffset() const {
-    return offset_;
-  }
-
-  virtual const char* GetDescription() const = 0;
-  virtual bool DoActualWrite(File* elf_file) = 0;
-
- private:
-  const Elf_Word offset_;
-
-  DISALLOW_COPY_AND_ASSIGN(ElfFilePiece);
-};
-
-template <typename Elf_Word>
-class ElfFileMemoryPiece FINAL : public ElfFilePiece<Elf_Word> {
- public:
-  ElfFileMemoryPiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
-      : ElfFilePiece<Elf_Word>(offset), dbg_name_(name), data_(data), size_(size) {}
-
- protected:
-  bool DoActualWrite(File* elf_file) OVERRIDE {
-    DCHECK(data_ != nullptr || size_ == 0U) << dbg_name_ << " " << size_;
-
-    if (!elf_file->WriteFully(data_, size_)) {
-      PLOG(ERROR) << "Failed to write " << dbg_name_ << " for " << elf_file->GetPath();
-      return false;
-    }
-
-    return true;
-  }
-
-  const char* GetDescription() const OVERRIDE {
-    return dbg_name_.c_str();
-  }
-
- private:
-  const std::string& dbg_name_;
-  const void *data_;
-  Elf_Word size_;
-};
-
 class CodeOutput {
  public:
-  virtual void SetCodeOffset(size_t offset) = 0;
   virtual bool Write(OutputStream* out) = 0;
   virtual ~CodeOutput() {}
 };
 
-template <typename Elf_Word>
-class ElfFileRodataPiece FINAL : public ElfFilePiece<Elf_Word> {
- public:
-  ElfFileRodataPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
-      output_(output) {}
-
- protected:
-  bool DoActualWrite(File* elf_file) OVERRIDE {
-    output_->SetCodeOffset(this->GetOffset());
-    std::unique_ptr<BufferedOutputStream> output_stream(
-        new BufferedOutputStream(new FileOutputStream(elf_file)));
-    if (!output_->Write(output_stream.get())) {
-      PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file->GetPath();
-      return false;
-    }
-
-    return true;
-  }
-
-  const char* GetDescription() const OVERRIDE {
-    return ".rodata";
-  }
-
- private:
-  CodeOutput* const output_;
-
-  DISALLOW_COPY_AND_ASSIGN(ElfFileRodataPiece);
-};
-
-template <typename Elf_Word>
-class ElfFileOatTextPiece FINAL : public ElfFilePiece<Elf_Word> {
- public:
-  ElfFileOatTextPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
-      output_(output) {}
-
- protected:
-  bool DoActualWrite(File* elf_file ATTRIBUTE_UNUSED) OVERRIDE {
-    // All data is written by the ElfFileRodataPiece right now, as the oat writer writes in one
-    // piece. This is for future flexibility.
-    UNUSED(output_);
-    return true;
-  }
-
-  const char* GetDescription() const OVERRIDE {
-    return ".text";
-  }
-
- private:
-  CodeOutput* const output_;
-
-  DISALLOW_COPY_AND_ASSIGN(ElfFileOatTextPiece);
-};
-
-template <typename Elf_Word>
-static bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces, File* elf_file) {
-  // TODO It would be nice if this checked for overlap.
-  for (auto it = pieces.begin(); it != pieces.end(); ++it) {
-    if (!(*it)->Write(elf_file)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-template <typename Elf_Word, typename Elf_Shdr>
-static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
-  return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
-}
-
+// Writes ELF file.
+// The main complication is that the sections often want to reference
+// each other.  We solve this by writing the ELF file in two stages:
+//  * Sections are asked about their size, and overall layout is calculated.
+//  * Sections do the actual writes which may use offsets of other sections.
 template <typename ElfTypes>
 class ElfBuilder FINAL {
  public:
   using Elf_Addr = typename ElfTypes::Addr;
+  using Elf_Off = typename ElfTypes::Off;
   using Elf_Word = typename ElfTypes::Word;
   using Elf_Sword = typename ElfTypes::Sword;
   using Elf_Ehdr = typename ElfTypes::Ehdr;
@@ -554,47 +51,481 @@
   using Elf_Phdr = typename ElfTypes::Phdr;
   using Elf_Dyn = typename ElfTypes::Dyn;
 
-  ElfBuilder(CodeOutput* oat_writer,
-             File* elf_file,
-             InstructionSet isa,
-             Elf_Word rodata_relative_offset,
-             Elf_Word rodata_size,
-             Elf_Word text_relative_offset,
-             Elf_Word text_size,
-             Elf_Word bss_relative_offset,
-             Elf_Word bss_size,
-             const bool add_symbols,
-             bool debug = false)
-    : oat_writer_(oat_writer),
-      elf_file_(elf_file),
-      add_symbols_(add_symbols),
-      debug_logging_(debug),
-      text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
-                    SHF_ALLOC | SHF_EXECINSTR),
-      rodata_builder_(".rodata", rodata_size, rodata_relative_offset, SHT_PROGBITS, SHF_ALLOC),
-      bss_builder_(".bss", bss_size, bss_relative_offset, SHT_NOBITS, SHF_ALLOC),
-      dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
-      symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
-      hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0, sizeof(Elf_Word),
-                    sizeof(Elf_Word)),
-      dynamic_builder_(".dynamic", &dynsym_builder_),
-      shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, nullptr, 0, 1, 1) {
-    SetupEhdr();
-    SetupDynamic();
-    SetupRequiredSymbols();
-    SetISA(isa);
+  // Base class of all sections.
+  class Section {
+   public:
+    Section(const std::string& name, Elf_Word type, Elf_Word flags,
+            const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize)
+        : header_(), section_index_(0), name_(name), link_(link) {
+      header_.sh_type = type;
+      header_.sh_flags = flags;
+      header_.sh_info = info;
+      header_.sh_addralign = align;
+      header_.sh_entsize = entsize;
+    }
+    virtual ~Section() {}
+
+    // Returns the size of the content of this section.  It is used to
+    // calculate file offsets of all sections before doing any writes.
+    virtual Elf_Word GetSize() const = 0;
+
+    // Write the content of this section to the given file.
+    // This must write exactly the number of bytes returned by GetSize().
+    // Offsets of all sections are known when this method is called.
+    virtual bool Write(File* elf_file) = 0;
+
+    Elf_Word GetLink() const {
+      return (link_ != nullptr) ? link_->GetSectionIndex() : 0;
+    }
+
+    const Elf_Shdr* GetHeader() const {
+      return &header_;
+    }
+
+    Elf_Shdr* GetHeader() {
+      return &header_;
+    }
+
+    Elf_Word GetSectionIndex() const {
+      DCHECK_NE(section_index_, 0u);
+      return section_index_;
+    }
+
+    void SetSectionIndex(Elf_Word section_index) {
+      section_index_ = section_index;
+    }
+
+    const std::string& GetName() const {
+      return name_;
+    }
+
+   private:
+    Elf_Shdr header_;
+    Elf_Word section_index_;
+    const std::string name_;
+    const Section* const link_;
+
+    DISALLOW_COPY_AND_ASSIGN(Section);
+  };
+
+  // Writer of .dynamic section.
+  class DynamicSection FINAL : public Section {
+   public:
+    void AddDynamicTag(Elf_Sword tag, Elf_Word value, const Section* section) {
+      DCHECK_NE(tag, static_cast<Elf_Sword>(DT_NULL));
+      dynamics_.push_back({tag, value, section});
+    }
+
+    DynamicSection(const std::string& name, Section* link)
+        : Section(name, SHT_DYNAMIC, SHF_ALLOC,
+                  link, 0, kPageSize, sizeof(Elf_Dyn)) {}
+
+    Elf_Word GetSize() const OVERRIDE {
+      return (dynamics_.size() + 1 /* DT_NULL */) * sizeof(Elf_Dyn);
+    }
+
+    bool Write(File* elf_file) OVERRIDE {
+      std::vector<Elf_Dyn> buffer;
+      buffer.reserve(dynamics_.size() + 1u);
+      for (const ElfDynamicState& it : dynamics_) {
+        if (it.section_ != nullptr) {
+          // We are adding an address relative to a section.
+          buffer.push_back(
+              {it.tag_, {it.value_ + it.section_->GetHeader()->sh_addr}});
+        } else {
+          buffer.push_back({it.tag_, {it.value_}});
+        }
+      }
+      buffer.push_back({DT_NULL, {0}});
+      return WriteArray(elf_file, buffer.data(), buffer.size());
+    }
+
+   private:
+    struct ElfDynamicState {
+      Elf_Sword tag_;
+      Elf_Word value_;
+      const Section* section_;
+    };
+    std::vector<ElfDynamicState> dynamics_;
+  };
+
+  // Section with content based on simple memory buffer.
+  // The buffer can be optionally patched before writing.
+  // The resulting address can be either absolute memory
+  // address or offset relative to the pointer location.
+  class RawSection FINAL : public Section {
+   public:
+    RawSection(const std::string& name, Elf_Word type, Elf_Word flags,
+               const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize,
+               const Section* patch_base = nullptr, bool patch_relative = false,
+               bool patch_64bit = (sizeof(Elf_Addr) == sizeof(Elf64_Addr)))
+        : Section(name, type, flags, link, info, align, entsize),
+          patched(false), patch_base_(patch_base),
+          patch_relative_(patch_relative), patch_64bit_(patch_64bit) {
+    }
+
+    Elf_Word GetSize() const OVERRIDE {
+      return buffer_.size();
+    }
+
+    bool Write(File* elf_file) OVERRIDE {
+      if (!patch_locations_.empty()) {
+        DCHECK(patch_base_ != nullptr);
+        DCHECK(!patched);  // Do not patch twice.
+        if (patch_relative_) {
+          if (patch_64bit_) {
+            Patch<true, uint64_t>();
+          } else {
+            Patch<true, uint32_t>();
+          }
+        } else {
+          if (patch_64bit_) {
+            Patch<false, uint64_t>();
+          } else {
+            Patch<false, uint32_t>();
+          }
+        }
+        patched = true;
+      }
+      return WriteArray(elf_file, buffer_.data(), buffer_.size());
+    }
+
+    bool IsEmpty() const {
+      return buffer_.size() == 0;
+    }
+
+    std::vector<uint8_t>* GetBuffer() {
+      return &buffer_;
+    }
+
+    void SetBuffer(const std::vector<uint8_t>& buffer) {
+      buffer_ = buffer;
+    }
+
+    std::vector<uintptr_t>* GetPatchLocations() {
+      return &patch_locations_;
+    }
+
+   private:
+    template <bool RelativeAddress = false, typename PatchedAddress = Elf_Addr>
+    void Patch() {
+      Elf_Addr base_addr = patch_base_->GetHeader()->sh_addr;
+      Elf_Addr addr = this->GetHeader()->sh_addr;
+      for (uintptr_t patch_location : patch_locations_) {
+        typedef __attribute__((__aligned__(1))) PatchedAddress UnalignedAddress;
+        auto* to_patch = reinterpret_cast<UnalignedAddress*>(buffer_.data() + patch_location);
+        *to_patch = (base_addr + *to_patch) - (RelativeAddress ? (addr + patch_location) : 0);
+      }
+    }
+
+    std::vector<uint8_t> buffer_;
+    std::vector<uintptr_t> patch_locations_;
+    bool patched;
+    const Section* patch_base_;
+    bool patch_relative_;
+    bool patch_64bit_;
+  };
+
+  // Writer of .rodata section or .text section.
+  // The write is done lazily using the provided CodeOutput.
+  class OatSection FINAL : public Section {
+   public:
+    OatSection(const std::string& name, Elf_Word type, Elf_Word flags,
+               const Section* link, Elf_Word info, Elf_Word align,
+               Elf_Word entsize, Elf_Word size, CodeOutput* code_output)
+        : Section(name, type, flags, link, info, align, entsize),
+          size_(size), code_output_(code_output) {
+    }
+
+    Elf_Word GetSize() const OVERRIDE {
+      return size_;
+    }
+
+    bool Write(File* elf_file) OVERRIDE {
+      // The BufferedOutputStream class contains the buffer as field,
+      // therefore it is too big to allocate on the stack.
+      std::unique_ptr<BufferedOutputStream> output_stream(
+          new BufferedOutputStream(new FileOutputStream(elf_file)));
+      return code_output_->Write(output_stream.get());
+    }
+
+   private:
+    Elf_Word size_;
+    CodeOutput* code_output_;
+  };
+
+  // Writer of .bss section.
+  class NoBitsSection FINAL : public Section {
+   public:
+    NoBitsSection(const std::string& name, Elf_Word size)
+        : Section(name, SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
+          size_(size) {
+    }
+
+    Elf_Word GetSize() const OVERRIDE {
+      return size_;
+    }
+
+    bool Write(File* elf_file ATTRIBUTE_UNUSED) OVERRIDE {
+      LOG(ERROR) << "This section should not be written to the ELF file";
+      return false;
+    }
+
+   private:
+    Elf_Word size_;
+  };
+
+  // Writer of .dynstr .strtab and .shstrtab sections.
+  class StrtabSection FINAL : public Section {
+   public:
+    StrtabSection(const std::string& name, Elf_Word flags)
+        : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 1) {
+      buffer_.reserve(4 * KB);
+      // The first entry of strtab must be empty string.
+      buffer_ += '\0';
+    }
+
+    Elf_Word AddName(const std::string& name) {
+      Elf_Word offset = buffer_.size();
+      buffer_ += name;
+      buffer_ += '\0';
+      return offset;
+    }
+
+    Elf_Word GetSize() const OVERRIDE {
+      return buffer_.size();
+    }
+
+    bool Write(File* elf_file) OVERRIDE {
+      return WriteArray(elf_file, buffer_.data(), buffer_.size());
+    }
+
+   private:
+    std::string buffer_;
+  };
+
+  class HashSection;
+
+  // Writer of .dynsym and .symtab sections.
+  class SymtabSection FINAL : public Section {
+   public:
+    // Add a symbol with given name to this symtab. The symbol refers to
+    // 'relative_addr' within the given section and has the given attributes.
+    void AddSymbol(const std::string& name, const Section* section,
+                   Elf_Addr addr, bool is_relative, Elf_Word size,
+                   uint8_t binding, uint8_t type, uint8_t other = 0) {
+      CHECK(section != nullptr);
+      Elf_Word name_idx = strtab_->AddName(name);
+      symbols_.push_back({ name, section, addr, size, is_relative,
+                           MakeStInfo(binding, type), other, name_idx });
+    }
+
+    SymtabSection(const std::string& name, Elf_Word type, Elf_Word flags,
+                  StrtabSection* strtab)
+        : Section(name, type, flags, strtab, 0, sizeof(Elf_Word), sizeof(Elf_Sym)),
+          strtab_(strtab) {
+    }
+
+    bool IsEmpty() const {
+      return symbols_.empty();
+    }
+
+    Elf_Word GetSize() const OVERRIDE {
+      return (1 /* NULL */ + symbols_.size()) * sizeof(Elf_Sym);
+    }
+
+    bool Write(File* elf_file) OVERRIDE {
+      std::vector<Elf_Sym> buffer;
+      buffer.reserve(1u + symbols_.size());
+      buffer.push_back(Elf_Sym());  // NULL.
+      for (const ElfSymbolState& it : symbols_) {
+        Elf_Sym sym = Elf_Sym();
+        sym.st_name = it.name_idx_;
+        if (it.is_relative_) {
+          sym.st_value = it.addr_ + it.section_->GetHeader()->sh_addr;
+        } else {
+          sym.st_value = it.addr_;
+        }
+        sym.st_size = it.size_;
+        sym.st_other = it.other_;
+        sym.st_shndx = it.section_->GetSectionIndex();
+        sym.st_info = it.info_;
+        buffer.push_back(sym);
+      }
+      return WriteArray(elf_file, buffer.data(), buffer.size());
+    }
+
+   private:
+    struct ElfSymbolState {
+      const std::string name_;
+      const Section* section_;
+      Elf_Addr addr_;
+      Elf_Word size_;
+      bool is_relative_;
+      uint8_t info_;
+      uint8_t other_;
+      Elf_Word name_idx_;  // index in the strtab.
+    };
+
+    static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+      return ((binding) << 4) + ((type) & 0xf);
+    }
+
+    // The symbols in the same order they will be in the symbol table.
+    std::vector<ElfSymbolState> symbols_;
+    StrtabSection* strtab_;
+
+    friend class HashSection;
+  };
+
+  // TODO: Consider removing.
+  // We use it only for the dynsym section which has only 5 symbols.
+  // We do not use it for symtab, and we probably do not have to
+  // since we use those symbols only to print backtraces.
+  class HashSection FINAL : public Section {
+   public:
+    HashSection(const std::string& name, Elf_Word flags, SymtabSection* symtab)
+        : Section(name, SHT_HASH, flags, symtab,
+                  0, sizeof(Elf_Word), sizeof(Elf_Word)),
+          symtab_(symtab) {
+    }
+
+    Elf_Word GetSize() const OVERRIDE {
+      Elf_Word nbuckets = GetNumBuckets();
+      Elf_Word chain_size = symtab_->symbols_.size() + 1 /* NULL */;
+      return (2 /* header */ + nbuckets + chain_size) * sizeof(Elf_Word);
+    }
+
+    bool Write(File* const elf_file) OVERRIDE {
+      // Here is how The ELF hash table works.
+      // There are 3 arrays to worry about.
+      // * The symbol table where the symbol information is.
+      // * The bucket array which is an array of indexes into the symtab and chain.
+      // * The chain array which is also an array of indexes into the symtab and chain.
+      //
+      // Lets say the state is something like this.
+      // +--------+       +--------+      +-----------+
+      // | symtab |       | bucket |      |   chain   |
+      // |  null  |       | 1      |      | STN_UNDEF |
+      // | <sym1> |       | 4      |      | 2         |
+      // | <sym2> |       |        |      | 5         |
+      // | <sym3> |       |        |      | STN_UNDEF |
+      // | <sym4> |       |        |      | 3         |
+      // | <sym5> |       |        |      | STN_UNDEF |
+      // +--------+       +--------+      +-----------+
+      //
+      // The lookup process (in python psudocode) is
+      //
+      // def GetSym(name):
+      //     # NB STN_UNDEF == 0
+      //     indx = bucket[elfhash(name) % num_buckets]
+      //     while indx != STN_UNDEF:
+      //         if GetSymbolName(symtab[indx]) == name:
+      //             return symtab[indx]
+      //         indx = chain[indx]
+      //     return SYMBOL_NOT_FOUND
+      //
+      // Between bucket and chain arrays every symtab index must be present exactly
+      // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
+      const auto& symbols = symtab_->symbols_;
+      // Select number of buckets.
+      // This is essentially arbitrary.
+      Elf_Word nbuckets = GetNumBuckets();
+      // 1 is for the implicit NULL symbol.
+      Elf_Word chain_size = (symbols.size() + 1);
+      std::vector<Elf_Word> hash;
+      hash.push_back(nbuckets);
+      hash.push_back(chain_size);
+      uint32_t bucket_offset = hash.size();
+      uint32_t chain_offset = bucket_offset + nbuckets;
+      hash.resize(hash.size() + nbuckets + chain_size, 0);
+
+      Elf_Word* buckets = hash.data() + bucket_offset;
+      Elf_Word* chain   = hash.data() + chain_offset;
+
+      // Set up the actual hash table.
+      for (Elf_Word i = 0; i < symbols.size(); i++) {
+        // Add 1 since we need to have the null symbol that is not in the symbols
+        // list.
+        Elf_Word index = i + 1;
+        Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols[i].name_.c_str())) % nbuckets;
+        if (buckets[hash_val] == 0) {
+          buckets[hash_val] = index;
+        } else {
+          hash_val = buckets[hash_val];
+          CHECK_LT(hash_val, chain_size);
+          while (chain[hash_val] != 0) {
+            hash_val = chain[hash_val];
+            CHECK_LT(hash_val, chain_size);
+          }
+          chain[hash_val] = index;
+          // Check for loops. Works because if this is non-empty then there must be
+          // another cell which already contains the same symbol index as this one,
+          // which means some symbol has more then one name, which isn't allowed.
+          CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
+        }
+      }
+      return WriteArray(elf_file, hash.data(), hash.size());
+    }
+
+   private:
+    Elf_Word GetNumBuckets() const {
+      const auto& symbols = symtab_->symbols_;
+      if (symbols.size() < 8) {
+        return 2;
+      } else if (symbols.size() < 32) {
+        return 4;
+      } else if (symbols.size() < 256) {
+        return 16;
+      } else {
+        // Have about 32 ids per bucket.
+        return RoundUp(symbols.size()/32, 2);
+      }
+    }
+
+    // from bionic
+    static inline unsigned elfhash(const char *_name) {
+      const unsigned char *name = (const unsigned char *) _name;
+      unsigned h = 0, g;
+
+      while (*name) {
+        h = (h << 4) + *name++;
+        g = h & 0xf0000000;
+        h ^= g;
+        h ^= g >> 24;
+      }
+      return h;
+    }
+
+    SymtabSection* symtab_;
+
+    DISALLOW_COPY_AND_ASSIGN(HashSection);
+  };
+
+  ElfBuilder(InstructionSet isa,
+             Elf_Word rodata_size, CodeOutput* rodata_writer,
+             Elf_Word text_size, CodeOutput* text_writer,
+             Elf_Word bss_size)
+    : isa_(isa),
+      dynstr_(".dynstr", SHF_ALLOC),
+      dynsym_(".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_),
+      hash_(".hash", SHF_ALLOC, &dynsym_),
+      rodata_(".rodata", SHT_PROGBITS, SHF_ALLOC,
+              nullptr, 0, kPageSize, 0, rodata_size, rodata_writer),
+      text_(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
+            nullptr, 0, kPageSize, 0, text_size, text_writer),
+      bss_(".bss", bss_size),
+      dynamic_(".dynamic", &dynsym_),
+      strtab_(".strtab", 0),
+      symtab_(".symtab", SHT_SYMTAB, 0, &strtab_),
+      shstrtab_(".shstrtab", 0) {
   }
   ~ElfBuilder() {}
 
-  const ElfOatSectionBuilder<ElfTypes>& GetTextBuilder() const {
-    return text_builder_;
-  }
+  OatSection* GetText() { return &text_; }
+  SymtabSection* GetSymtab() { return &symtab_; }
 
-  ElfSymtabBuilder<ElfTypes>* GetSymtabBuilder() {
-    return &symtab_builder_;
-  }
-
-  bool Init() {
+  bool Write(File* elf_file) {
     // Since the .text section of an oat file contains relative references to .rodata
     // and (optionally) .bss, we keep these 2 or 3 sections together. This creates
     // a non-traditional layout where the .bss section is mapped independently of the
@@ -605,11 +536,12 @@
     // | Elf_Ehdr                |
     // +-------------------------+
     // | Elf_Phdr PHDR           |
-    // | Elf_Phdr LOAD R         | .dynsym .dynstr .hash .eh_frame .eh_frame_hdr .rodata
+    // | Elf_Phdr LOAD R         | .dynsym .dynstr .hash .rodata
     // | Elf_Phdr LOAD R X       | .text
     // | Elf_Phdr LOAD RW        | .bss (Optional)
     // | Elf_Phdr LOAD RW        | .dynamic
     // | Elf_Phdr DYNAMIC        | .dynamic
+    // | Elf_Phdr LOAD R         | .eh_frame .eh_frame_hdr
     // | Elf_Phdr EH_FRAME R     | .eh_frame_hdr
     // +-------------------------+
     // | .dynsym                 |
@@ -621,25 +553,10 @@
     // | Elf_Sym  oatbsslastword | (Optional)
     // +-------------------------+
     // | .dynstr                 |
-    // | \0                      |
-    // | oatdata\0               |
-    // | oatexec\0               |
-    // | oatlastword\0           |
-    // | boot.oat\0              |
+    // | names for .dynsym       |
     // +-------------------------+
     // | .hash                   |
-    // | Elf_Word nbucket = b    |
-    // | Elf_Word nchain  = c    |
-    // | Elf_Word bucket[0]      |
-    // |         ...             |
-    // | Elf_Word bucket[b - 1]  |
-    // | Elf_Word chain[0]       |
-    // |         ...             |
-    // | Elf_Word chain[c - 1]   |
-    // +-------------------------+
-    // | .eh_frame               |  (Optional)
-    // +-------------------------+
-    // | .eh_frame_hdr           |  (Optional)
+    // | hashtable for dynsym    |
     // +-------------------------+
     // | .rodata                 |
     // | oatdata..oatexec-4      |
@@ -648,38 +565,23 @@
     // | oatexec..oatlastword    |
     // +-------------------------+
     // | .dynamic                |
-    // | Elf_Dyn DT_SONAME       |
     // | Elf_Dyn DT_HASH         |
+    // | Elf_Dyn DT_STRTAB       |
     // | Elf_Dyn DT_SYMTAB       |
     // | Elf_Dyn DT_SYMENT       |
-    // | Elf_Dyn DT_STRTAB       |
     // | Elf_Dyn DT_STRSZ        |
+    // | Elf_Dyn DT_SONAME       |
     // | Elf_Dyn DT_NULL         |
     // +-------------------------+  (Optional)
-    // | .strtab                 |  (Optional)
-    // | program symbol names    |  (Optional)
-    // +-------------------------+  (Optional)
     // | .symtab                 |  (Optional)
     // | program symbols         |  (Optional)
-    // +-------------------------+
-    // | .shstrtab               |
-    // | \0                      |
-    // | .dynamic\0              |
-    // | .dynsym\0               |
-    // | .dynstr\0               |
-    // | .hash\0                 |
-    // | .rodata\0               |
-    // | .text\0                 |
-    // | .bss\0                  |  (Optional)
-    // | .shstrtab\0             |
-    // | .symtab\0               |  (Optional)
-    // | .strtab\0               |  (Optional)
-    // | .eh_frame\0             |  (Optional)
-    // | .eh_frame_hdr\0         |  (Optional)
-    // | .debug_info\0           |  (Optional)
-    // | .debug_abbrev\0         |  (Optional)
-    // | .debug_str\0            |  (Optional)
-    // | .debug_line\0           |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .strtab                 |  (Optional)
+    // | names for .symtab       |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .eh_frame               |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .eh_frame_hdr           |  (Optional)
     // +-------------------------+  (Optional)
     // | .debug_info             |  (Optional)
     // +-------------------------+  (Optional)
@@ -688,7 +590,10 @@
     // | .debug_str              |  (Optional)
     // +-------------------------+  (Optional)
     // | .debug_line             |  (Optional)
-    // +-------------------------+  (Optional)
+    // +-------------------------+
+    // | .shstrtab               |
+    // | names of sections       |
+    // +-------------------------+
     // | Elf_Shdr null           |
     // | Elf_Shdr .dynsym        |
     // | Elf_Shdr .dynstr        |
@@ -697,552 +602,266 @@
     // | Elf_Shdr .text          |
     // | Elf_Shdr .bss           |  (Optional)
     // | Elf_Shdr .dynamic       |
-    // | Elf_Shdr .shstrtab      |
+    // | Elf_Shdr .symtab        |  (Optional)
+    // | Elf_Shdr .strtab        |  (Optional)
     // | Elf_Shdr .eh_frame      |  (Optional)
     // | Elf_Shdr .eh_frame_hdr  |  (Optional)
     // | Elf_Shdr .debug_info    |  (Optional)
     // | Elf_Shdr .debug_abbrev  |  (Optional)
     // | Elf_Shdr .debug_str     |  (Optional)
     // | Elf_Shdr .debug_line    |  (Optional)
+    // | Elf_Shdr .oat_patches   |  (Optional)
+    // | Elf_Shdr .shstrtab      |
     // +-------------------------+
+    constexpr bool debug_logging_ = false;
 
-    if (fatal_error_) {
-      return false;
+    // Create a list of all section which we want to write.
+    // This is the order in which they will be written.
+    std::vector<Section*> sections;
+    sections.push_back(&dynsym_);
+    sections.push_back(&dynstr_);
+    sections.push_back(&hash_);
+    sections.push_back(&rodata_);
+    sections.push_back(&text_);
+    if (bss_.GetSize() != 0u) {
+      sections.push_back(&bss_);
     }
-    // Step 1. Figure out all the offsets.
-
-    if (debug_logging_) {
-      LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
-      LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
+    sections.push_back(&dynamic_);
+    if (!symtab_.IsEmpty()) {
+      sections.push_back(&symtab_);
+      sections.push_back(&strtab_);
+    }
+    for (Section* section : other_sections_) {
+      sections.push_back(section);
+    }
+    sections.push_back(&shstrtab_);
+    for (size_t i = 0; i < sections.size(); i++) {
+      // The first section index is 1.  Index 0 is reserved for NULL.
+      // Section index is used for relative symbols and for section links.
+      sections[i]->SetSectionIndex(i + 1);
+      // Add section name to .shstrtab.
+      Elf_Word name_offset = shstrtab_.AddName(sections[i]->GetName());
+      sections[i]->GetHeader()->sh_name = name_offset;
     }
 
-    memset(&program_headers_, 0, sizeof(program_headers_));
-    program_headers_[PH_PHDR].p_type    = PT_PHDR;
-    program_headers_[PH_PHDR].p_offset  = PHDR_OFFSET;
-    program_headers_[PH_PHDR].p_vaddr   = PHDR_OFFSET;
-    program_headers_[PH_PHDR].p_paddr   = PHDR_OFFSET;
-    program_headers_[PH_PHDR].p_filesz  = sizeof(program_headers_);
-    program_headers_[PH_PHDR].p_memsz   = sizeof(program_headers_);
-    program_headers_[PH_PHDR].p_flags   = PF_R;
-    program_headers_[PH_PHDR].p_align   = sizeof(Elf_Word);
+    // The running program does not have access to section headers
+    // and the loader is not supposed to use them either.
+    // The dynamic sections therefore replicates some of the layout
+    // information like the address and size of .rodata and .text.
+    // It also contains other metadata like the SONAME.
+    // The .dynamic section is found using the PT_DYNAMIC program header.
+    BuildDynsymSection();
+    BuildDynamicSection(elf_file->GetPath());
 
-    program_headers_[PH_LOAD_R__].p_type    = PT_LOAD;
-    program_headers_[PH_LOAD_R__].p_offset  = 0;
-    program_headers_[PH_LOAD_R__].p_vaddr   = 0;
-    program_headers_[PH_LOAD_R__].p_paddr   = 0;
-    program_headers_[PH_LOAD_R__].p_flags   = PF_R;
+    // We do not know the number of headers until the final stages of write.
+    // It is easiest to just reserve a fixed amount of space for them.
+    constexpr size_t kMaxProgramHeaders = 8;
+    constexpr size_t kProgramHeadersOffset = sizeof(Elf_Ehdr);
+    constexpr size_t kProgramHeadersSize = sizeof(Elf_Phdr) * kMaxProgramHeaders;
 
-    program_headers_[PH_LOAD_R_X].p_type    = PT_LOAD;
-    program_headers_[PH_LOAD_R_X].p_flags   = PF_R | PF_X;
-
-    program_headers_[PH_LOAD_RW_BSS].p_type    = PT_LOAD;
-    program_headers_[PH_LOAD_RW_BSS].p_flags   = PF_R | PF_W;
-
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_type    = PT_LOAD;
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_flags   = PF_R | PF_W;
-
-    program_headers_[PH_DYNAMIC].p_type    = PT_DYNAMIC;
-    program_headers_[PH_DYNAMIC].p_flags   = PF_R | PF_W;
-
-    program_headers_[PH_EH_FRAME_HDR].p_type = PT_NULL;
-    program_headers_[PH_EH_FRAME_HDR].p_flags = PF_R;
-
-    // Get the dynstr string.
-    dynstr_ = dynsym_builder_.GenerateStrtab();
-
-    // Add the SONAME to the dynstr.
-    dynstr_soname_offset_ = dynstr_.size();
-    std::string file_name(elf_file_->GetPath());
-    size_t directory_separator_pos = file_name.rfind('/');
-    if (directory_separator_pos != std::string::npos) {
-      file_name = file_name.substr(directory_separator_pos + 1);
-    }
-    dynstr_ += file_name;
-    dynstr_ += '\0';
-    if (debug_logging_) {
-      LOG(INFO) << "dynstr size (bytes)   =" << dynstr_.size()
-                << std::hex << " " << dynstr_.size();
-      LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
-                << std::hex << " " << dynsym_builder_.GetSize();
-    }
-
-    // Get the section header string table.
-    shstrtab_ += '\0';
-
-    // Setup sym_undef
-    memset(&null_hdr_, 0, sizeof(null_hdr_));
-    null_hdr_.sh_type = SHT_NULL;
-    null_hdr_.sh_link = SHN_UNDEF;
-    section_ptrs_.push_back(&null_hdr_);
-
-    section_index_ = 1;
-
-    // setup .dynsym
-    section_ptrs_.push_back(dynsym_builder_.GetSection());
-    AssignSectionStr(&dynsym_builder_, &shstrtab_);
-    dynsym_builder_.SetSectionIndex(section_index_);
-    section_index_++;
-
-    // Setup .dynstr
-    section_ptrs_.push_back(dynsym_builder_.GetStrTab()->GetSection());
-    AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
-    dynsym_builder_.GetStrTab()->SetSectionIndex(section_index_);
-    section_index_++;
-
-    // Setup .hash
-    section_ptrs_.push_back(hash_builder_.GetSection());
-    AssignSectionStr(&hash_builder_, &shstrtab_);
-    hash_builder_.SetSectionIndex(section_index_);
-    section_index_++;
-
-    // Setup .rodata
-    section_ptrs_.push_back(rodata_builder_.GetSection());
-    AssignSectionStr(&rodata_builder_, &shstrtab_);
-    rodata_builder_.SetSectionIndex(section_index_);
-    section_index_++;
-
-    // Setup .text
-    section_ptrs_.push_back(text_builder_.GetSection());
-    AssignSectionStr(&text_builder_, &shstrtab_);
-    text_builder_.SetSectionIndex(section_index_);
-    section_index_++;
-
-    // Setup .bss
-    if (bss_builder_.GetSize() != 0u) {
-      section_ptrs_.push_back(bss_builder_.GetSection());
-      AssignSectionStr(&bss_builder_, &shstrtab_);
-      bss_builder_.SetSectionIndex(section_index_);
-      section_index_++;
-    }
-
-    // Setup .dynamic
-    section_ptrs_.push_back(dynamic_builder_.GetSection());
-    AssignSectionStr(&dynamic_builder_, &shstrtab_);
-    dynamic_builder_.SetSectionIndex(section_index_);
-    section_index_++;
-
-    // Fill in the hash section.
-    hash_ = dynsym_builder_.GenerateHashContents();
-
-    if (debug_logging_) {
-      LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
-                << std::hex << " " << hash_.size() * sizeof(Elf_Word);
-    }
-
-    Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
-
-    // Get the layout in the sections.
-    //
-    // Get the layout of the dynsym section.
-    dynsym_builder_.GetSection()->sh_offset =
-        RoundUp(base_offset, dynsym_builder_.GetSection()->sh_addralign);
-    dynsym_builder_.GetSection()->sh_addr = dynsym_builder_.GetSection()->sh_offset;
-    dynsym_builder_.GetSection()->sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
-    dynsym_builder_.GetSection()->sh_link = dynsym_builder_.GetLink();
-
-    // Get the layout of the dynstr section.
-    dynsym_builder_.GetStrTab()->GetSection()->sh_offset =
-        NextOffset<Elf_Word, Elf_Shdr>(*dynsym_builder_.GetStrTab()->GetSection(),
-                                       *dynsym_builder_.GetSection());
-    dynsym_builder_.GetStrTab()->GetSection()->sh_addr =
-        dynsym_builder_.GetStrTab()->GetSection()->sh_offset;
-    dynsym_builder_.GetStrTab()->GetSection()->sh_size = dynstr_.size();
-    dynsym_builder_.GetStrTab()->GetSection()->sh_link = dynsym_builder_.GetStrTab()->GetLink();
-
-    // Get the layout of the hash section
-    hash_builder_.GetSection()->sh_offset =
-        NextOffset<Elf_Word, Elf_Shdr>(*hash_builder_.GetSection(),
-                                       *dynsym_builder_.GetStrTab()->GetSection());
-    hash_builder_.GetSection()->sh_addr = hash_builder_.GetSection()->sh_offset;
-    hash_builder_.GetSection()->sh_size = hash_.size() * sizeof(Elf_Word);
-    hash_builder_.GetSection()->sh_link = hash_builder_.GetLink();
-
-    // Get the layout of the extra sections with SHF_ALLOC flag.
-    // This will deal with .eh_frame and .eh_frame_hdr.
-    // .eh_frame contains relative pointers to .text which we
-    // want to fixup between the calls to Init() and Write().
-    // Therefore we handle those sections here as opposed to Write().
-    // It also has the nice side effect of including .eh_frame
-    // with the rest of LOAD_R segment.  It must come before .rodata
-    // because .rodata and .text must be next to each other.
-    Elf_Shdr* prev = hash_builder_.GetSection();
-    for (auto* it : other_builders_) {
-      if ((it->GetSection()->sh_flags & SHF_ALLOC) != 0) {
-        it->GetSection()->sh_offset = NextOffset<Elf_Word, Elf_Shdr>(*it->GetSection(), *prev);
-        it->GetSection()->sh_addr = it->GetSection()->sh_offset;
-        it->GetSection()->sh_size = it->GetBuffer()->size();
-        it->GetSection()->sh_link = it->GetLink();
-        prev = it->GetSection();
+    // Layout of all sections - determine the final file offsets and addresses.
+    // This must be done after we have built all sections and know their size.
+    Elf_Off file_offset = kProgramHeadersOffset + kProgramHeadersSize;
+    Elf_Addr load_address = file_offset;
+    std::vector<Elf_Shdr> section_headers;
+    section_headers.reserve(1u + sections.size());
+    section_headers.push_back(Elf_Shdr());  // NULL at index 0.
+    for (auto* section : sections) {
+      Elf_Shdr* header = section->GetHeader();
+      Elf_Off alignment = header->sh_addralign > 0 ? header->sh_addralign : 1;
+      header->sh_size = section->GetSize();
+      header->sh_link = section->GetLink();
+      // Allocate memory for the section in the file.
+      if (header->sh_type != SHT_NOBITS) {
+        header->sh_offset = RoundUp(file_offset, alignment);
+        file_offset = header->sh_offset + header->sh_size;
       }
+      // Allocate memory for the section during program execution.
+      if ((header->sh_flags & SHF_ALLOC) != 0) {
+        header->sh_addr = RoundUp(load_address, alignment);
+        load_address = header->sh_addr + header->sh_size;
+      }
+      if (debug_logging_) {
+        LOG(INFO) << "Section " << section->GetName() << ":" << std::hex
+                  << " offset=0x" << header->sh_offset
+                  << " addr=0x" << header->sh_addr
+                  << " size=0x" << header->sh_size;
+      }
+      // Collect section headers into continuous array for convenience.
+      section_headers.push_back(*header);
     }
-    // If the sections exist, check that they have been handled.
-    const auto* eh_frame = FindRawSection(".eh_frame");
+    Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Word));
+
+    // Create program headers now that we know the layout of the whole file.
+    // Each segment contains one or more sections which are mapped together.
+    // Not all sections are mapped during the execution of the program.
+    // PT_LOAD does the mapping.  Other PT_* types allow the program to locate
+    // interesting parts of memory and their addresses overlap with PT_LOAD.
+    std::vector<Elf_Phdr> program_headers;
+    program_headers.push_back(MakeProgramHeader(PT_PHDR, PF_R,
+      kProgramHeadersOffset, kProgramHeadersSize, sizeof(Elf_Word)));
+    // Create the main LOAD R segment which spans all sections up to .rodata.
+    const Elf_Shdr* rodata = rodata_.GetHeader();
+    program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R,
+      0, rodata->sh_offset + rodata->sh_size, rodata->sh_addralign));
+    program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_X, text_));
+    if (bss_.GetHeader()->sh_size != 0u) {
+      program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, bss_));
+    }
+    program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, dynamic_));
+    program_headers.push_back(MakeProgramHeader(PT_DYNAMIC, PF_R | PF_W, dynamic_));
+    const Section* eh_frame = FindSection(".eh_frame");
     if (eh_frame != nullptr) {
-      DCHECK_NE(eh_frame->GetSection()->sh_offset, 0u);
-    }
-    const auto* eh_frame_hdr = FindRawSection(".eh_frame_hdr");
-    if (eh_frame_hdr != nullptr) {
-      DCHECK_NE(eh_frame_hdr->GetSection()->sh_offset, 0u);
-    }
-
-    // Get the layout of the rodata section.
-    rodata_builder_.GetSection()->sh_offset =
-        NextOffset<Elf_Word, Elf_Shdr>(*rodata_builder_.GetSection(), *prev);
-    rodata_builder_.GetSection()->sh_addr = rodata_builder_.GetSection()->sh_offset;
-    rodata_builder_.GetSection()->sh_size = rodata_builder_.GetSize();
-    rodata_builder_.GetSection()->sh_link = rodata_builder_.GetLink();
-
-    // Get the layout of the text section.
-    text_builder_.GetSection()->sh_offset =
-        NextOffset<Elf_Word, Elf_Shdr>(*text_builder_.GetSection(),
-                                       *rodata_builder_.GetSection());
-    text_builder_.GetSection()->sh_addr = text_builder_.GetSection()->sh_offset;
-    text_builder_.GetSection()->sh_size = text_builder_.GetSize();
-    text_builder_.GetSection()->sh_link = text_builder_.GetLink();
-    CHECK_ALIGNED(rodata_builder_.GetSection()->sh_offset +
-                  rodata_builder_.GetSection()->sh_size, kPageSize);
-
-    // Get the layout of the .bss section.
-    bss_builder_.GetSection()->sh_offset =
-        NextOffset<Elf_Word, Elf_Shdr>(*bss_builder_.GetSection(),
-                                       *text_builder_.GetSection());
-    bss_builder_.GetSection()->sh_addr = bss_builder_.GetSection()->sh_offset;
-    bss_builder_.GetSection()->sh_size = bss_builder_.GetSize();
-    bss_builder_.GetSection()->sh_link = bss_builder_.GetLink();
-
-    // Get the layout of the dynamic section.
-    CHECK(IsAlignedParam(bss_builder_.GetSection()->sh_offset,
-                         dynamic_builder_.GetSection()->sh_addralign));
-    dynamic_builder_.GetSection()->sh_offset = bss_builder_.GetSection()->sh_offset;
-    dynamic_builder_.GetSection()->sh_addr =
-        NextOffset<Elf_Word, Elf_Shdr>(*dynamic_builder_.GetSection(), *bss_builder_.GetSection());
-    dynamic_builder_.GetSection()->sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
-    dynamic_builder_.GetSection()->sh_link = dynamic_builder_.GetLink();
-
-    if (debug_logging_) {
-      LOG(INFO) << "dynsym off=" << dynsym_builder_.GetSection()->sh_offset
-                << " dynsym size=" << dynsym_builder_.GetSection()->sh_size;
-      LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->GetSection()->sh_offset
-                << " dynstr size=" << dynsym_builder_.GetStrTab()->GetSection()->sh_size;
-      LOG(INFO) << "hash off=" << hash_builder_.GetSection()->sh_offset
-                << " hash size=" << hash_builder_.GetSection()->sh_size;
-      LOG(INFO) << "rodata off=" << rodata_builder_.GetSection()->sh_offset
-                << " rodata size=" << rodata_builder_.GetSection()->sh_size;
-      LOG(INFO) << "text off=" << text_builder_.GetSection()->sh_offset
-                << " text size=" << text_builder_.GetSection()->sh_size;
-      LOG(INFO) << "dynamic off=" << dynamic_builder_.GetSection()->sh_offset
-                << " dynamic size=" << dynamic_builder_.GetSection()->sh_size;
-    }
-
-    return true;
-  }
-
-  bool Write() {
-    std::vector<ElfFilePiece<Elf_Word>*> pieces;
-    Elf_Shdr* prev = dynamic_builder_.GetSection();
-    std::string strtab;
-
-    if (IncludingDebugSymbols()) {
-      // Setup .symtab
-      section_ptrs_.push_back(symtab_builder_.GetSection());
-      AssignSectionStr(&symtab_builder_, &shstrtab_);
-      symtab_builder_.SetSectionIndex(section_index_);
-      section_index_++;
-
-      // Setup .strtab
-      section_ptrs_.push_back(symtab_builder_.GetStrTab()->GetSection());
-      AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
-      symtab_builder_.GetStrTab()->SetSectionIndex(section_index_);
-      section_index_++;
-
-      strtab = symtab_builder_.GenerateStrtab();
-      if (debug_logging_) {
-        LOG(INFO) << "strtab size (bytes)    =" << strtab.size()
-                  << std::hex << " " << strtab.size();
-        LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
-                  << std::hex << " " << symtab_builder_.GetSize();
+      program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R, *eh_frame));
+      const Section* eh_frame_hdr = FindSection(".eh_frame_hdr");
+      if (eh_frame_hdr != nullptr) {
+        // Check layout: eh_frame is before eh_frame_hdr and there is no gap.
+        CHECK_LE(eh_frame->GetHeader()->sh_offset, eh_frame_hdr->GetHeader()->sh_offset);
+        CHECK_EQ(eh_frame->GetHeader()->sh_offset + eh_frame->GetHeader()->sh_size,
+                 eh_frame_hdr->GetHeader()->sh_offset);
+        // Extend the PT_LOAD of .eh_frame to include the .eh_frame_hdr as well.
+        program_headers.back().p_filesz += eh_frame_hdr->GetHeader()->sh_size;
+        program_headers.back().p_memsz  += eh_frame_hdr->GetHeader()->sh_size;
+        program_headers.push_back(MakeProgramHeader(PT_GNU_EH_FRAME, PF_R, *eh_frame_hdr));
       }
     }
+    CHECK_LE(program_headers.size(), kMaxProgramHeaders);
 
-    // Setup all the other sections.
-    for (auto* builder : other_builders_) {
-      section_ptrs_.push_back(builder->GetSection());
-      AssignSectionStr(builder, &shstrtab_);
-      builder->SetSectionIndex(section_index_);
-      section_index_++;
-    }
+    // Create the main ELF header.
+    Elf_Ehdr elf_header = MakeElfHeader(isa_);
+    elf_header.e_phoff = kProgramHeadersOffset;
+    elf_header.e_shoff = section_headers_offset;
+    elf_header.e_phnum = program_headers.size();
+    elf_header.e_shnum = section_headers.size();
+    elf_header.e_shstrndx = shstrtab_.GetSectionIndex();
 
-    // Setup shstrtab
-    section_ptrs_.push_back(shstrtab_builder_.GetSection());
-    AssignSectionStr(&shstrtab_builder_, &shstrtab_);
-    shstrtab_builder_.SetSectionIndex(section_index_);
-    section_index_++;
-
-    if (debug_logging_) {
-      LOG(INFO) << ".shstrtab size    (bytes)   =" << shstrtab_.size()
-                << std::hex << " " << shstrtab_.size();
-      LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
-                << std::hex << " " << section_ptrs_.size();
-    }
-
-    if (IncludingDebugSymbols()) {
-      // Get the layout of the symtab section.
-      symtab_builder_.GetSection()->sh_offset =
-          NextOffset<Elf_Word, Elf_Shdr>(*symtab_builder_.GetSection(),
-                                         *dynamic_builder_.GetSection());
-      symtab_builder_.GetSection()->sh_addr = 0;
-      // Add to leave space for the null symbol.
-      symtab_builder_.GetSection()->sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
-      symtab_builder_.GetSection()->sh_link = symtab_builder_.GetLink();
-
-      // Get the layout of the dynstr section.
-      symtab_builder_.GetStrTab()->GetSection()->sh_offset =
-          NextOffset<Elf_Word, Elf_Shdr>(*symtab_builder_.GetStrTab()->GetSection(),
-                                         *symtab_builder_.GetSection());
-      symtab_builder_.GetStrTab()->GetSection()->sh_addr = 0;
-      symtab_builder_.GetStrTab()->GetSection()->sh_size = strtab.size();
-      symtab_builder_.GetStrTab()->GetSection()->sh_link = symtab_builder_.GetStrTab()->GetLink();
-
-      prev = symtab_builder_.GetStrTab()->GetSection();
-      if (debug_logging_) {
-        LOG(INFO) << "symtab off=" << symtab_builder_.GetSection()->sh_offset
-                  << " symtab size=" << symtab_builder_.GetSection()->sh_size;
-        LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->GetSection()->sh_offset
-                  << " strtab size=" << symtab_builder_.GetStrTab()->GetSection()->sh_size;
-      }
-    }
-
-    // Get the layout of the extra sections without SHF_ALLOC flag.
-    // (This will deal with the debug sections if they are there)
-    for (auto* it : other_builders_) {
-      if ((it->GetSection()->sh_flags & SHF_ALLOC) == 0) {
-        it->GetSection()->sh_offset = NextOffset<Elf_Word, Elf_Shdr>(*it->GetSection(), *prev);
-        it->GetSection()->sh_addr = 0;
-        it->GetSection()->sh_size = it->GetBuffer()->size();
-        it->GetSection()->sh_link = it->GetLink();
-
-        // We postpone adding an ElfFilePiece to keep the order in "pieces."
-
-        prev = it->GetSection();
-        if (debug_logging_) {
-          LOG(INFO) << it->GetName() << " off=" << it->GetSection()->sh_offset
-                    << " size=" << it->GetSection()->sh_size;
-        }
-      }
-    }
-
-    // Get the layout of the shstrtab section
-    shstrtab_builder_.GetSection()->sh_offset =
-        NextOffset<Elf_Word, Elf_Shdr>(*shstrtab_builder_.GetSection(), *prev);
-    shstrtab_builder_.GetSection()->sh_addr = 0;
-    shstrtab_builder_.GetSection()->sh_size = shstrtab_.size();
-    shstrtab_builder_.GetSection()->sh_link = shstrtab_builder_.GetLink();
-    if (debug_logging_) {
-        LOG(INFO) << "shstrtab off=" << shstrtab_builder_.GetSection()->sh_offset
-                  << " shstrtab size=" << shstrtab_builder_.GetSection()->sh_size;
-    }
-
-    // The section list comes after come after.
-    Elf_Word sections_offset = RoundUp(
-        shstrtab_builder_.GetSection()->sh_offset + shstrtab_builder_.GetSection()->sh_size,
-        sizeof(Elf_Word));
-
-    // Setup the actual symbol arrays.
-    std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
-    CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.GetSection()->sh_size);
-    std::vector<Elf_Sym> symtab;
-    if (IncludingDebugSymbols()) {
-      symtab = symtab_builder_.GenerateSymtab();
-      CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.GetSection()->sh_size);
-    }
-
-    // Setup the dynamic section.
-    // This will add the 2 values we cannot know until now time, namely the size
-    // and the soname_offset.
-    std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
-                                                                  dynstr_soname_offset_);
-    CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.GetSection()->sh_size);
-
-    // Finish setup of the program headers now that we know the layout of the
-    // whole file.
-    Elf_Word load_r_size =
-        rodata_builder_.GetSection()->sh_offset + rodata_builder_.GetSection()->sh_size;
-    program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
-    program_headers_[PH_LOAD_R__].p_memsz =  load_r_size;
-    program_headers_[PH_LOAD_R__].p_align =  rodata_builder_.GetSection()->sh_addralign;
-
-    Elf_Word load_rx_size = text_builder_.GetSection()->sh_size;
-    program_headers_[PH_LOAD_R_X].p_offset = text_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_R_X].p_vaddr  = text_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_R_X].p_paddr  = text_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
-    program_headers_[PH_LOAD_R_X].p_memsz  = load_rx_size;
-    program_headers_[PH_LOAD_R_X].p_align  = text_builder_.GetSection()->sh_addralign;
-
-    program_headers_[PH_LOAD_RW_BSS].p_offset = bss_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_RW_BSS].p_vaddr  = bss_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_RW_BSS].p_paddr  = bss_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_RW_BSS].p_filesz = 0;
-    program_headers_[PH_LOAD_RW_BSS].p_memsz  = bss_builder_.GetSection()->sh_size;
-    program_headers_[PH_LOAD_RW_BSS].p_align  = bss_builder_.GetSection()->sh_addralign;
-
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_offset = dynamic_builder_.GetSection()->sh_offset;
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_vaddr  = dynamic_builder_.GetSection()->sh_addr;
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_paddr  = dynamic_builder_.GetSection()->sh_addr;
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_filesz = dynamic_builder_.GetSection()->sh_size;
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_memsz  = dynamic_builder_.GetSection()->sh_size;
-    program_headers_[PH_LOAD_RW_DYNAMIC].p_align  = dynamic_builder_.GetSection()->sh_addralign;
-
-    program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.GetSection()->sh_offset;
-    program_headers_[PH_DYNAMIC].p_vaddr  = dynamic_builder_.GetSection()->sh_addr;
-    program_headers_[PH_DYNAMIC].p_paddr  = dynamic_builder_.GetSection()->sh_addr;
-    program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.GetSection()->sh_size;
-    program_headers_[PH_DYNAMIC].p_memsz  = dynamic_builder_.GetSection()->sh_size;
-    program_headers_[PH_DYNAMIC].p_align  = dynamic_builder_.GetSection()->sh_addralign;
-
-    const auto* eh_frame_hdr = FindRawSection(".eh_frame_hdr");
-    if (eh_frame_hdr != nullptr) {
-      const auto* eh_frame = FindRawSection(".eh_frame");
-      // Check layout:
-      // 1) eh_frame is before eh_frame_hdr.
-      // 2) There's no gap.
-      CHECK(eh_frame != nullptr);
-      CHECK_LE(eh_frame->GetSection()->sh_offset, eh_frame_hdr->GetSection()->sh_offset);
-      CHECK_EQ(eh_frame->GetSection()->sh_offset + eh_frame->GetSection()->sh_size,
-               eh_frame_hdr->GetSection()->sh_offset);
-
-      program_headers_[PH_EH_FRAME_HDR].p_type   = PT_GNU_EH_FRAME;
-      program_headers_[PH_EH_FRAME_HDR].p_offset = eh_frame_hdr->GetSection()->sh_offset;
-      program_headers_[PH_EH_FRAME_HDR].p_vaddr  = eh_frame_hdr->GetSection()->sh_addr;
-      program_headers_[PH_EH_FRAME_HDR].p_paddr  = eh_frame_hdr->GetSection()->sh_addr;
-      program_headers_[PH_EH_FRAME_HDR].p_filesz = eh_frame_hdr->GetSection()->sh_size;
-      program_headers_[PH_EH_FRAME_HDR].p_memsz  = eh_frame_hdr->GetSection()->sh_size;
-      program_headers_[PH_EH_FRAME_HDR].p_align  = eh_frame_hdr->GetSection()->sh_addralign;
-    }
-
-    // Finish setup of the Ehdr values.
-    elf_header_.e_phoff = PHDR_OFFSET;
-    elf_header_.e_shoff = sections_offset;
-    elf_header_.e_phnum = (bss_builder_.GetSection()->sh_size != 0u) ? PH_NUM : PH_NUM - 1;
-    elf_header_.e_shnum = section_ptrs_.size();
-    elf_header_.e_shstrndx = shstrtab_builder_.GetSectionIndex();
-
-    // Add the rest of the pieces to the list.
-    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Elf Header", 0, &elf_header_,
-                                                      sizeof(elf_header_)));
-    if (bss_builder_.GetSection()->sh_size != 0u) {
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Program headers", PHDR_OFFSET,
-                                                        &program_headers_[0],
-                                                        elf_header_.e_phnum * sizeof(Elf_Phdr)));
-    } else {
-      // Skip PH_LOAD_RW_BSS.
-      Elf_Word part1_size = PH_LOAD_RW_BSS * sizeof(Elf_Phdr);
-      Elf_Word part2_size = (PH_NUM - PH_LOAD_RW_BSS - 1) * sizeof(Elf_Phdr);
-      CHECK_EQ(part1_size + part2_size, elf_header_.e_phnum * sizeof(Elf_Phdr));
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Program headers", PHDR_OFFSET,
-                                                        &program_headers_[0], part1_size));
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Program headers part 2",
-                                                        PHDR_OFFSET + part1_size,
-                                                        &program_headers_[PH_LOAD_RW_BSS + 1],
-                                                        part2_size));
-    }
-    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynamic",
-                                                      dynamic_builder_.GetSection()->sh_offset,
-                                                      dynamic.data(),
-                                                      dynamic_builder_.GetSection()->sh_size));
-    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynsym", dynsym_builder_.GetSection()->sh_offset,
-                                                      dynsym.data(),
-                                                      dynsym.size() * sizeof(Elf_Sym)));
-    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynstr",
-                                                    dynsym_builder_.GetStrTab()->GetSection()->sh_offset,
-                                                    dynstr_.c_str(), dynstr_.size()));
-    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".hash", hash_builder_.GetSection()->sh_offset,
-                                                      hash_.data(),
-                                                      hash_.size() * sizeof(Elf_Word)));
-    pieces.push_back(new ElfFileRodataPiece<Elf_Word>(rodata_builder_.GetSection()->sh_offset,
-                                                      oat_writer_));
-    pieces.push_back(new ElfFileOatTextPiece<Elf_Word>(text_builder_.GetSection()->sh_offset,
-                                                       oat_writer_));
-    if (IncludingDebugSymbols()) {
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".symtab",
-                                                        symtab_builder_.GetSection()->sh_offset,
-                                                        symtab.data(),
-                                                        symtab.size() * sizeof(Elf_Sym)));
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".strtab",
-                                                    symtab_builder_.GetStrTab()->GetSection()->sh_offset,
-                                                    strtab.c_str(), strtab.size()));
-    }
-    pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".shstrtab",
-                                                      shstrtab_builder_.GetSection()->sh_offset,
-                                                      &shstrtab_[0], shstrtab_.size()));
-    for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
-      // Just add all the sections in induvidually since they are all over the
-      // place on the heap/stack.
-      Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("section table piece", cur_off,
-                                                        section_ptrs_[i], sizeof(Elf_Shdr)));
-    }
-
-    // Postponed debug info.
-    for (auto* it : other_builders_) {
-      pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(it->GetName(), it->GetSection()->sh_offset,
-                                                        it->GetBuffer()->data(),
-                                                        it->GetBuffer()->size()));
-    }
-
-    if (!WriteOutFile(pieces)) {
-      LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
-
-      STLDeleteElements(&pieces);  // Have to manually clean pieces.
+    // Write all headers and section content to the file.
+    // Depending on the implementations of Section::Write, this
+    // might be just memory copies or some more elaborate operations.
+    if (!WriteArray(elf_file, &elf_header, 1)) {
+      LOG(INFO) << "Failed to write the ELF header";
       return false;
     }
-
-    STLDeleteElements(&pieces);  // Have to manually clean pieces.
+    if (!WriteArray(elf_file, program_headers.data(), program_headers.size())) {
+      LOG(INFO) << "Failed to write the program headers";
+      return false;
+    }
+    for (Section* section : sections) {
+      const Elf_Shdr* header = section->GetHeader();
+      if (header->sh_type != SHT_NOBITS) {
+        if (!SeekTo(elf_file, header->sh_offset) || !section->Write(elf_file)) {
+          LOG(INFO) << "Failed to write section " << section->GetName();
+          return false;
+        }
+        Elf_Word current_offset = lseek(elf_file->Fd(), 0, SEEK_CUR);
+        CHECK_EQ(current_offset, header->sh_offset + header->sh_size)
+          << "The number of bytes written does not match GetSize()";
+      }
+    }
+    if (!SeekTo(elf_file, section_headers_offset) ||
+        !WriteArray(elf_file, section_headers.data(), section_headers.size())) {
+      LOG(INFO) << "Failed to write the section headers";
+      return false;
+    }
     return true;
   }
 
-  // Adds the given raw section to the builder.  It does not take ownership.
-  void RegisterRawSection(ElfRawSectionBuilder<ElfTypes>* bld) {
-    other_builders_.push_back(bld);
+  // Adds the given section to the builder.  It does not take ownership.
+  void RegisterSection(Section* section) {
+    other_sections_.push_back(section);
   }
 
-  const ElfRawSectionBuilder<ElfTypes>* FindRawSection(const char* name) {
-    for (const auto* other_builder : other_builders_) {
-      if (other_builder->GetName() == name) {
-        return other_builder;
+  const Section* FindSection(const char* name) {
+    for (const auto* section : other_sections_) {
+      if (section->GetName() == name) {
+        return section;
       }
     }
     return nullptr;
   }
 
  private:
-  void SetISA(InstructionSet isa) {
+  static bool SeekTo(File* elf_file, Elf_Word offset) {
+    DCHECK_LE(lseek(elf_file->Fd(), 0, SEEK_CUR), static_cast<off_t>(offset))
+      << "Seeking backwards";
+    if (static_cast<off_t>(offset) != lseek(elf_file->Fd(), offset, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek in file " << elf_file->GetPath();
+      return false;
+    }
+    return true;
+  }
+
+  template<typename T>
+  static bool WriteArray(File* elf_file, const T* data, size_t count) {
+    DCHECK(data != nullptr);
+    if (!elf_file->WriteFully(data, count * sizeof(T))) {
+      PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
+      return false;
+    }
+    return true;
+  }
+
+  // Helper - create segment header based on memory range.
+  static Elf_Phdr MakeProgramHeader(Elf_Word type, Elf_Word flags,
+                                    Elf_Off offset, Elf_Word size, Elf_Word align) {
+    Elf_Phdr phdr = Elf_Phdr();
+    phdr.p_type    = type;
+    phdr.p_flags   = flags;
+    phdr.p_offset  = offset;
+    phdr.p_vaddr   = offset;
+    phdr.p_paddr   = offset;
+    phdr.p_filesz  = size;
+    phdr.p_memsz   = size;
+    phdr.p_align   = align;
+    return phdr;
+  }
+
+  // Helper - create segment header based on section header.
+  static Elf_Phdr MakeProgramHeader(Elf_Word type, Elf_Word flags,
+                                    const Section& section) {
+    const Elf_Shdr* shdr = section.GetHeader();
+    // Only run-time allocated sections should be in segment headers.
+    CHECK_NE(shdr->sh_flags & SHF_ALLOC, 0u);
+    Elf_Phdr phdr = Elf_Phdr();
+    phdr.p_type   = type;
+    phdr.p_flags  = flags;
+    phdr.p_offset = shdr->sh_offset;
+    phdr.p_vaddr  = shdr->sh_addr;
+    phdr.p_paddr  = shdr->sh_addr;
+    phdr.p_filesz = shdr->sh_type != SHT_NOBITS ? shdr->sh_size : 0u;
+    phdr.p_memsz  = shdr->sh_size;
+    phdr.p_align  = shdr->sh_addralign;
+    return phdr;
+  }
+
+  static Elf_Ehdr MakeElfHeader(InstructionSet isa) {
+    Elf_Ehdr elf_header = Elf_Ehdr();
     switch (isa) {
       case kArm:
         // Fall through.
       case kThumb2: {
-        elf_header_.e_machine = EM_ARM;
-        elf_header_.e_flags = EF_ARM_EABI_VER5;
+        elf_header.e_machine = EM_ARM;
+        elf_header.e_flags = EF_ARM_EABI_VER5;
         break;
       }
       case kArm64: {
-        elf_header_.e_machine = EM_AARCH64;
-        elf_header_.e_flags = 0;
+        elf_header.e_machine = EM_AARCH64;
+        elf_header.e_flags = 0;
         break;
       }
       case kX86: {
-        elf_header_.e_machine = EM_386;
-        elf_header_.e_flags = 0;
+        elf_header.e_machine = EM_386;
+        elf_header.e_flags = 0;
         break;
       }
       case kX86_64: {
-        elf_header_.e_machine = EM_X86_64;
-        elf_header_.e_flags = 0;
+        elf_header.e_machine = EM_X86_64;
+        elf_header.e_flags = 0;
         break;
       }
       case kMips: {
-        elf_header_.e_machine = EM_MIPS;
-        elf_header_.e_flags = (EF_MIPS_NOREORDER |
+        elf_header.e_machine = EM_MIPS;
+        elf_header.e_flags = (EF_MIPS_NOREORDER |
                                EF_MIPS_PIC       |
                                EF_MIPS_CPIC      |
                                EF_MIPS_ABI_O32   |
@@ -1250,147 +869,82 @@
         break;
       }
       case kMips64: {
-        elf_header_.e_machine = EM_MIPS;
-        elf_header_.e_flags = (EF_MIPS_NOREORDER |
+        elf_header.e_machine = EM_MIPS;
+        elf_header.e_flags = (EF_MIPS_NOREORDER |
                                EF_MIPS_PIC       |
                                EF_MIPS_CPIC      |
                                EF_MIPS_ARCH_64R6);
         break;
       }
-      default: {
-        fatal_error_ = true;
-        LOG(FATAL) << "Unknown instruction set: " << isa;
-        break;
+      case kNone: {
+        LOG(FATAL) << "No instruction set";
       }
     }
-  }
 
-  void SetupEhdr() {
-    memset(&elf_header_, 0, sizeof(elf_header_));
-    elf_header_.e_ident[EI_MAG0]       = ELFMAG0;
-    elf_header_.e_ident[EI_MAG1]       = ELFMAG1;
-    elf_header_.e_ident[EI_MAG2]       = ELFMAG2;
-    elf_header_.e_ident[EI_MAG3]       = ELFMAG3;
-    elf_header_.e_ident[EI_CLASS]      = (sizeof(Elf_Addr) == sizeof(Elf32_Addr))
+    elf_header.e_ident[EI_MAG0]       = ELFMAG0;
+    elf_header.e_ident[EI_MAG1]       = ELFMAG1;
+    elf_header.e_ident[EI_MAG2]       = ELFMAG2;
+    elf_header.e_ident[EI_MAG3]       = ELFMAG3;
+    elf_header.e_ident[EI_CLASS]      = (sizeof(Elf_Addr) == sizeof(Elf32_Addr))
                                          ? ELFCLASS32 : ELFCLASS64;;
-    elf_header_.e_ident[EI_DATA]       = ELFDATA2LSB;
-    elf_header_.e_ident[EI_VERSION]    = EV_CURRENT;
-    elf_header_.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
-    elf_header_.e_ident[EI_ABIVERSION] = 0;
-    elf_header_.e_type = ET_DYN;
-    elf_header_.e_version = 1;
-    elf_header_.e_entry = 0;
-    elf_header_.e_ehsize = sizeof(Elf_Ehdr);
-    elf_header_.e_phentsize = sizeof(Elf_Phdr);
-    elf_header_.e_shentsize = sizeof(Elf_Shdr);
-    elf_header_.e_phoff = sizeof(Elf_Ehdr);
+    elf_header.e_ident[EI_DATA]       = ELFDATA2LSB;
+    elf_header.e_ident[EI_VERSION]    = EV_CURRENT;
+    elf_header.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
+    elf_header.e_ident[EI_ABIVERSION] = 0;
+    elf_header.e_type = ET_DYN;
+    elf_header.e_version = 1;
+    elf_header.e_entry = 0;
+    elf_header.e_ehsize = sizeof(Elf_Ehdr);
+    elf_header.e_phentsize = sizeof(Elf_Phdr);
+    elf_header.e_shentsize = sizeof(Elf_Shdr);
+    elf_header.e_phoff = sizeof(Elf_Ehdr);
+    return elf_header;
   }
 
-  // Sets up a bunch of the required Dynamic Section entries.
-  // Namely it will initialize all the mandatory ones that it can.
-  // Specifically:
-  // DT_HASH
-  // DT_STRTAB
-  // DT_SYMTAB
-  // DT_SYMENT
-  //
-  // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
-  void SetupDynamic() {
-    dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
-    dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, dynsym_builder_.GetStrTab());
-    dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
-    dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
+  void BuildDynamicSection(const std::string& elf_file_path) {
+    std::string soname(elf_file_path);
+    size_t directory_separator_pos = soname.rfind('/');
+    if (directory_separator_pos != std::string::npos) {
+      soname = soname.substr(directory_separator_pos + 1);
+    }
+    // NB: We must add the name before adding DT_STRSZ.
+    Elf_Word soname_offset = dynstr_.AddName(soname);
+
+    dynamic_.AddDynamicTag(DT_HASH, 0, &hash_);
+    dynamic_.AddDynamicTag(DT_STRTAB, 0, &dynstr_);
+    dynamic_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_);
+    dynamic_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym), nullptr);
+    dynamic_.AddDynamicTag(DT_STRSZ, dynstr_.GetSize(), nullptr);
+    dynamic_.AddDynamicTag(DT_SONAME, soname_offset, nullptr);
   }
 
-  // Sets up the basic dynamic symbols that are needed, namely all those we
-  // can know already.
-  //
-  // Specifically adds:
-  // oatdata
-  // oatexec
-  // oatlastword
-  void SetupRequiredSymbols() {
-    dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
-                              rodata_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
-    dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
-                              text_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
-    dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.GetSize() - 4,
-                              true, 4, STB_GLOBAL, STT_OBJECT);
-    if (bss_builder_.GetSize() != 0u) {
-      dynsym_builder_.AddSymbol("oatbss", &bss_builder_, 0, true,
-                                bss_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
-      dynsym_builder_.AddSymbol("oatbsslastword", &bss_builder_, bss_builder_.GetSize() - 4,
-                                true, 4, STB_GLOBAL, STT_OBJECT);
+  void BuildDynsymSection() {
+    dynsym_.AddSymbol("oatdata", &rodata_, 0, true,
+                      rodata_.GetSize(), STB_GLOBAL, STT_OBJECT);
+    dynsym_.AddSymbol("oatexec", &text_, 0, true,
+                      text_.GetSize(), STB_GLOBAL, STT_OBJECT);
+    dynsym_.AddSymbol("oatlastword", &text_, text_.GetSize() - 4,
+                      true, 4, STB_GLOBAL, STT_OBJECT);
+    if (bss_.GetSize() != 0u) {
+      dynsym_.AddSymbol("oatbss", &bss_, 0, true,
+                        bss_.GetSize(), STB_GLOBAL, STT_OBJECT);
+      dynsym_.AddSymbol("oatbsslastword", &bss_, bss_.GetSize() - 4,
+                        true, 4, STB_GLOBAL, STT_OBJECT);
     }
   }
 
-  void AssignSectionStr(ElfSectionBuilder<ElfTypes>* builder, std::string* strtab) {
-    builder->GetSection()->sh_name = strtab->size();
-    *strtab += builder->GetName();
-    *strtab += '\0';
-    if (debug_logging_) {
-      LOG(INFO) << "adding section name \"" << builder->GetName() << "\" "
-                << "to shstrtab at offset " << builder->GetSection()->sh_name;
-    }
-  }
-
-
-  // Write each of the pieces out to the file.
-  bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces) {
-    for (auto it = pieces.begin(); it != pieces.end(); ++it) {
-      if (!(*it)->Write(elf_file_)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  bool IncludingDebugSymbols() const {
-    return add_symbols_ && symtab_builder_.GetSize() > 1;
-  }
-
-  CodeOutput* const oat_writer_;
-  File* const elf_file_;
-  const bool add_symbols_;
-  const bool debug_logging_;
-
-  bool fatal_error_ = false;
-
-  // What phdr is.
-  static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
-  enum : uint8_t {
-    PH_PHDR             = 0,
-    PH_LOAD_R__         = 1,
-    PH_LOAD_R_X         = 2,
-    PH_LOAD_RW_BSS      = 3,
-    PH_LOAD_RW_DYNAMIC  = 4,
-    PH_DYNAMIC          = 5,
-    PH_EH_FRAME_HDR     = 6,
-    PH_NUM              = 7,
-  };
-  static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
-  Elf_Phdr program_headers_[PH_NUM];
-
-  Elf_Ehdr elf_header_;
-
-  Elf_Shdr null_hdr_;
-  std::string shstrtab_;
-  // The index of the current section being built. The first being 1.
-  uint32_t section_index_;
-  std::string dynstr_;
-  uint32_t dynstr_soname_offset_;
-  std::vector<const Elf_Shdr*> section_ptrs_;
-  std::vector<Elf_Word> hash_;
-
-  ElfOatSectionBuilder<ElfTypes> text_builder_;
-  ElfOatSectionBuilder<ElfTypes> rodata_builder_;
-  ElfOatSectionBuilder<ElfTypes> bss_builder_;
-  ElfSymtabBuilder<ElfTypes> dynsym_builder_;
-  ElfSymtabBuilder<ElfTypes> symtab_builder_;
-  ElfSectionBuilder<ElfTypes> hash_builder_;
-  ElfDynamicBuilder<ElfTypes> dynamic_builder_;
-  ElfSectionBuilder<ElfTypes> shstrtab_builder_;
-  std::vector<ElfRawSectionBuilder<ElfTypes>*> other_builders_;
+  InstructionSet isa_;
+  StrtabSection dynstr_;
+  SymtabSection dynsym_;
+  HashSection hash_;
+  OatSection rodata_;
+  OatSection text_;
+  NoBitsSection bss_;
+  DynamicSection dynamic_;
+  StrtabSection strtab_;
+  SymtabSection symtab_;
+  std::vector<Section*> other_sections_;
+  StrtabSection shstrtab_;
 
   DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
 };
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 3b2ca94..178aa03 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -21,7 +21,6 @@
 
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
-#include "buffered_output_stream.h"
 #include "compiled_method.h"
 #include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
@@ -30,7 +29,6 @@
 #include "elf_file.h"
 #include "elf_utils.h"
 #include "elf_writer_debug.h"
-#include "file_output_stream.h"
 #include "globals.h"
 #include "leb128.h"
 #include "oat.h"
@@ -50,20 +48,6 @@
   return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
 }
 
-class OatWriterWrapper FINAL : public CodeOutput {
- public:
-  explicit OatWriterWrapper(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
-
-  void SetCodeOffset(size_t offset) {
-    oat_writer_->SetOatDataOffset(offset);
-  }
-  bool Write(OutputStream* out) OVERRIDE {
-    return oat_writer_->Write(out);
-  }
- private:
-  OatWriter* const oat_writer_;
-};
-
 template <typename ElfTypes>
 static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer);
 
@@ -99,18 +83,29 @@
   buffer->push_back(0);  // End of sections.
 }
 
-template<typename AddressType, bool SubtractPatchLocation = false>
-static void PatchAddresses(const std::vector<uintptr_t>* patch_locations,
-                           AddressType delta, std::vector<uint8_t>* buffer) {
-  // Addresses in .debug_* sections are unaligned.
-  typedef __attribute__((__aligned__(1))) AddressType UnalignedAddressType;
-  if (patch_locations != nullptr) {
-    for (uintptr_t patch_location : *patch_locations) {
-      *reinterpret_cast<UnalignedAddressType*>(buffer->data() + patch_location) +=
-          delta - (SubtractPatchLocation ? patch_location : 0);
-    }
+class RodataWriter FINAL : public CodeOutput {
+ public:
+  explicit RodataWriter(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+  bool Write(OutputStream* out) OVERRIDE {
+    return oat_writer_->WriteRodata(out);
   }
-}
+
+ private:
+  OatWriter* oat_writer_;
+};
+
+class TextWriter FINAL : public CodeOutput {
+ public:
+  explicit TextWriter(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+  bool Write(OutputStream* out) OVERRIDE {
+    return oat_writer_->WriteCode(out);
+  }
+
+ private:
+  OatWriter* oat_writer_;
+};
 
 template <typename ElfTypes>
 bool ElfWriterQuick<ElfTypes>::Write(
@@ -118,106 +113,81 @@
     const std::vector<const DexFile*>& dex_files_unused ATTRIBUTE_UNUSED,
     const std::string& android_root_unused ATTRIBUTE_UNUSED,
     bool is_host_unused ATTRIBUTE_UNUSED) {
-  constexpr bool debug = false;
-  const OatHeader& oat_header = oat_writer->GetOatHeader();
-  typename ElfTypes::Word oat_data_size = oat_header.GetExecutableOffset();
-  uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
-  uint32_t oat_bss_size = oat_writer->GetBssSize();
+  const InstructionSet isa = compiler_driver_->GetInstructionSet();
 
-  OatWriterWrapper wrapper(oat_writer);
-
+  // Setup the builder with the main OAT sections (.rodata .text .bss).
+  const size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
+  const size_t text_size = oat_writer->GetSize() - rodata_size;
+  const size_t bss_size = oat_writer->GetBssSize();
+  RodataWriter rodata_writer(oat_writer);
+  TextWriter text_writer(oat_writer);
   std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(
-      &wrapper,
-      elf_file_,
-      compiler_driver_->GetInstructionSet(),
-      0,
-      oat_data_size,
-      oat_data_size,
-      oat_exec_size,
-      RoundUp(oat_data_size + oat_exec_size, kPageSize),
-      oat_bss_size,
-      compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
-      debug));
+      isa, rodata_size, &rodata_writer, text_size, &text_writer, bss_size));
 
-  InstructionSet isa = compiler_driver_->GetInstructionSet();
-  int alignment = GetInstructionSetPointerSize(isa);
-  typedef ElfRawSectionBuilder<ElfTypes> RawSection;
-  RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, alignment, 0);
-  RawSection eh_frame_hdr(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
-  RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-  RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-  RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-  RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+  // Add debug sections.
+  // They are stack allocated here (in the same scope as the builder),
+  // but they are registred with the builder only if they are used.
+  using RawSection = typename ElfBuilder<ElfTypes>::RawSection;
+  const auto* text = builder->GetText();
+  constexpr bool absolute = false;  // patch to make absolute addresses.
+  constexpr bool relative = true;  // patch to make relative addresses.
+  const bool is64bit = Is64BitInstructionSet(isa);
+  RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC,
+                      nullptr, 0, kPageSize, 0, text, relative, is64bit);
+  RawSection eh_frame_hdr(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC,
+                          nullptr, 0, 4, 0);
+  RawSection debug_info(".debug_info", SHT_PROGBITS, 0,
+                        nullptr, 0, 1, 0, text, absolute, false /* 32bit */);
+  RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0,
+                          nullptr, 0, 1, 0);
+  RawSection debug_str(".debug_str", SHT_PROGBITS, 0,
+                       nullptr, 0, 1, 0);
+  RawSection debug_line(".debug_line", SHT_PROGBITS, 0,
+                        nullptr, 0, 1, 0, text, absolute, false /* 32bit */);
+  if (!oat_writer->GetMethodDebugInfo().empty()) {
+    if (compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
+      dwarf::WriteEhFrame(
+          compiler_driver_, oat_writer, dwarf::DW_EH_PE_pcrel,
+          eh_frame.GetBuffer(), eh_frame.GetPatchLocations(),
+          eh_frame_hdr.GetBuffer());
+      builder->RegisterSection(&eh_frame);
+      builder->RegisterSection(&eh_frame_hdr);
+    }
+    if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+      // Add methods to .symtab.
+      WriteDebugSymbols(builder.get(), oat_writer);
+      // Generate DWARF .debug_* sections.
+      dwarf::WriteDebugSections(
+          compiler_driver_, oat_writer,
+          debug_info.GetBuffer(), debug_info.GetPatchLocations(),
+          debug_abbrev.GetBuffer(),
+          debug_str.GetBuffer(),
+          debug_line.GetBuffer(), debug_line.GetPatchLocations());
+      builder->RegisterSection(&debug_info);
+      builder->RegisterSection(&debug_abbrev);
+      builder->RegisterSection(&debug_str);
+      builder->RegisterSection(&debug_line);
+      *oat_writer->GetAbsolutePatchLocationsFor(".debug_info") =
+          *debug_info.GetPatchLocations();
+      *oat_writer->GetAbsolutePatchLocationsFor(".debug_line") =
+          *debug_line.GetPatchLocations();
+    }
+  }
+
+  // Add relocation section.
   RawSection oat_patches(".oat_patches", SHT_OAT_PATCH, 0, nullptr, 0, 1, 0);
-
-  // Do not add to .oat_patches since we will make the addresses relative.
-  std::vector<uintptr_t> eh_frame_patches;
-  if (compiler_driver_->GetCompilerOptions().GetIncludeCFI() &&
-      !oat_writer->GetMethodDebugInfo().empty()) {
-    dwarf::WriteEhFrame(compiler_driver_, oat_writer,
-                        dwarf::DW_EH_PE_pcrel,
-                        eh_frame.GetBuffer(), &eh_frame_patches,
-                        eh_frame_hdr.GetBuffer());
-    builder->RegisterRawSection(&eh_frame);
-    builder->RegisterRawSection(&eh_frame_hdr);
-  }
-
-  // Must be done after .eh_frame is created since it is used in the Elf layout.
-  if (!builder->Init()) {
-    return false;
-  }
-
-  std::vector<uintptr_t>* debug_info_patches = nullptr;
-  std::vector<uintptr_t>* debug_line_patches = nullptr;
-  if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols() &&
-      !oat_writer->GetMethodDebugInfo().empty()) {
-    // Add methods to .symtab.
-    WriteDebugSymbols(builder.get(), oat_writer);
-    // Generate DWARF .debug_* sections.
-    debug_info_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_info");
-    debug_line_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_line");
-    dwarf::WriteDebugSections(compiler_driver_, oat_writer,
-                              debug_info.GetBuffer(), debug_info_patches,
-                              debug_abbrev.GetBuffer(),
-                              debug_str.GetBuffer(),
-                              debug_line.GetBuffer(), debug_line_patches);
-    builder->RegisterRawSection(&debug_info);
-    builder->RegisterRawSection(&debug_abbrev);
-    builder->RegisterRawSection(&debug_str);
-    builder->RegisterRawSection(&debug_line);
-  }
-
   if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() ||
       // ElfWriter::Fixup will be called regardless and it needs to be able
       // to patch debug sections so we have to include patches for them.
       compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
     EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer());
-    builder->RegisterRawSection(&oat_patches);
+    builder->RegisterSection(&oat_patches);
   }
 
-  // We know where .text and .eh_frame will be located, so patch the addresses.
-  typename ElfTypes::Addr text_addr = builder->GetTextBuilder().GetSection()->sh_addr;
-  // TODO: Simplify once we use Elf64 - we can use ElfTypes::Addr instead of branching.
-  if (Is64BitInstructionSet(compiler_driver_->GetInstructionSet())) {
-    // relative_address = (text_addr + address) - (eh_frame_addr + patch_location);
-    PatchAddresses<uint64_t, true>(&eh_frame_patches,
-        text_addr - eh_frame.GetSection()->sh_addr, eh_frame.GetBuffer());
-    PatchAddresses<uint64_t>(debug_info_patches, text_addr, debug_info.GetBuffer());
-    PatchAddresses<uint64_t>(debug_line_patches, text_addr, debug_line.GetBuffer());
-  } else {
-    // relative_address = (text_addr + address) - (eh_frame_addr + patch_location);
-    PatchAddresses<uint32_t, true>(&eh_frame_patches,
-        text_addr - eh_frame.GetSection()->sh_addr, eh_frame.GetBuffer());
-    PatchAddresses<uint32_t>(debug_info_patches, text_addr, debug_info.GetBuffer());
-    PatchAddresses<uint32_t>(debug_line_patches, text_addr, debug_line.GetBuffer());
-  }
-
-  return builder->Write();
+  return builder->Write(elf_file_);
 }
 
 template <typename ElfTypes>
-// Do not inline to avoid Clang stack frame problems. b/18738594
-NO_INLINE
 static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer) {
   const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetMethodDebugInfo();
 
@@ -230,7 +200,7 @@
     }
   }
 
-  ElfSymtabBuilder<ElfTypes>* symtab = builder->GetSymtabBuilder();
+  auto* symtab = builder->GetSymtab();
   for (auto it = method_info.begin(); it != method_info.end(); ++it) {
     std::string name = PrettyMethod(it->dex_method_index_, *it->dex_file_, true);
     if (deduped_addresses.find(it->low_pc_) != deduped_addresses.end()) {
@@ -240,13 +210,13 @@
     uint32_t low_pc = it->low_pc_;
     // Add in code delta, e.g., thumb bit 0 for Thumb2 code.
     low_pc += it->compiled_method_->CodeDelta();
-    symtab->AddSymbol(name, &builder->GetTextBuilder(), low_pc,
+    symtab->AddSymbol(name, builder->GetText(), low_pc,
                       true, it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
 
     // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
     // instructions, so that disassembler tools can correctly disassemble.
     if (it->compiled_method_->GetInstructionSet() == kThumb2) {
-      symtab->AddSymbol("$t", &builder->GetTextBuilder(), it->low_pc_ & ~1, true,
+      symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
                         0, STB_LOCAL, STT_NOTYPE);
     }
   }
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index d2d38da..15b4017 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1112,13 +1112,14 @@
   return offset;
 }
 
-bool OatWriter::Write(OutputStream* out) {
+bool OatWriter::WriteRodata(OutputStream* out) {
   const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
   if (raw_file_offset == (off_t) -1) {
     LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
     return false;
   }
   const size_t file_offset = static_cast<size_t>(raw_file_offset);
+  oat_data_offset_ = file_offset;
 
   // Reserve space for header. It will be written last - after updating the checksum.
   size_t header_size = oat_header_->GetHeaderSize();
@@ -1146,6 +1147,27 @@
     return false;
   }
 
+  // Write padding.
+  off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent);
+  relative_offset += size_executable_offset_alignment_;
+  DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset());
+  size_t expected_file_offset = file_offset + relative_offset;
+  if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
+    PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
+                << " Expected: " << expected_file_offset << " File: " << out->GetLocation();
+    return 0;
+  }
+  DCHECK_OFFSET();
+
+  return true;
+}
+
+bool OatWriter::WriteCode(OutputStream* out) {
+  size_t header_size = oat_header_->GetHeaderSize();
+  const size_t file_offset = oat_data_offset_;
+  size_t relative_offset = oat_header_->GetExecutableOffset();
+  DCHECK_OFFSET();
+
   relative_offset = WriteCode(out, file_offset, relative_offset);
   if (relative_offset == 0) {
     LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
@@ -1215,7 +1237,7 @@
     PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation();
     return false;
   }
-  DCHECK_EQ(raw_file_offset, out->Seek(0, kSeekCurrent));
+  DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent)));
   if (!out->WriteFully(oat_header_, header_size)) {
     PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
     return false;
@@ -1290,16 +1312,6 @@
 }
 
 size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset) {
-  off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent);
-  relative_offset += size_executable_offset_alignment_;
-  DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset());
-  size_t expected_file_offset = file_offset + relative_offset;
-  if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
-    PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
-                << " Expected: " << expected_file_offset << " File: " << out->GetLocation();
-    return 0;
-  }
-  DCHECK_OFFSET();
   if (compiler_driver_->IsImage()) {
     InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
 
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 8c79b44..6f1b4ec 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -118,11 +118,8 @@
     return it.first->second.get();
   }
 
-  void SetOatDataOffset(size_t oat_data_offset) {
-    oat_data_offset_ = oat_data_offset;
-  }
-
-  bool Write(OutputStream* out);
+  bool WriteRodata(OutputStream* out);
+  bool WriteCode(OutputStream* out);
 
   ~OatWriter();
 
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index dc3124b..8ea8f3c 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -170,7 +170,8 @@
     }
   }
 
-  // Ensure the uses of `instruction` are defined in a block of the graph.
+  // Ensure the uses of `instruction` are defined in a block of the graph,
+  // and the entry in the use list is consistent.
   for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
        !use_it.Done(); use_it.Advance()) {
     HInstruction* use = use_it.Current()->GetUser();
@@ -184,6 +185,27 @@
                             use->GetId(),
                             instruction->GetId()));
     }
+    size_t use_index = use_it.Current()->GetIndex();
+    if ((use_index >= use->InputCount()) || (use->InputAt(use_index) != instruction)) {
+      AddError(StringPrintf("User %s:%d of instruction %d has a wrong "
+                            "UseListNode index.",
+                            use->DebugName(),
+                            use->GetId(),
+                            instruction->GetId()));
+    }
+  }
+
+  // Ensure the environment uses entries are consistent.
+  for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
+       !use_it.Done(); use_it.Advance()) {
+    HEnvironment* use = use_it.Current()->GetUser();
+    size_t use_index = use_it.Current()->GetIndex();
+    if ((use_index >= use->Size()) || (use->GetInstructionAt(use_index) != instruction)) {
+      AddError(StringPrintf("Environment user of %s:%d has a wrong "
+                            "UseListNode index.",
+                            instruction->DebugName(),
+                            instruction->GetId()));
+    }
   }
 
   // Ensure 'instruction' has pointers to its inputs' use entries.
@@ -191,7 +213,11 @@
     HUserRecord<HInstruction*> input_record = instruction->InputRecordAt(i);
     HInstruction* input = input_record.GetInstruction();
     HUseListNode<HInstruction*>* use_node = input_record.GetUseNode();
-    if (use_node == nullptr || !input->GetUses().Contains(use_node)) {
+    size_t use_index = use_node->GetIndex();
+    if ((use_node == nullptr)
+        || !input->GetUses().Contains(use_node)
+        || (use_index >= e)
+        || (use_index != i)) {
       AddError(StringPrintf("Instruction %s:%d has an invalid pointer to use entry "
                             "at input %u (%s:%d).",
                             instruction->DebugName(),
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f07f4c7..d3ee770 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -712,6 +712,9 @@
 void HPhi::RemoveInputAt(size_t index) {
   RemoveAsUserOfInput(index);
   inputs_.DeleteAt(index);
+  for (size_t i = index, e = InputCount(); i < e; ++i) {
+    InputRecordAt(i).GetUseNode()->SetIndex(i);
+  }
 }
 
 #define DEFINE_ACCEPT(name, super)                                             \
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 50eecff..63f3c95 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -850,13 +850,14 @@
   HUseListNode* GetNext() const { return next_; }
   T GetUser() const { return user_; }
   size_t GetIndex() const { return index_; }
+  void SetIndex(size_t index) { index_ = index; }
 
  private:
   HUseListNode(T user, size_t index)
       : user_(user), index_(index), prev_(nullptr), next_(nullptr) {}
 
   T const user_;
-  const size_t index_;
+  size_t index_;
   HUseListNode<T>* prev_;
   HUseListNode<T>* next_;
 
@@ -1090,7 +1091,7 @@
 
   GrowableArray<HUserRecord<HEnvironment*> > vregs_;
 
-  friend HInstruction;
+  friend class HInstruction;
 
   DISALLOW_COPY_AND_ASSIGN(HEnvironment);
 };
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index f2e35af..b9a3d37 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -76,41 +76,38 @@
   "kClassRoots",
 };
 
-class OatSymbolizer FINAL : public CodeOutput {
+class OatSymbolizer FINAL {
  public:
-  explicit OatSymbolizer(const OatFile* oat_file, const std::string& output_name) :
-      oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr),
-      output_name_(output_name.empty() ? "symbolized.oat" : output_name) {
-  }
+  class RodataWriter FINAL : public CodeOutput {
+   public:
+    explicit RodataWriter(const OatFile* oat_file) : oat_file_(oat_file) {}
 
-  bool Init() {
-    Elf32_Word oat_data_size = oat_file_->GetOatHeader().GetExecutableOffset();
-
-    uint32_t diff = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
-    uint32_t oat_exec_size = diff - oat_data_size;
-    uint32_t oat_bss_size = oat_file_->BssSize();
-
-    elf_output_ = OS::CreateEmptyFile(output_name_.c_str());
-
-    builder_.reset(new ElfBuilder<ElfTypes32>(
-        this,
-        elf_output_,
-        oat_file_->GetOatHeader().GetInstructionSet(),
-        0,
-        oat_data_size,
-        oat_data_size,
-        oat_exec_size,
-        RoundUp(oat_data_size + oat_exec_size, kPageSize),
-        oat_bss_size,
-        true,
-        false));
-
-    if (!builder_->Init()) {
-      builder_.reset(nullptr);
-      return false;
+    bool Write(OutputStream* out) OVERRIDE {
+      const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset();
+      return out->WriteFully(oat_file_->Begin(), rodata_size);
     }
 
-    return true;
+   private:
+    const OatFile* oat_file_;
+  };
+
+  class TextWriter FINAL : public CodeOutput {
+   public:
+    explicit TextWriter(const OatFile* oat_file) : oat_file_(oat_file) {}
+
+    bool Write(OutputStream* out) OVERRIDE {
+      const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset();
+      const uint8_t* text_begin = oat_file_->Begin() + rodata_size;
+      return out->WriteFully(text_begin, oat_file_->End() - text_begin);
+    }
+
+   private:
+    const OatFile* oat_file_;
+  };
+
+  explicit OatSymbolizer(const OatFile* oat_file, const std::string& output_name) :
+      oat_file_(oat_file), builder_(nullptr),
+      output_name_(output_name.empty() ? "symbolized.oat" : output_name) {
   }
 
   typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
@@ -122,9 +119,17 @@
                                           uint32_t);
 
   bool Symbolize() {
-    if (builder_.get() == nullptr) {
-      return false;
-    }
+    Elf32_Word rodata_size = oat_file_->GetOatHeader().GetExecutableOffset();
+    uint32_t size = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
+    uint32_t text_size = size - rodata_size;
+    uint32_t bss_size = oat_file_->BssSize();
+    RodataWriter rodata_writer(oat_file_);
+    TextWriter text_writer(oat_file_);
+    builder_.reset(new ElfBuilder<ElfTypes32>(
+        oat_file_->GetOatHeader().GetInstructionSet(),
+        rodata_size, &rodata_writer,
+        text_size, &text_writer,
+        bss_size));
 
     Walk(&art::OatSymbolizer::RegisterForDedup);
 
@@ -132,10 +137,11 @@
 
     Walk(&art::OatSymbolizer::AddSymbol);
 
-    bool result = builder_->Write();
+    File* elf_output = OS::CreateEmptyFile(output_name_.c_str());
+    bool result = builder_->Write(elf_output);
 
     // Ignore I/O errors.
-    UNUSED(elf_output_->FlushClose());
+    UNUSED(elf_output->FlushClose());
 
     return result;
   }
@@ -269,24 +275,14 @@
         pretty_name = "[Dedup]" + pretty_name;
       }
 
-      ElfSymtabBuilder<ElfTypes32>* symtab = builder_->GetSymtabBuilder();
+      auto* symtab = builder_->GetSymtab();
 
-      symtab->AddSymbol(pretty_name, &builder_->GetTextBuilder(),
+      symtab->AddSymbol(pretty_name, builder_->GetText(),
           oat_method.GetCodeOffset() - oat_file_->GetOatHeader().GetExecutableOffset(),
           true, oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC);
     }
   }
 
-  // Set oat data offset. Required by ElfBuilder/CodeOutput.
-  void SetCodeOffset(size_t offset ATTRIBUTE_UNUSED) {
-    // Nothing to do.
-  }
-
-  // Write oat code. Required by ElfBuilder/CodeOutput.
-  bool Write(OutputStream* out) {
-    return out->WriteFully(oat_file_->Begin(), oat_file_->End() - oat_file_->Begin());
-  }
-
  private:
   static void SkipAllFields(ClassDataItemIterator* it) {
     while (it->HasNextStaticField()) {
@@ -299,7 +295,6 @@
 
   const OatFile* oat_file_;
   std::unique_ptr<ElfBuilder<ElfTypes32> > builder_;
-  File* elf_output_;
   std::unordered_map<uint32_t, uint32_t> state_;
   const std::string output_name_;
 };
@@ -2203,10 +2198,6 @@
   }
 
   OatSymbolizer oat_symbolizer(oat_file, output_name);
-  if (!oat_symbolizer.Init()) {
-    fprintf(stderr, "Failed to initialize symbolizer\n");
-    return EXIT_FAILURE;
-  }
   if (!oat_symbolizer.Symbolize()) {
     fprintf(stderr, "Failed to symbolize\n");
     return EXIT_FAILURE;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 962e821..b099088 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -82,10 +82,6 @@
 
 static constexpr bool kSanityCheckObjects = kIsDebugBuild;
 
-// Do a simple class redefinition check in OpenDexFilesFromOat. This is a conservative check to
-// avoid problems with compile-time class-path != runtime class-path.
-static constexpr bool kCheckForDexCollisions = true;
-
 static void ThrowNoClassDefFoundError(const char* fmt, ...)
     __attribute__((__format__(__printf__, 1, 2)))
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -743,6 +739,8 @@
     const char* rhsDescriptor = rhs.cached_descriptor_;
     int cmp = strcmp(lhsDescriptor, rhsDescriptor);
     if (cmp != 0) {
+      // Note that the order must be reversed. We want to iterate over the classes in dex files.
+      // They are sorted lexicographically. Thus, the priority-queue must be a min-queue.
       return cmp > 0;
     }
     return dex_file_ < rhs.dex_file_;
@@ -768,6 +766,11 @@
     return dex_file_;
   }
 
+  void DeleteDexFile() {
+    delete dex_file_;
+    dex_file_ = nullptr;
+  }
+
  private:
   static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) {
     const DexFile::ClassDef& class_def = dex_file->GetClassDef(static_cast<uint16_t>(index));
@@ -799,13 +802,13 @@
   }
 }
 
-static void AddNext(const DexFileAndClassPair& original,
+static void AddNext(DexFileAndClassPair* original,
                     std::priority_queue<DexFileAndClassPair>* heap) {
-  if (original.DexFileHasMoreClasses()) {
-    heap->push(original.GetNext());
+  if (original->DexFileHasMoreClasses()) {
+    heap->push(original->GetNext());
   } else {
     // Need to delete the dex file.
-    delete original.GetDexFile();
+    original->DeleteDexFile();
   }
 }
 
@@ -824,19 +827,17 @@
 // the two elements agree on whether their dex file was from an already-loaded oat-file or the
 // new oat file. Any disagreement indicates a collision.
 bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) {
-  if (!kCheckForDexCollisions) {
-    return false;
-  }
-
   // Dex files are registered late - once a class is actually being loaded. We have to compare
-  // against the open oat files.
+  // against the open oat files. Take the dex_lock_ that protects oat_files_ accesses.
   ReaderMutexLock mu(Thread::Current(), dex_lock_);
 
-  std::priority_queue<DexFileAndClassPair> heap;
+  std::priority_queue<DexFileAndClassPair> queue;
 
   // Add dex files from already loaded oat files, but skip boot.
   {
-    // To grab the boot oat, look at the dex files in the boot classpath.
+    // To grab the boot oat, look at the dex files in the boot classpath. Any of those is fine, as
+    // they were all compiled into the same oat file. So grab the first one, which is guaranteed to
+    // exist if the boot class-path isn't empty.
     const OatFile* boot_oat = nullptr;
     if (!boot_class_path_.empty()) {
       const DexFile* boot_dex_file = boot_class_path_[0];
@@ -850,26 +851,26 @@
       if (loaded_oat_file == boot_oat) {
         continue;
       }
-      AddDexFilesFromOat(loaded_oat_file, true, &heap);
+      AddDexFilesFromOat(loaded_oat_file, true, &queue);
     }
   }
 
-  if (heap.empty()) {
+  if (queue.empty()) {
     // No other oat files, return early.
     return false;
   }
 
   // Add dex files from the oat file to check.
-  AddDexFilesFromOat(oat_file, false, &heap);
+  AddDexFilesFromOat(oat_file, false, &queue);
 
-  // Now drain the heap.
-  while (!heap.empty()) {
-    DexFileAndClassPair compare_pop = heap.top();
-    heap.pop();
+  // Now drain the queue.
+  while (!queue.empty()) {
+    DexFileAndClassPair compare_pop = queue.top();
+    queue.pop();
 
     // Compare against the following elements.
-    while (!heap.empty()) {
-      DexFileAndClassPair top = heap.top();
+    while (!queue.empty()) {
+      DexFileAndClassPair top = queue.top();
 
       if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) {
         // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files.
@@ -879,18 +880,18 @@
                            compare_pop.GetCachedDescriptor(),
                            compare_pop.GetDexFile()->GetLocation().c_str(),
                            top.GetDexFile()->GetLocation().c_str());
-          FreeDexFilesInHeap(&heap);
+          FreeDexFilesInHeap(&queue);
           return true;
         }
         // Pop it.
-        heap.pop();
-        AddNext(top, &heap);
+        queue.pop();
+        AddNext(&top, &queue);
       } else {
         // Something else. Done here.
         break;
       }
     }
-    AddNext(compare_pop, &heap);
+    AddNext(&compare_pop, &queue);
   }
 
   return false;
@@ -941,11 +942,10 @@
     // Get the oat file on disk.
     std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
     if (oat_file.get() != nullptr) {
-      // Take the file only if it has no collisions.
-      if (!HasCollisions(oat_file.get(), &error_msg)) {
-        source_oat_file = oat_file.release();
-        RegisterOatFile(source_oat_file);
-      } else {
+      // Take the file only if it has no collisions, or we must take it because of preopting.
+      bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg);
+      if (!accept_oat_file) {
+        // Failed the collision check. Print warning.
         if (Runtime::Current()->IsDexFileFallbackEnabled()) {
           LOG(WARNING) << "Found duplicate classes, falling back to interpreter mode for "
                        << dex_location;
@@ -954,6 +954,19 @@
                           " load classes for " << dex_location;
         }
         LOG(WARNING) << error_msg;
+
+        // However, if the app was part of /system and preopted, there is no original dex file
+        // available. In that case grudgingly accept the oat file.
+        if (!DexFile::MaybeDex(dex_location)) {
+          accept_oat_file = true;
+          LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
+                       << "Allow oat file use. This is potentially dangerous.";
+        }
+      }
+
+      if (accept_oat_file) {
+        source_oat_file = oat_file.release();
+        RegisterOatFile(source_oat_file);
       }
     }
   }
@@ -975,8 +988,7 @@
     if (Runtime::Current()->IsDexFileFallbackEnabled()) {
       if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
         LOG(WARNING) << error_msg;
-        error_msgs->push_back("Failed to open dex files from "
-            + std::string(dex_location));
+        error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
       }
     } else {
       error_msgs->push_back("Fallback mode disabled, skipping dex files.");
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 20098e7..dfe5a04 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -153,6 +153,31 @@
   return false;
 }
 
+static bool ContainsClassesDex(int fd, const char* filename) {
+  std::string error_msg;
+  std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, filename, &error_msg));
+  if (zip_archive.get() == nullptr) {
+    return false;
+  }
+  std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(DexFile::kClassesDex, &error_msg));
+  return (zip_entry.get() != nullptr);
+}
+
+bool DexFile::MaybeDex(const char* filename) {
+  uint32_t magic;
+  std::string error_msg;
+  ScopedFd fd(OpenAndReadMagic(filename, &magic, &error_msg));
+  if (fd.get() == -1) {
+    return false;
+  }
+  if (IsZipMagic(magic)) {
+    return ContainsClassesDex(fd.release(), filename);
+  } else if (IsDexMagic(magic)) {
+    return true;
+  }
+  return false;
+}
+
 int DexFile::GetPermissions() const {
   if (mem_map_.get() == nullptr) {
     return 0;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 6b3f883..84eaa4a 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -388,6 +388,10 @@
   static bool Open(const char* filename, const char* location, std::string* error_msg,
                    std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
+  // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
+  // If this function returns false, Open will not succeed. The inverse is not true, however.
+  static bool MaybeDex(const char* filename);
+
   // Opens .dex file, backed by existing memory
   static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
                                              const std::string& location,
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ef3c6e2..ae67efb 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -501,6 +501,7 @@
   uint16_t num_regs;
   if (LIKELY(code_item != nullptr)) {
     num_regs = code_item->registers_size_;
+    DCHECK_EQ(string_init ? num_ins - 1 : num_ins, code_item->ins_size_);
   } else {
     DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
     num_regs = num_ins;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index eb60318..2633898 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1566,14 +1566,15 @@
   // Throwing an exception may cause its class initialization. If we mark the transaction
   // aborted before that, we may warn with a false alarm. Throwing the exception before
   // marking the transaction aborted avoids that.
-  preinitialization_transaction_->ThrowAbortError(self, false);
+  preinitialization_transaction_->ThrowAbortError(self, &abort_message);
   preinitialization_transaction_->Abort(abort_message);
 }
 
 void Runtime::ThrowTransactionAbortError(Thread* self) {
   DCHECK(IsAotCompiler());
   DCHECK(IsActiveTransaction());
-  preinitialization_transaction_->ThrowAbortError(self, true);
+  // Passing nullptr means we rethrow an exception with the earlier transaction abort message.
+  preinitialization_transaction_->ThrowAbortError(self, nullptr);
 }
 
 void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index cc0f15f..ab821d7 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -70,13 +70,21 @@
   }
 }
 
-void Transaction::ThrowAbortError(Thread* self, bool rethrow) {
+void Transaction::ThrowAbortError(Thread* self, const std::string* abort_message) {
+  const bool rethrow = (abort_message == nullptr);
   if (kIsDebugBuild && rethrow) {
     CHECK(IsAborted()) << "Rethrow " << Transaction::kAbortExceptionDescriptor
                        << " while transaction is not aborted";
   }
-  std::string abort_msg(GetAbortMessage());
-  self->ThrowNewWrappedException(Transaction::kAbortExceptionSignature, abort_msg.c_str());
+  if (rethrow) {
+    // Rethrow an exception with the earlier abort message stored in the transaction.
+    self->ThrowNewWrappedException(Transaction::kAbortExceptionSignature,
+                                   GetAbortMessage().c_str());
+  } else {
+    // Throw an exception with the given abort message.
+    self->ThrowNewWrappedException(Transaction::kAbortExceptionSignature,
+                                   abort_message->c_str());
+  }
 }
 
 bool Transaction::IsAborted() {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 4d85662..030478c 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -48,7 +48,7 @@
   void Abort(const std::string& abort_message)
       LOCKS_EXCLUDED(log_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void ThrowAbortError(Thread* self, bool rethrow)
+  void ThrowAbortError(Thread* self, const std::string* abort_message)
       LOCKS_EXCLUDED(log_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool IsAborted() LOCKS_EXCLUDED(log_lock_);
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 8445751..2838681 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -137,7 +137,7 @@
     if (GetRegisterType(verifier, i).Equals(uninit_type)) {
       line_[i] = init_type.GetId();
       changed++;
-      if (i != this_reg && is_string) {
+      if (is_string && i != this_reg) {
         auto it = verifier->GetStringInitPcRegMap().find(dex_pc);
         if (it != verifier->GetStringInitPcRegMap().end()) {
           it->second.insert(i);
diff --git a/test/138-duplicate-classes-check/build b/test/138-duplicate-classes-check/build
deleted file mode 100755
index 7ddc81d..0000000
--- a/test/138-duplicate-classes-check/build
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
-
-mkdir classes-ex
-${JAVAC} -d classes-ex `find src-ex -name '*.java'`
-
-if [ ${NEED_DEX} = "true" ]; then
-  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
-  zip $TEST_NAME.jar classes.dex
-  ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
-  zip ${TEST_NAME}-ex.jar classes.dex
-fi
diff --git a/test/483-dce-block/expected.txt b/test/483-dce-block/expected.txt
new file mode 100644
index 0000000..ef48625
--- /dev/null
+++ b/test/483-dce-block/expected.txt
@@ -0,0 +1 @@
+class Main
diff --git a/test/483-dce-block/info.txt b/test/483-dce-block/info.txt
new file mode 100644
index 0000000..3db88ab
--- /dev/null
+++ b/test/483-dce-block/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing that used to crash
+compiling the `foo` method.
diff --git a/test/483-dce-block/src/Main.java b/test/483-dce-block/src/Main.java
new file mode 100644
index 0000000..2f66a74
--- /dev/null
+++ b/test/483-dce-block/src/Main.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void foo(Object o, int a) {
+    Object result = null;
+    if (o instanceof Main) {
+      // The compiler optimizes the type of `o` by introducing
+      // a `HBoundType` in this block.
+      while (a != 3) {
+        if (a == 2) {
+          a++;
+          result = o;
+          continue;
+        } else if (willInline()) {
+          // This block will be detected as dead after inlining.
+          result = new Object();
+          continue;
+        }
+        result = new Object();
+      }
+      // The compiler produces a phi at the back edge for `result`.
+      // Before dead block elimination, the phi has three inputs:
+      // result = (new Object(), new Object(), HBoundType)
+      //
+      // After dead block elimination, the phi has now two inputs:
+      // result = (new Object(), HBoundType)
+      //
+      // Our internal data structure for linking users and inputs expect
+      // the input index stored in that data structure to be the index
+      // in the inputs array. So the index before dead block elimination
+      // of the `HBoundType` would be 2. Dead block elimination must update
+      // that index to be 1.
+    }
+    System.out.println(result.getClass());
+  }
+
+  public static boolean willInline() {
+    return false;
+  }
+
+  public static void main(String[] args) {
+    foo(new Main(), 2);
+  }
+}