Subzero: Add basic ELFObjectWriter (text section, symtab, strtab, headers).

Able to write out the ELF file header w/ a text section,
a symbol table, and string table. Write text buffer
directly to file after translating each CFG.
This means that the header is written out early w/ fake
data and then we seek back and write the real header
at the very end.

Does not yet handle relocations, data, rodata, constant
pools, bss, or -ffunction-sections, more than 64K sections
or more than 2^24 symbols.

Numbers w/ current NOASSERT=1 build on 176.gcc:

w/out -elf-writer:
    0.233771 (21.1%): [ 1287] emit
    28MB .s file

w/ -elf-writer:
    0.051056 ( 5.6%): [ 1287] emit
    2.4MB .o file

BUG=none
R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/678533005
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp
index 950a3f8..e40bd16 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -405,8 +405,9 @@
     Str << "\t.globl\t" << MangledName << "\n";
     Str << "\t.type\t" << MangledName << ",@function\n";
   }
-  Str << "\t.p2align " << getTarget()->getBundleAlignLog2Bytes() << ",0x";
-  for (AsmCodeByte I : getTarget()->getNonExecBundlePadding())
+  Assembler *Asm = getAssembler<Assembler>();
+  Str << "\t.p2align " << Asm->getBundleAlignLog2Bytes() << ",0x";
+  for (uint8_t I : Asm->getNonExecBundlePadding())
     Str.write_hex(I);
   Str << "\n";
   Str << MangledName << ":\n";
@@ -444,10 +445,20 @@
   TimerMarker T(TimerStack::TT_emit, this);
   assert(!Ctx->getFlags().DecorateAsm);
   IceString MangledName = getContext()->mangleName(getFunctionName());
-  emitTextHeader(MangledName);
+  if (!Ctx->getFlags().UseELFWriter)
+    emitTextHeader(MangledName);
   for (CfgNode *Node : Nodes)
     Node->emitIAS(this);
-  getAssembler<Assembler>()->emitIASBytes(Ctx);
+  // Now write the function to the file and track.
+  if (Ctx->getFlags().UseELFWriter) {
+    getAssembler<Assembler>()->alignFunction();
+    // TODO(jvoung): Transfer remaining fixups too. They may need their
+    // offsets adjusted.
+    Ctx->getObjectWriter()->writeFunctionCode(
+        MangledName, getInternal(), getAssembler<Assembler>()->getBufferView());
+  } else {
+    getAssembler<Assembler>()->emitIASBytes(Ctx);
+  }
 }
 
 // Dumps the IR with an optional introductory message.
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index a1dcf68..68255fb 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -23,17 +23,18 @@
   ClFlags()
       : DisableInternal(false), SubzeroTimingEnabled(false),
         DisableTranslation(false), FunctionSections(false), DataSections(false),
-        UseIntegratedAssembler(false), UseSandboxing(false),
-        PhiEdgeSplit(false), DecorateAsm(false), DumpStats(false),
-        AllowUninitializedGlobals(false), TimeEachFunction(false),
-        DisableIRGeneration(false), DefaultGlobalPrefix(""),
-        DefaultFunctionPrefix(""), TimingFocusOn(""), VerboseFocusOn(""),
-        TranslateOnly("") {}
+        UseELFWriter(false), UseIntegratedAssembler(false),
+        UseSandboxing(false), PhiEdgeSplit(false), DecorateAsm(false),
+        DumpStats(false), AllowUninitializedGlobals(false),
+        TimeEachFunction(false), DisableIRGeneration(false),
+        DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
+        VerboseFocusOn(""), TranslateOnly("") {}
   bool DisableInternal;
   bool SubzeroTimingEnabled;
   bool DisableTranslation;
   bool FunctionSections;
   bool DataSections;
+  bool UseELFWriter;
   bool UseIntegratedAssembler;
   bool UseSandboxing;
   bool PhiEdgeSplit;
diff --git a/src/IceDefs.h b/src/IceDefs.h
index a28da4b..5d9dd4c 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -33,6 +33,7 @@
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/ELF.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace Ice {
@@ -126,6 +127,7 @@
 typedef uint32_t VerboseMask;
 
 typedef llvm::raw_ostream Ostream;
+typedef llvm::raw_fd_ostream Fdstream;
 
 } // end of namespace Ice
 
diff --git a/src/IceELFObjectWriter.cpp b/src/IceELFObjectWriter.cpp
new file mode 100644
index 0000000..f3a63df
--- /dev/null
+++ b/src/IceELFObjectWriter.cpp
@@ -0,0 +1,347 @@
+//===- subzero/src/IceELFObjectWriter.cpp - ELF object file writer --------===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the writer for ELF relocatable object files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IceDefs.h"
+#include "IceELFObjectWriter.h"
+#include "IceELFSection.h"
+#include "IceELFStreamer.h"
+#include "IceGlobalContext.h"
+#include "IceGlobalInits.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+namespace {
+
+struct {
+  bool IsELF64;
+  uint16_t ELFMachine;
+  uint32_t ELFFlags;
+} ELFTargetInfo[] = {
+#define X(tag, str, is_elf64, e_machine, e_flags)                              \
+  { is_elf64, e_machine, e_flags }                                             \
+  ,
+    TARGETARCH_TABLE
+#undef X
+};
+
+bool isELF64(TargetArch Arch) {
+  if (Arch < TargetArch_NUM)
+    return ELFTargetInfo[Arch].IsELF64;
+  llvm_unreachable("Invalid target arch for isELF64");
+  return false;
+}
+
+uint16_t getELFMachine(TargetArch Arch) {
+  if (Arch < TargetArch_NUM)
+    return ELFTargetInfo[Arch].ELFMachine;
+  llvm_unreachable("Invalid target arch for getELFMachine");
+  return EM_NONE;
+}
+
+uint32_t getELFFlags(TargetArch Arch) {
+  if (Arch < TargetArch_NUM)
+    return ELFTargetInfo[Arch].ELFFlags;
+  llvm_unreachable("Invalid target arch for getELFFlags");
+  return 0;
+}
+
+} // end of anonymous namespace
+
+ELFObjectWriter::ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out)
+    : Ctx(Ctx), Str(Out), SectionNumbersAssigned(false) {
+  // Create the special bookkeeping sections now.
+  const IceString NullSectionName("");
+  NullSection = new (Ctx.allocate<ELFSection>())
+      ELFSection(NullSectionName, SHT_NULL, 0, 0, 0);
+
+  const IceString ShStrTabName(".shstrtab");
+  ShStrTab = new (Ctx.allocate<ELFStringTableSection>())
+      ELFStringTableSection(ShStrTabName, SHT_STRTAB, 0, 1, 0);
+  ShStrTab->add(ShStrTabName);
+
+  const IceString SymTabName(".symtab");
+  bool IsELF64 = isELF64(Ctx.getTargetArch());
+  const Elf64_Xword SymTabAlign = IsELF64 ? 8 : 4;
+  const Elf64_Xword SymTabEntSize =
+      IsELF64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
+  static_assert(sizeof(Elf64_Sym) == 24 && sizeof(Elf32_Sym) == 16,
+                "Elf_Sym sizes cannot be derived from sizeof");
+  SymTab = createSection<ELFSymbolTableSection>(SymTabName, SHT_SYMTAB, 0,
+                                                SymTabAlign, SymTabEntSize);
+  // The first entry in the symbol table should be a NULL entry.
+  const IceString NullSymName("");
+  SymTab->createDefinedSym(NullSymName, STT_NOTYPE, STB_LOCAL, NullSection, 0,
+                           0);
+
+  const IceString StrTabName(".strtab");
+  StrTab =
+      createSection<ELFStringTableSection>(StrTabName, SHT_STRTAB, 0, 1, 0);
+}
+
+template <typename T>
+T *ELFObjectWriter::createSection(const IceString &Name, Elf64_Word ShType,
+                                  Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
+                                  Elf64_Xword ShEntsize) {
+  assert(!SectionNumbersAssigned);
+  T *NewSection =
+      new (Ctx.allocate<T>()) T(Name, ShType, ShFlags, ShAddralign, ShEntsize);
+  ShStrTab->add(Name);
+  return NewSection;
+}
+
+template <typename UserSectionList>
+void ELFObjectWriter::assignRelSectionNumInPairs(SizeT &CurSectionNumber,
+                                                 UserSectionList &UserSections,
+                                                 RelSectionList &RelSections,
+                                                 SectionList &AllSections) {
+  RelSectionList::iterator RelIt = RelSections.begin();
+  RelSectionList::iterator RelE = RelSections.end();
+  for (ELFSection *UserSection : UserSections) {
+    UserSection->setNumber(CurSectionNumber++);
+    UserSection->setNameStrIndex(ShStrTab->getIndex(UserSection->getName()));
+    AllSections.push_back(UserSection);
+    if (RelIt != RelE) {
+      ELFRelocationSectionBase *RelSection = *RelIt;
+      if (RelSection->getRelatedSection() == UserSection) {
+        RelSection->setInfoNum(UserSection->getNumber());
+        RelSection->setNumber(CurSectionNumber++);
+        RelSection->setNameStrIndex(ShStrTab->getIndex(RelSection->getName()));
+        AllSections.push_back(RelSection);
+        ++RelIt;
+      }
+    }
+  }
+  // Should finish with UserIt at the same time as RelIt.
+  assert(RelIt == RelE);
+  return;
+}
+
+void ELFObjectWriter::assignRelLinkNum(SizeT SymTabNumber,
+                                       RelSectionList &RelSections) {
+  for (ELFRelocationSectionBase *S : RelSections) {
+    S->setLinkNum(SymTabNumber);
+  }
+}
+
+void ELFObjectWriter::assignSectionNumbersInfo(SectionList &AllSections) {
+  // Go through each section, assigning them section numbers and
+  // and fill in the size for sections that aren't incrementally updated.
+  assert(!SectionNumbersAssigned);
+  SizeT CurSectionNumber = 0;
+  NullSection->setNumber(CurSectionNumber++);
+  // The rest of the fields are initialized to 0, and stay that way.
+  AllSections.push_back(NullSection);
+
+  assignRelSectionNumInPairs<TextSectionList>(CurSectionNumber, TextSections,
+                                              RelTextSections, AllSections);
+  assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, DataSections,
+                                              RelDataSections, AllSections);
+  assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, RoDataSections,
+                                              RelRoDataSections, AllSections);
+
+  ShStrTab->setNumber(CurSectionNumber++);
+  ShStrTab->setNameStrIndex(ShStrTab->getIndex(ShStrTab->getName()));
+  AllSections.push_back(ShStrTab);
+
+  SymTab->setNumber(CurSectionNumber++);
+  SymTab->setNameStrIndex(ShStrTab->getIndex(SymTab->getName()));
+  AllSections.push_back(SymTab);
+
+  StrTab->setNumber(CurSectionNumber++);
+  StrTab->setNameStrIndex(ShStrTab->getIndex(StrTab->getName()));
+  AllSections.push_back(StrTab);
+
+  SymTab->setLinkNum(StrTab->getNumber());
+  SymTab->setInfoNum(SymTab->getNumLocals());
+
+  assignRelLinkNum(SymTab->getNumber(), RelTextSections);
+  assignRelLinkNum(SymTab->getNumber(), RelDataSections);
+  assignRelLinkNum(SymTab->getNumber(), RelRoDataSections);
+  SectionNumbersAssigned = true;
+}
+
+Elf64_Off ELFObjectWriter::alignFileOffset(Elf64_Xword Align) {
+  assert(llvm::isPowerOf2_32(Align));
+  Elf64_Off OffsetInFile = Str.tell();
+  Elf64_Xword Mod = OffsetInFile & (Align - 1);
+  if (Mod == 0)
+    return OffsetInFile;
+  Elf64_Xword AlignDiff = Align - Mod;
+  Str.writeZeroPadding(AlignDiff);
+  OffsetInFile += AlignDiff;
+  assert((OffsetInFile & (Align - 1)) == 0);
+  return OffsetInFile;
+}
+
+void ELFObjectWriter::writeFunctionCode(const IceString &FuncName,
+                                        bool IsInternal,
+                                        const llvm::StringRef Data) {
+  assert(!SectionNumbersAssigned);
+  // TODO(jvoung): handle ffunction-sections.
+  IceString SectionName = ".text";
+  ELFTextSection *Section = nullptr;
+  if (TextSections.size() == 0) {
+    const Elf64_Xword ShFlags = SHF_ALLOC | SHF_EXECINSTR;
+    // TODO(jvoung): Should be bundle size. Grab it from that target?
+    const Elf64_Xword ShAlign = 32;
+    Section = createSection<ELFTextSection>(SectionName, SHT_PROGBITS, ShFlags,
+                                            ShAlign, 0);
+    Elf64_Off OffsetInFile = alignFileOffset(Section->getSectionAlign());
+    Section->setFileOffset(OffsetInFile);
+    TextSections.push_back(Section);
+  } else {
+    Section = TextSections[0];
+  }
+  RelocOffsetT OffsetInSection = Section->getCurrentSize();
+  // Function symbols are set to 0 size in the symbol table,
+  // in contrast to data symbols which have a proper size.
+  SizeT SymbolSize = 0;
+  Section->appendData(Str, Data);
+  uint8_t SymbolType;
+  uint8_t SymbolBinding;
+  if (IsInternal) {
+    SymbolType = STT_NOTYPE;
+    SymbolBinding = STB_LOCAL;
+  } else {
+    SymbolType = STT_FUNC;
+    SymbolBinding = STB_GLOBAL;
+  }
+  SymTab->createDefinedSym(FuncName, SymbolType, SymbolBinding, Section,
+                           OffsetInSection, SymbolSize);
+  StrTab->add(FuncName);
+}
+
+void ELFObjectWriter::writeDataInitializer(const IceString &VarName,
+                                           const llvm::StringRef Data) {
+  assert(!SectionNumbersAssigned);
+  (void)Data;
+  llvm_unreachable("TODO");
+  StrTab->add(VarName);
+}
+
+void ELFObjectWriter::writeInitialELFHeader() {
+  assert(!SectionNumbersAssigned);
+  const size_t DummySHOffset = 0;
+  const SizeT DummySHStrIndex = 0;
+  const SizeT DummyNumSections = 0;
+  if (isELF64(Ctx.getTargetArch())) {
+    writeELFHeaderInternal<true>(DummySHOffset, DummySHStrIndex,
+                                 DummyNumSections);
+  } else {
+    writeELFHeaderInternal<false>(DummySHOffset, DummySHStrIndex,
+                                  DummyNumSections);
+  }
+}
+
+template <bool IsELF64>
+void ELFObjectWriter::writeELFHeaderInternal(size_t SectionHeaderOffset,
+                                             SizeT SectHeaderStrIndex,
+                                             SizeT NumSections) {
+  // Write the e_ident: magic number, class, etc.
+  // The e_ident is byte order and ELF class independent.
+  Str.writeBytes(llvm::StringRef(ElfMagic, strlen(ElfMagic)));
+  Str.write8(IsELF64 ? ELFCLASS64 : ELFCLASS32);
+  Str.write8(ELFDATA2LSB);
+  Str.write8(EV_CURRENT);
+  Str.write8(ELFOSABI_NONE);
+  const uint8_t ELF_ABIVersion = 0;
+  Str.write8(ELF_ABIVersion);
+  Str.writeZeroPadding(EI_NIDENT - EI_PAD);
+
+  // TODO(jvoung): Handle and test > 64K sections.  See the generic ABI doc:
+  // https://refspecs.linuxbase.org/elf/gabi4+/ch4.eheader.html
+  // e_shnum should be 0 and then actual number of sections is
+  // stored in the sh_size member of the 0th section.
+  assert(NumSections < SHN_LORESERVE);
+  assert(SectHeaderStrIndex < SHN_LORESERVE);
+
+  // Write the rest of the file header, which does depend on byte order
+  // and ELF class.
+  Str.writeLE16(ET_REL);                             // e_type
+  Str.writeLE16(getELFMachine(Ctx.getTargetArch())); // e_machine
+  Str.writeELFWord<IsELF64>(1);                      // e_version
+  // Since this is for a relocatable object, there is no entry point,
+  // and no program headers.
+  Str.writeAddrOrOffset<IsELF64>(0);                                // e_entry
+  Str.writeAddrOrOffset<IsELF64>(0);                                // e_phoff
+  Str.writeAddrOrOffset<IsELF64>(SectionHeaderOffset);              // e_shoff
+  Str.writeELFWord<IsELF64>(getELFFlags(Ctx.getTargetArch()));      // e_flags
+  Str.writeLE16(IsELF64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); // e_ehsize
+  static_assert(sizeof(Elf64_Ehdr) == 64 && sizeof(Elf32_Ehdr) == 52,
+                "Elf_Ehdr sizes cannot be derived from sizeof");
+  Str.writeLE16(0); // e_phentsize
+  Str.writeLE16(0); // e_phnum
+  Str.writeLE16(IsELF64 ? sizeof(Elf64_Shdr)
+                        : sizeof(Elf32_Shdr)); // e_shentsize
+  static_assert(sizeof(Elf64_Shdr) == 64 && sizeof(Elf32_Shdr) == 40,
+                "Elf_Shdr sizes cannot be derived from sizeof");
+  Str.writeLE16(static_cast<Elf64_Half>(NumSections));        // e_shnum
+  Str.writeLE16(static_cast<Elf64_Half>(SectHeaderStrIndex)); // e_shstrndx
+}
+
+void ELFObjectWriter::writeNonUserSections() {
+  bool IsELF64 = isELF64(Ctx.getTargetArch());
+
+  // Write out the shstrtab now that all sections are known.
+  ShStrTab->doLayout();
+  ShStrTab->setSize(ShStrTab->getSectionDataSize());
+  Elf64_Off ShStrTabOffset = alignFileOffset(ShStrTab->getSectionAlign());
+  ShStrTab->setFileOffset(ShStrTabOffset);
+  Str.writeBytes(ShStrTab->getSectionData());
+
+  SectionList AllSections;
+  assignSectionNumbersInfo(AllSections);
+
+  // Finalize the regular StrTab and fix up references in the SymTab.
+  StrTab->doLayout();
+  StrTab->setSize(StrTab->getSectionDataSize());
+
+  SymTab->updateIndices(StrTab);
+
+  Elf64_Off SymTabOffset = alignFileOffset(SymTab->getSectionAlign());
+  SymTab->setFileOffset(SymTabOffset);
+  SymTab->setSize(SymTab->getSectionDataSize());
+  SymTab->writeData(Str, IsELF64);
+
+  Elf64_Off StrTabOffset = alignFileOffset(StrTab->getSectionAlign());
+  StrTab->setFileOffset(StrTabOffset);
+  Str.writeBytes(StrTab->getSectionData());
+
+  // TODO: Write out the relocation sections.
+  // May also be able to seek around the file and resolve function calls
+  // that are for functions within the same section.
+
+  // Write out the section headers.
+  const size_t ShdrAlign = IsELF64 ? 8 : 4;
+  Elf64_Off ShOffset = alignFileOffset(ShdrAlign);
+  for (const auto S : AllSections) {
+    if (IsELF64)
+      S->writeHeader<true>(Str);
+    else
+      S->writeHeader<false>(Str);
+  }
+
+  // Finally write the updated ELF header w/ the correct number of sections.
+  Str.seek(0);
+  if (IsELF64) {
+    writeELFHeaderInternal<true>(ShOffset, ShStrTab->getNumber(),
+                                 AllSections.size());
+  } else {
+    writeELFHeaderInternal<false>(ShOffset, ShStrTab->getNumber(),
+                                  AllSections.size());
+  }
+}
+
+} // end of namespace Ice
diff --git a/src/IceELFObjectWriter.h b/src/IceELFObjectWriter.h
new file mode 100644
index 0000000..1dc9daa
--- /dev/null
+++ b/src/IceELFObjectWriter.h
@@ -0,0 +1,125 @@
+//===- subzero/src/IceELFObjectWriter.h - ELF object writer -----*- C++ -*-===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstraction for a writer that is responsible for writing an ELF file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEELFOBJECTWRITER_H
+#define SUBZERO_SRC_ICEELFOBJECTWRITER_H
+
+#include "IceDefs.h"
+#include "IceELFSection.h"
+#include "IceELFStreamer.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+// Higher level ELF object writer.  Manages section information and writes
+// the final ELF object.  The object writer will write to file the code
+// and data as it is being defined (rather than keep a copy).
+// After all definitions are written out, it will finalize the bookkeeping
+// sections and write them out.  Expected usage:
+//
+// (1) writeInitialELFHeader
+// (2) writeDataInitializer*
+// (3) writeFunctionCode*
+// (4) writeNonUserSections
+class ELFObjectWriter {
+  ELFObjectWriter(const ELFObjectWriter &) = delete;
+  ELFObjectWriter &operator=(const ELFObjectWriter &) = delete;
+
+public:
+  ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out);
+
+  // Write the initial ELF header. This is just to reserve space in the ELF
+  // file. Reserving space allows the other functions to write text
+  // and data directly to the file and get the right file offsets.
+  void writeInitialELFHeader();
+
+  // Copy data of a function's text section to file and note the offset of the
+  // symbol's definition in the symbol table.
+  // TODO(jvoung): This also needs the relocations to adjust the
+  // section-relative offsets and hook them up to the symbol table references.
+  void writeFunctionCode(const IceString &FuncName, bool IsInternal,
+                         const llvm::StringRef Data);
+
+  // Copy initializer data for a global to file and note the offset and
+  // size of the global's definition in the symbol table.
+  // TODO(jvoung): This needs to know which section. This also needs the
+  // relocations to hook them up to the symbol table references. Also
+  // TODO is handling BSS (which just needs to note the size).
+  void writeDataInitializer(const IceString &VarName,
+                            const llvm::StringRef Data);
+
+  // Do final layout and write out the rest of the object file, then
+  // patch up the initial ELF header with the final info.
+  void writeNonUserSections();
+
+private:
+  GlobalContext &Ctx;
+  ELFStreamer &Str;
+  bool SectionNumbersAssigned;
+
+  // All created sections, separated into different pools.
+  typedef std::vector<ELFSection *> SectionList;
+  typedef std::vector<ELFTextSection *> TextSectionList;
+  typedef std::vector<ELFDataSection *> DataSectionList;
+  typedef std::vector<ELFRelocationSectionBase *> RelSectionList;
+  TextSectionList TextSections;
+  RelSectionList RelTextSections;
+  DataSectionList DataSections;
+  RelSectionList RelDataSections;
+  DataSectionList RoDataSections;
+  RelSectionList RelRoDataSections;
+
+  // Handles to special sections that need incremental bookkeeping.
+  ELFSection *NullSection;
+  ELFStringTableSection *ShStrTab;
+  ELFSymbolTableSection *SymTab;
+  ELFStringTableSection *StrTab;
+
+  template <typename T>
+  T *createSection(const IceString &Name, Elf64_Word ShType,
+                   Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
+                   Elf64_Xword ShEntsize);
+
+  // Align the file position before writing out a section's data,
+  // and return the position of the file.
+  Elf64_Off alignFileOffset(Elf64_Xword Align);
+
+  // Assign an ordering / section numbers to each section.
+  // Fill in other information that is only known near the end
+  // (such as the size, if it wasn't already incrementally updated).
+  // This then collects all sections in the decided order, into one vector,
+  // for conveniently writing out all of the section headers.
+  void assignSectionNumbersInfo(SectionList &AllSections);
+
+  // This function assigns .foo and .rel.foo consecutive section numbers.
+  // It also sets the relocation section's sh_info field to the related
+  // section's number.
+  template <typename UserSectionList>
+  void assignRelSectionNumInPairs(SizeT &CurSectionNumber,
+                                  UserSectionList &UserSections,
+                                  RelSectionList &RelSections,
+                                  SectionList &AllSections);
+
+  // Link the relocation sections to the symbol table.
+  void assignRelLinkNum(SizeT SymTabNumber, RelSectionList &RelSections);
+
+  // Write the ELF file header with the given information about sections.
+  template <bool IsELF64>
+  void writeELFHeaderInternal(uint64_t SectionHeaderOffset,
+                              SizeT SectHeaderStrIndex, SizeT NumSections);
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEELFOBJECTWRITER_H
diff --git a/src/IceELFSection.cpp b/src/IceELFSection.cpp
new file mode 100644
index 0000000..1910e77
--- /dev/null
+++ b/src/IceELFSection.cpp
@@ -0,0 +1,159 @@
+//===- subzero/src/IceELFSection.cpp - Representation of ELF sections -----===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines how ELF sections are represented.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IceDefs.h"
+#include "IceELFSection.h"
+#include "IceELFStreamer.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+// Text sections.
+
+void ELFTextSection::appendData(ELFStreamer &Str,
+                                const llvm::StringRef MoreData) {
+  Str.writeBytes(MoreData);
+  Header.sh_size += MoreData.size();
+}
+
+// Data sections.
+
+void ELFDataSection::appendData(ELFStreamer &Str,
+                                const llvm::StringRef MoreData) {
+  Str.writeBytes(MoreData);
+  Header.sh_size += MoreData.size();
+}
+
+// Relocation sections.
+
+// Symbol tables.
+
+void ELFSymbolTableSection::createDefinedSym(const IceString &Name,
+                                             uint8_t Type, uint8_t Binding,
+                                             ELFSection *Section,
+                                             RelocOffsetT Offset, SizeT Size) {
+  ELFSym NewSymbol = ELFSym();
+  NewSymbol.Sym.setBindingAndType(Binding, Type);
+  NewSymbol.Sym.st_value = Offset;
+  NewSymbol.Sym.st_size = Size;
+  NewSymbol.Number = ELFSym::UnknownNumber;
+  SymtabKey Key = {Name, Section};
+  bool Unique;
+  if (Type == STB_LOCAL)
+    Unique = LocalSymbols.insert(std::make_pair(Key, NewSymbol)).second;
+  else
+    Unique = GlobalSymbols.insert(std::make_pair(Key, NewSymbol)).second;
+  assert(Unique);
+}
+
+void ELFSymbolTableSection::noteUndefinedSym(const IceString &Name,
+                                             ELFSection *NullSection) {
+  ELFSym NewSymbol = ELFSym();
+  NewSymbol.Sym.setBindingAndType(STB_GLOBAL, STT_NOTYPE);
+  NewSymbol.Number = ELFSym::UnknownNumber;
+  SymtabKey Key = {Name, NullSection};
+  GlobalSymbols.insert(std::make_pair(Key, NewSymbol));
+}
+
+void ELFSymbolTableSection::updateIndices(const ELFStringTableSection *StrTab) {
+  SizeT SymNumber = 0;
+  for (auto &KeyValue : LocalSymbols) {
+    const IceString &Name = KeyValue.first.first;
+    ELFSection *Section = KeyValue.first.second;
+    Elf64_Sym &SymInfo = KeyValue.second.Sym;
+    if (!Name.empty())
+      SymInfo.st_name = StrTab->getIndex(Name);
+    SymInfo.st_shndx = Section->getNumber();
+    KeyValue.second.setNumber(SymNumber++);
+  }
+  for (auto &KeyValue : GlobalSymbols) {
+    const IceString &Name = KeyValue.first.first;
+    ELFSection *Section = KeyValue.first.second;
+    Elf64_Sym &SymInfo = KeyValue.second.Sym;
+    if (!Name.empty())
+      SymInfo.st_name = StrTab->getIndex(Name);
+    SymInfo.st_shndx = Section->getNumber();
+    KeyValue.second.setNumber(SymNumber++);
+  }
+}
+
+void ELFSymbolTableSection::writeData(ELFStreamer &Str, bool IsELF64) {
+  if (IsELF64) {
+    writeSymbolMap<true>(Str, LocalSymbols);
+    writeSymbolMap<true>(Str, GlobalSymbols);
+  } else {
+    writeSymbolMap<false>(Str, LocalSymbols);
+    writeSymbolMap<false>(Str, GlobalSymbols);
+  }
+}
+
+// String tables.
+
+void ELFStringTableSection::add(const IceString &Str) {
+  assert(!isLaidOut());
+  assert(!Str.empty());
+  StringToIndexMap.insert(std::make_pair(Str, UnknownIndex));
+}
+
+size_t ELFStringTableSection::getIndex(const IceString &Str) const {
+  assert(isLaidOut());
+  StringToIndexType::const_iterator It = StringToIndexMap.find(Str);
+  if (It == StringToIndexMap.end()) {
+    llvm_unreachable("String index not found");
+    return UnknownIndex;
+  }
+  return It->second;
+}
+
+bool ELFStringTableSection::SuffixComparator::
+operator()(const IceString &StrA, const IceString &StrB) const {
+  size_t LenA = StrA.size();
+  size_t LenB = StrB.size();
+  size_t CommonLen = std::min(LenA, LenB);
+  // If there is a difference in the common suffix, use that diff to sort.
+  for (size_t i = 0; i < CommonLen; ++i) {
+    char a = StrA[LenA - i - 1];
+    char b = StrB[LenB - i - 1];
+    if (a != b)
+      return a > b;
+  }
+  // If the common suffixes are completely equal, let the longer one come
+  // first, so that it can be laid out first and its characters shared.
+  return LenA > LenB;
+}
+
+void ELFStringTableSection::doLayout() {
+  assert(!isLaidOut());
+  llvm::StringRef Prev;
+
+  // String table starts with 0 byte.
+  StringData.push_back(0);
+
+  for (auto &StringIndex : StringToIndexMap) {
+    assert(StringIndex.second == UnknownIndex);
+    llvm::StringRef Cur = llvm::StringRef(StringIndex.first);
+    if (Prev.endswith(Cur)) {
+      // Prev is already in the StringData, and Cur is shorter than Prev
+      // based on the sort.
+      StringIndex.second = StringData.size() - Cur.size() - 1;
+      continue;
+    }
+    StringIndex.second = StringData.size();
+    std::copy(Cur.begin(), Cur.end(), back_inserter(StringData));
+    StringData.push_back(0);
+    Prev = Cur;
+  }
+}
+
+} // end of namespace Ice
diff --git a/src/IceELFSection.h b/src/IceELFSection.h
new file mode 100644
index 0000000..227affe
--- /dev/null
+++ b/src/IceELFSection.h
@@ -0,0 +1,324 @@
+//===- subzero/src/IceELFSection.h - Model of ELF sections ------*- C++ -*-===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of ELF sections.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEELFSECTION_H
+#define SUBZERO_SRC_ICEELFSECTION_H
+
+#include "IceDefs.h"
+#include "IceELFStreamer.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+class ELFStreamer;
+class ELFStringTableSection;
+
+// Base representation of an ELF section.
+class ELFSection {
+  ELFSection(const ELFSection &) = delete;
+  ELFSection &operator=(const ELFSection &) = delete;
+
+public:
+  // Sentinel value for a section number/index for before the final
+  // section index is actually known. The dummy NULL section will be assigned
+  // number 0, and it is referenced by the dummy 0-th symbol in the symbol
+  // table, so use max() instead of 0.
+  enum { NoSectionNumber = std::numeric_limits<SizeT>::max() };
+
+  // Constructs an ELF section, filling in fields that will be known
+  // once the *type* of section is decided.  Other fields may be updated
+  // incrementally or only after the program is completely defined.
+  ELFSection(const IceString &Name, Elf64_Word ShType, Elf64_Xword ShFlags,
+             Elf64_Xword ShAddralign, Elf64_Xword ShEntsize)
+      : Name(Name), Header(), Number(NoSectionNumber) {
+    Header.sh_type = ShType;
+    Header.sh_flags = ShFlags;
+    Header.sh_addralign = ShAddralign;
+    Header.sh_entsize = ShEntsize;
+  }
+
+  // Set the section number/index after it is finally known.
+  void setNumber(SizeT N) {
+    // Should only set the number once: from NoSectionNumber -> N.
+    assert(Number == NoSectionNumber);
+    Number = N;
+  }
+  SizeT getNumber() const {
+    assert(Number != NoSectionNumber);
+    return Number;
+  }
+
+  void setSize(Elf64_Xword sh_size) { Header.sh_size = sh_size; }
+  SizeT getCurrentSize() const { return Header.sh_size; }
+
+  void setNameStrIndex(Elf64_Word sh_name) { Header.sh_name = sh_name; }
+
+  IceString getName() const { return Name; }
+
+  void setLinkNum(Elf64_Word sh_link) { Header.sh_link = sh_link; }
+
+  void setInfoNum(Elf64_Word sh_info) { Header.sh_info = sh_info; }
+
+  void setFileOffset(Elf64_Off sh_offset) { Header.sh_offset = sh_offset; }
+
+  Elf64_Xword getSectionAlign() const { return Header.sh_addralign; }
+
+  // Write the section header out with the given streamer.
+  template <bool IsELF64> void writeHeader(ELFStreamer &Str);
+
+protected:
+  ~ELFSection() {}
+
+  // Name of the section in convenient string form (instead of a index
+  // into the Section Header String Table, which is not known till later).
+  IceString Name;
+
+  // The fields of the header. May only be partially initialized, but should
+  // be fully initialized before writing.
+  Elf64_Shdr Header;
+
+  // The number of the section after laying out sections.
+  SizeT Number;
+};
+
+// Models text/code sections. Code is written out incrementally and the
+// size of the section is then updated incrementally.
+class ELFTextSection : public ELFSection {
+  ELFTextSection(const ELFTextSection &) = delete;
+  ELFTextSection &operator=(const ELFTextSection &) = delete;
+
+public:
+  using ELFSection::ELFSection;
+
+  void appendData(ELFStreamer &Str, const llvm::StringRef MoreData);
+};
+
+// Models data/rodata sections. Data is written out incrementally and the
+// size of the section is then updated incrementally.
+// Some rodata sections may have fixed entsize and duplicates may be mergeable.
+class ELFDataSection : public ELFSection {
+  ELFDataSection(const ELFDataSection &) = delete;
+  ELFDataSection &operator=(const ELFDataSection &) = delete;
+
+public:
+  using ELFSection::ELFSection;
+
+  void appendData(ELFStreamer &Str, const llvm::StringRef MoreData);
+};
+
+// Model of ELF symbol table entries. Besides keeping track of the fields
+// required for an elf symbol table entry it also tracks the number that
+// represents the symbol's final index in the symbol table.
+struct ELFSym {
+  Elf64_Sym Sym;
+  SizeT Number;
+
+  // Sentinel value for symbols that haven't been assigned a number yet.
+  // The dummy 0-th symbol will be assigned number 0, so don't use that.
+  enum { UnknownNumber = std::numeric_limits<SizeT>::max() };
+
+  void setNumber(SizeT N) {
+    assert(Number == UnknownNumber);
+    Number = N;
+  }
+
+  SizeT getNumber() const {
+    assert(Number != UnknownNumber);
+    return Number;
+  }
+};
+
+// Models a symbol table. Symbols may be added up until updateIndices is
+// called. At that point the indices of each symbol will be finalized.
+class ELFSymbolTableSection : public ELFSection {
+public:
+  using ELFSection::ELFSection;
+
+  // Create initial entry for a symbol when it is defined.
+  // Each entry should only be defined once.
+  // We might want to allow Name to be a dummy name initially, then
+  // get updated to the real thing, since Data initializers are read
+  // before the bitcode's symbol table is read.
+  void createDefinedSym(const IceString &Name, uint8_t Type, uint8_t Binding,
+                        ELFSection *Section, RelocOffsetT Offset, SizeT Size);
+
+  // Note that a symbol table entry needs to be created for the given
+  // symbol because it is undefined.
+  void noteUndefinedSym(const IceString &Name, ELFSection *NullSection);
+
+  size_t getSectionDataSize() const {
+    return (LocalSymbols.size() + GlobalSymbols.size()) * Header.sh_entsize;
+  }
+
+  size_t getNumLocals() const { return LocalSymbols.size(); }
+
+  void updateIndices(const ELFStringTableSection *StrTab);
+
+  void writeData(ELFStreamer &Str, bool IsELF64);
+
+private:
+  // Map from symbol name + section to its symbol information.
+  typedef std::pair<IceString, ELFSection *> SymtabKey;
+  typedef std::map<SymtabKey, ELFSym> SymMap;
+
+  template <bool IsELF64>
+  void writeSymbolMap(ELFStreamer &Str, const SymMap &Map);
+
+  // Keep Local and Global symbols separate, since the sh_info needs to
+  // know the index of the last LOCAL.
+  SymMap LocalSymbols;
+  SymMap GlobalSymbols;
+};
+
+// Base model of a relocation section.
+class ELFRelocationSectionBase : public ELFSection {
+  ELFRelocationSectionBase(const ELFRelocationSectionBase &) = delete;
+  ELFRelocationSectionBase &
+  operator=(const ELFRelocationSectionBase &) = delete;
+
+public:
+  ELFRelocationSectionBase(const IceString &Name, Elf64_Word ShType,
+                           Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
+                           Elf64_Xword ShEntsize, ELFSection *RelatedSection)
+      : ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize),
+        RelatedSection(RelatedSection) {}
+
+  ELFSection *getRelatedSection() const { return RelatedSection; }
+
+private:
+  ELFSection *RelatedSection;
+};
+
+// ELFRelocationSection which depends on the actual relocation type.
+// Specializations are needed depending on the ELFCLASS and whether
+// or not addends are explicit or implicitly embedded in the related
+// section (ELFCLASS64 pack their r_info field differently from ELFCLASS32).
+template <typename RelType>
+class ELFRelocationSection : ELFRelocationSectionBase {
+  ELFRelocationSection(const ELFRelocationSectionBase &) = delete;
+  ELFRelocationSection &operator=(const ELFRelocationSectionBase &) = delete;
+
+public:
+  using ELFRelocationSectionBase::ELFRelocationSectionBase;
+
+  void addRelocations() {
+    // TODO: fill me in
+  }
+
+private:
+  typedef std::pair<RelType, ELFSym *> ELFRelSym;
+  typedef std::vector<ELFRelSym> RelocationList;
+  RelocationList Relocations;
+};
+
+// Models a string table.  The user will build the string table by
+// adding strings incrementally.  At some point, all strings should be
+// known and doLayout() should be called. After that, no other
+// strings may be added.  However, the final offsets of the strings
+// can be discovered and used to fill out section headers and symbol
+// table entries.
+class ELFStringTableSection : public ELFSection {
+  ELFStringTableSection(const ELFStringTableSection &) = delete;
+  ELFStringTableSection &operator=(const ELFStringTableSection &) = delete;
+
+public:
+  using ELFSection::ELFSection;
+
+  // Add a string to the table, in preparation for final layout.
+  void add(const IceString &Str);
+
+  // Finalizes the layout of the string table and fills in the section Data.
+  void doLayout();
+
+  // The first byte of the string table should be \0, so it is an
+  // invalid index.  Indices start out as unknown until layout is complete.
+  enum { UnknownIndex = 0 };
+
+  // Grabs the final index of a string after layout. Returns UnknownIndex
+  // if the string's index is not found.
+  size_t getIndex(const IceString &Str) const;
+
+  llvm::StringRef getSectionData() const {
+    assert(isLaidOut());
+    return llvm::StringRef(reinterpret_cast<const char *>(StringData.data()),
+                           StringData.size());
+  }
+
+  size_t getSectionDataSize() const { return getSectionData().size(); }
+
+private:
+  bool isLaidOut() const { return !StringData.empty(); }
+
+  // Strings can share a string table entry if they share the same
+  // suffix.  E.g., "pop" and "lollipop" can both use the characters
+  // in "lollipop", but "pops" cannot, and "unpop" cannot either.
+  // Though, "pop", "lollipop", and "unpop" share "pop" as the suffix,
+  // "pop" can only share the characters with one of them.
+  struct SuffixComparator {
+    bool operator()(const IceString &StrA, const IceString &StrB) const;
+  };
+
+  typedef std::map<IceString, size_t, SuffixComparator> StringToIndexType;
+
+  // Track strings to their index.  Index will be UnknownIndex if not
+  // yet laid out.
+  StringToIndexType StringToIndexMap;
+
+  typedef std::vector<uint8_t> RawDataType;
+  RawDataType StringData;
+};
+
+template <bool IsELF64> void ELFSection::writeHeader(ELFStreamer &Str) {
+  Str.writeELFWord<IsELF64>(Header.sh_name);
+  Str.writeELFWord<IsELF64>(Header.sh_type);
+  Str.writeELFXword<IsELF64>(Header.sh_flags);
+  Str.writeAddrOrOffset<IsELF64>(Header.sh_addr);
+  Str.writeAddrOrOffset<IsELF64>(Header.sh_offset);
+  Str.writeELFXword<IsELF64>(Header.sh_size);
+  Str.writeELFWord<IsELF64>(Header.sh_link);
+  Str.writeELFWord<IsELF64>(Header.sh_info);
+  Str.writeELFXword<IsELF64>(Header.sh_addralign);
+  Str.writeELFXword<IsELF64>(Header.sh_entsize);
+}
+
+template <bool IsELF64>
+void ELFSymbolTableSection::writeSymbolMap(ELFStreamer &Str,
+                                           const SymMap &Map) {
+  // The order of the fields is different, so branch on IsELF64.
+  if (IsELF64) {
+    for (auto &KeyValue : Map) {
+      const Elf64_Sym &SymInfo = KeyValue.second.Sym;
+      Str.writeELFWord<IsELF64>(SymInfo.st_name);
+      Str.write8(SymInfo.st_info);
+      Str.write8(SymInfo.st_other);
+      Str.writeLE16(SymInfo.st_shndx);
+      Str.writeAddrOrOffset<IsELF64>(SymInfo.st_value);
+      Str.writeELFXword<IsELF64>(SymInfo.st_size);
+    }
+  } else {
+    for (auto &KeyValue : Map) {
+      const Elf64_Sym &SymInfo = KeyValue.second.Sym;
+      Str.writeELFWord<IsELF64>(SymInfo.st_name);
+      Str.writeAddrOrOffset<IsELF64>(SymInfo.st_value);
+      Str.writeELFWord<IsELF64>(SymInfo.st_size);
+      Str.write8(SymInfo.st_info);
+      Str.write8(SymInfo.st_other);
+      Str.writeLE16(SymInfo.st_shndx);
+    }
+  }
+}
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEELFSECTION_H
diff --git a/src/IceELFStreamer.h b/src/IceELFStreamer.h
new file mode 100644
index 0000000..ce70e86
--- /dev/null
+++ b/src/IceELFStreamer.h
@@ -0,0 +1,84 @@
+//===- subzero/src/IceELFStreamer.h - Low level ELF writing -----*- C++ -*-===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Interface for serializing bits for common ELF types (words, extended words,
+// etc.), based on the ELF Class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEELFSTREAMER_H
+#define SUBZERO_SRC_ICEELFSTREAMER_H
+
+#include "IceDefs.h"
+
+namespace Ice {
+
+// Low level writer that can that can handle ELFCLASS32/64.
+// Little endian only for now.
+class ELFStreamer {
+public:
+  explicit ELFStreamer(Fdstream &Out) : Out(Out) {}
+
+  void write8(uint8_t Value) { Out << char(Value); }
+
+  void writeLE16(uint16_t Value) {
+    write8(uint8_t(Value));
+    write8(uint8_t(Value >> 8));
+  }
+
+  void writeLE32(uint32_t Value) {
+    writeLE16(uint16_t(Value));
+    writeLE16(uint16_t(Value >> 16));
+  }
+
+  void writeLE64(uint64_t Value) {
+    writeLE32(uint32_t(Value));
+    writeLE32(uint32_t(Value >> 32));
+  }
+
+  template <bool IsELF64, typename T> void writeAddrOrOffset(T Value) {
+    if (IsELF64)
+      writeLE64(Value);
+    else
+      writeLE32(Value);
+  }
+
+  template <bool IsELF64, typename T> void writeELFWord(T Value) {
+    writeLE32(Value);
+  }
+
+  template <bool IsELF64, typename T> void writeELFXword(T Value) {
+    if (IsELF64)
+      writeLE64(Value);
+    else
+      writeLE32(Value);
+  }
+
+  void writeBytes(llvm::StringRef Bytes) { Out << Bytes; }
+
+  void writeZeroPadding(SizeT N) {
+    static const char Zeros[16] = {0};
+
+    for (SizeT i = 0, e = N / 16; i != e; ++i)
+      Out << llvm::StringRef(Zeros, 16);
+
+    Out << llvm::StringRef(Zeros, N % 16);
+  }
+
+  uint64_t tell() const { return Out.tell(); }
+
+  void seek(uint64_t Off) { Out.seek(Off); }
+
+private:
+  Fdstream &Out;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEELFSTREAMER_H
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index 8b345d4..095f288 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -103,17 +103,20 @@
   UndefPool Undefs;
 };
 
-GlobalContext::GlobalContext(llvm::raw_ostream *OsDump,
-                             llvm::raw_ostream *OsEmit, VerboseMask Mask,
+GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
+                             ELFStreamer *ELFStr, VerboseMask Mask,
                              TargetArch Arch, OptLevel Opt,
                              IceString TestPrefix, const ClFlags &Flags)
     : StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
       ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt),
       TestPrefix(TestPrefix), Flags(Flags), HasEmittedFirstMethod(false),
-      RNG("") {
+      RNG(""), ObjectWriter() {
   // Pre-register built-in stack names.
   newTimerStackID("Total across all functions");
   newTimerStackID("Per-function summary");
+  if (Flags.UseELFWriter) {
+    ObjectWriter.reset(new ELFObjectWriter(*this, *ELFStr));
+  }
 }
 
 // Scan a string for S[0-9A-Z]*_ patterns and replace them with
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 02e216b..a082625 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -18,10 +18,10 @@
 #include <memory>
 
 #include "llvm/Support/Allocator.h"
-#include "llvm/Support/raw_ostream.h"
 
 #include "IceDefs.h"
 #include "IceClFlags.h"
+#include "IceELFObjectWriter.h"
 #include "IceIntrinsics.h"
 #include "IceRNG.h"
 #include "IceTimerTree.h"
@@ -74,7 +74,7 @@
   GlobalContext &operator=(const GlobalContext &) = delete;
 
 public:
-  GlobalContext(llvm::raw_ostream *OsDump, llvm::raw_ostream *OsEmit,
+  GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer,
                 VerboseMask Mask, TargetArch Arch, OptLevel Opt,
                 IceString TestPrefix, const ClFlags &Flags);
   ~GlobalContext();
@@ -158,6 +158,8 @@
   // translation.
   RandomNumberGenerator &getRNG() { return RNG; }
 
+  ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
+
   // Reset stats at the beginning of a function.
   void resetStats() { StatsFunction.reset(); }
   void dumpStats(const IceString &Name, bool Final = false);
@@ -212,6 +214,7 @@
   const ClFlags &Flags;
   bool HasEmittedFirstMethod;
   RandomNumberGenerator RNG;
+  std::unique_ptr<ELFObjectWriter> ObjectWriter;
   CodeStats StatsFunction;
   CodeStats StatsCumulative;
   std::vector<TimerStack> Timers;
diff --git a/src/IceMemoryRegion.cpp b/src/IceMemoryRegion.cpp
deleted file mode 100644
index ccfd3cf..0000000
--- a/src/IceMemoryRegion.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-//===- subzero/src/IceMemoryRegion.cpp - Memory region --------------------===//
-// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-//
-// Modified by the Subzero authors.
-//
-//===----------------------------------------------------------------------===//
-//
-//                        The Subzero Code Generator
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the MemoryRegion class. It tracks a pointer plus its
-// bounds for bounds-checking in debug mode.
-//===----------------------------------------------------------------------===//
-
-#include "IceMemoryRegion.h"
-
-namespace Ice {
-
-void MemoryRegion::CopyFrom(uintptr_t offset, const MemoryRegion &from) const {
-  assert(from.pointer() != NULL && from.size() > 0);
-  assert(this->size() >= from.size());
-  assert(offset <= this->size() - from.size());
-  memmove(reinterpret_cast<void *>(start() + offset), from.pointer(),
-          from.size());
-}
-
-} // end of namespace Ice
diff --git a/src/IceMemoryRegion.h b/src/IceMemoryRegion.h
deleted file mode 100644
index b1069a9..0000000
--- a/src/IceMemoryRegion.h
+++ /dev/null
@@ -1,90 +0,0 @@
-//===- subzero/src/IceMemoryRegion.h - Memory region ------------*- C++ -*-===//
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-//
-// Modified by the Subzero authors.
-//
-//===----------------------------------------------------------------------===//
-//
-//                        The Subzero Code Generator
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file declares the MemoryRegion class. It tracks a pointer
-// plus its bounds for bounds-checking in debug mode.
-//===----------------------------------------------------------------------===//
-
-#ifndef SUBZERO_SRC_ICE_MEMORY_REGION_H_
-#define SUBZERO_SRC_ICE_MEMORY_REGION_H_
-
-#include "IceDefs.h"
-#include "IceUtils.h"
-
-namespace Ice {
-
-// Memory regions are useful for accessing memory with bounds check in
-// debug mode. They can be safely passed by value and do not assume ownership
-// of the region.
-class MemoryRegion {
-public:
-  MemoryRegion(const MemoryRegion &other) = default;
-  MemoryRegion &operator=(const MemoryRegion &other) = default;
-  MemoryRegion() : pointer_(NULL), size_(0) {}
-  MemoryRegion(void *pointer, size_t size) : pointer_(pointer), size_(size) {}
-
-  void *pointer() const { return pointer_; }
-  size_t size() const { return size_; }
-
-  size_t start() const { return reinterpret_cast<size_t>(pointer_); }
-  size_t end() const { return start() + size_; }
-
-  template <typename T> T Load(size_t offset) const {
-    return *ComputeInternalPointer<T>(offset);
-  }
-
-  template <typename T> void Store(size_t offset, T value) const {
-    *ComputeInternalPointer<T>(offset) = value;
-  }
-
-  template <typename T> T *PointerTo(size_t offset) const {
-    return ComputeInternalPointer<T>(offset);
-  }
-
-  bool Contains(size_t address) const {
-    return (address >= start()) && (address < end());
-  }
-
-  void CopyFrom(size_t offset, const MemoryRegion &from) const;
-
-  // Compute a sub memory region based on an existing one.
-  void Subregion(const MemoryRegion &from, size_t offset, size_t size) {
-    assert(from.size() >= size);
-    assert(offset <= (from.size() - size));
-    pointer_ = reinterpret_cast<void *>(from.start() + offset);
-    size_ = size;
-  }
-
-  // Compute an extended memory region based on an existing one.
-  void Extend(const MemoryRegion &region, size_t extra) {
-    pointer_ = region.pointer();
-    size_ = (region.size() + extra);
-  }
-
-private:
-  template <typename T> T *ComputeInternalPointer(size_t offset) const {
-    assert(size() >= sizeof(T));
-    assert(offset <= size() - sizeof(T));
-    return reinterpret_cast<T *>(start() + offset);
-  }
-
-  void *pointer_;
-  size_t size_;
-};
-
-} // end of namespace Ice
-
-#endif // SUBZERO_SRC_ICE_MEMORY_REGION_H_
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index da1d3f0..3e2243a 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -24,8 +24,6 @@
 
 namespace Ice {
 
-typedef uint8_t AsmCodeByte;
-
 class Assembler;
 
 // LoweringContext makes it easy to iterate through non-deleted
@@ -165,8 +163,6 @@
   virtual bool hasFramePointer() const { return false; }
   virtual SizeT getFrameOrStackReg() const = 0;
   virtual size_t typeWidthInBytesOnStack(Type Ty) const = 0;
-  virtual SizeT getBundleAlignLog2Bytes() const = 0;
-  virtual llvm::ArrayRef<AsmCodeByte> getNonExecBundlePadding() const = 0;
   bool hasComputedFrame() const { return HasComputedFrame; }
   bool shouldDoNopInsertion() const;
   // Returns true if this function calls a function that has the
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index 937a15b..3fcf8a6 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -4555,6 +4555,9 @@
     : TargetGlobalInitLowering(Ctx) {}
 
 void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) {
+  // TODO(jvoung): handle this without text.
+  if (Ctx->getFlags().UseELFWriter)
+    return;
 
   Ostream &Str = Ctx->getStrEmit();
 
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h
index 920d4cd..2846ac9 100644
--- a/src/IceTargetLoweringX8632.h
+++ b/src/IceTargetLoweringX8632.h
@@ -52,11 +52,6 @@
     // i8, and i16 are rounded up to 4 bytes.
     return (typeWidthInBytes(Ty) + 3) & ~3;
   }
-  SizeT getBundleAlignLog2Bytes() const override { return 5; }
-  llvm::ArrayRef<AsmCodeByte> getNonExecBundlePadding() const override {
-    static const AsmCodeByte Padding[] = { 0xF4 };
-    return llvm::ArrayRef<AsmCodeByte>(Padding, 1);
-  }
   void emitVariable(const Variable *Var) const override;
   void lowerArguments() override;
   void addProlog(CfgNode *Node) override;
diff --git a/src/IceTranslator.cpp b/src/IceTranslator.cpp
index 2a50a41..a6acfb5 100644
--- a/src/IceTranslator.cpp
+++ b/src/IceTranslator.cpp
@@ -97,8 +97,13 @@
 }
 
 void Translator::emitConstants() {
-  if (!Ctx->getFlags().DisableTranslation && Func)
-    Func->getTarget()->emitConstants();
+  if (!Ctx->getFlags().DisableTranslation && Func) {
+    if (Ctx->getFlags().UseELFWriter) {
+      // TODO(jvoung): create the rodata.cst.{4,8} sections for UseELFWriter.
+    } else {
+      Func->getTarget()->emitConstants();
+    }
+  }
 }
 
 void Translator::lowerGlobals(
diff --git a/src/IceTypes.cpp b/src/IceTypes.cpp
index 455aa0c..e6dbadf 100644
--- a/src/IceTypes.cpp
+++ b/src/IceTypes.cpp
@@ -19,8 +19,8 @@
 namespace {
 
 const char *TargetArchName[] = {
-#define X(tag, str) str,
-  TARGETARCH_TABLE
+#define X(tag, str, is_elf64, e_machine, e_flags) str,
+    TARGETARCH_TABLE
 #undef X
 };
 
diff --git a/src/IceTypes.def b/src/IceTypes.def
index 3d4c9b1..0b85ad6 100644
--- a/src/IceTypes.def
+++ b/src/IceTypes.def
@@ -15,13 +15,18 @@
 #ifndef SUBZERO_SRC_ICETYPES_DEF
 #define SUBZERO_SRC_ICETYPES_DEF
 
-#define TARGETARCH_TABLE                                                 \
-  /* enum value, printable string */                                     \
-  X(Target_X8632, "x86-32")                                              \
-  X(Target_X8664, "x86-64")                                              \
-  X(Target_ARM32, "arm32")                                               \
-  X(Target_ARM64, "arm64")                                               \
-//#define X(tag, str)
+// Attributes of each target architecture.
+// NOTE on is_elf64 -- At some point NaCl would like to use ELF32 for all
+// ILP32 sandboxes, but for now the 64-bit architectures use ELF64:
+// https://code.google.com/p/nativeclient/issues/detail?id=349
+// TODO: Whoever adds AArch64 will need to set ABI e_flags.
+#define TARGETARCH_TABLE                                                \
+  /* enum value, printable string, is_elf64, e_machine, e_flags */      \
+  X(Target_X8632, "x86-32", false, EM_386,    0)                        \
+  X(Target_X8664, "x86-64", true,  EM_X86_64, 0)                        \
+  X(Target_ARM32, "arm32",  false, EM_ARM,    EF_ARM_EABI_VER5)         \
+  X(Target_ARM64, "arm64",  true,  EM_AARCH64, 0)                       \
+  //#define X(tag, str, is_elf64, e_machine, e_flags)
 
 #define ICETYPE_TABLE                                                    \
   /* enum value,  size, align, # elts, element type, printable string */ \
diff --git a/src/IceTypes.h b/src/IceTypes.h
index 58f0aa4..662a31a 100644
--- a/src/IceTypes.h
+++ b/src/IceTypes.h
@@ -28,7 +28,7 @@
 };
 
 enum TargetArch {
-#define X(tag, str) tag,
+#define X(tag, str, is_elf64, e_machine, e_flags) tag,
   TARGETARCH_TABLE
 #undef X
       TargetArch_NUM
diff --git a/src/assembler.cpp b/src/assembler.cpp
index b0bd297..a9516e8 100644
--- a/src/assembler.cpp
+++ b/src/assembler.cpp
@@ -20,7 +20,6 @@
 
 #include "assembler.h"
 #include "IceGlobalContext.h"
-#include "IceMemoryRegion.h"
 #include "IceOperand.h"
 
 namespace Ice {
@@ -76,25 +75,6 @@
 
 AssemblerBuffer::~AssemblerBuffer() {}
 
-void AssemblerBuffer::ProcessFixups(const MemoryRegion &region) {
-  for (SizeT I = 0; I < fixups_.size(); ++I) {
-    AssemblerFixup *fixup = fixups_[I];
-    fixup->Process(region, fixup->position());
-  }
-}
-
-void AssemblerBuffer::FinalizeInstructions(const MemoryRegion &instructions) {
-  // Copy the instructions from the buffer.
-  MemoryRegion from(reinterpret_cast<void *>(contents()), Size());
-  instructions.CopyFrom(0, from);
-
-  // Process fixups in the instructions.
-  ProcessFixups(instructions);
-#ifndef NDEBUG
-  fixups_processed_ = true;
-#endif // !NDEBUG
-}
-
 void AssemblerBuffer::ExtendCapacity() {
   intptr_t old_size = Size();
   intptr_t old_capacity = Capacity();
@@ -123,6 +103,11 @@
   assert(Size() == old_size);
 }
 
+llvm::StringRef Assembler::getBufferView() const {
+  return llvm::StringRef(reinterpret_cast<const char *>(buffer_.contents()),
+                         buffer_.Size());
+}
+
 void Assembler::emitIASBytes(GlobalContext *Ctx) const {
   Ostream &Str = Ctx->getStrEmit();
   intptr_t EndPosition = buffer_.Size();
diff --git a/src/assembler.h b/src/assembler.h
index 37963c4..dc70668 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -35,7 +35,6 @@
 class AssemblerFixup;
 class AssemblerBuffer;
 class ConstantRelocatable;
-class MemoryRegion;
 
 // Assembler fixups are positions in generated code that hold relocation
 // information that needs to be processed before finalizing the code
@@ -45,7 +44,6 @@
   AssemblerFixup &operator=(const AssemblerFixup &) = delete;
 
 public:
-  virtual void Process(const MemoryRegion &region, intptr_t position) = 0;
 
   // It would be ideal if the destructor method could be made private,
   // but the g++ compiler complains when this is subclassed.
@@ -109,12 +107,6 @@
   intptr_t Size() const { return cursor_ - contents_; }
   uintptr_t contents() const { return contents_; }
 
-  // Copy the assembled instructions into the specified memory block
-  // and apply all fixups.
-  // TODO(jvoung): This will be different. We'll be writing the text
-  // and reloc section to a file?
-  void FinalizeInstructions(const MemoryRegion &region);
-
 // To emit an instruction to the assembler buffer, the EnsureCapacity helper
 // must be used to guarantee that the underlying data area is big enough to
 // hold the emitted instruction. Usage:
@@ -189,9 +181,6 @@
     return (limit_ - contents_) + kMinimumGap;
   }
 
-  // Process the fixup chain.
-  void ProcessFixups(const MemoryRegion &region);
-
   // Compute the limit based on the data area and the capacity. See
   // description of kMinimumGap for the reasoning behind the value.
   static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) {
@@ -226,8 +215,20 @@
   // Allocate data of type T using the per-Assembler allocator.
   template <typename T> T *Allocate() { return Allocator.Allocate<T>(); }
 
+  // Align the tail end of the function to the required target alignment.
+  virtual void alignFunction() = 0;
+
+  virtual SizeT getBundleAlignLog2Bytes() const = 0;
+
+  virtual llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const = 0;
+
+  // Mark the current text location as the start of a CFG node
+  // (represented by NodeNumber).
   virtual void BindCfgNodeLabel(SizeT NodeNumber) = 0;
 
+  // Return a view of all the bytes of code for the current function.
+  llvm::StringRef getBufferView() const;
+
   void emitIASBytes(GlobalContext *Ctx) const;
 
 private:
diff --git a/src/assembler_ia32.cpp b/src/assembler_ia32.cpp
index 94263e8..30b84e8 100644
--- a/src/assembler_ia32.cpp
+++ b/src/assembler_ia32.cpp
@@ -20,7 +20,6 @@
 
 #include "assembler_ia32.h"
 #include "IceCfg.h"
-#include "IceMemoryRegion.h"
 #include "IceOperand.h"
 
 namespace Ice {
@@ -37,13 +36,6 @@
         DirectCallRelocation(Kind, Sym);
   }
 
-  void Process(const MemoryRegion &region, intptr_t position) override {
-    // Direct calls are relative to the following instruction on x86.
-    int32_t pointer = region.Load<int32_t>(position);
-    int32_t delta = region.start() + position + sizeof(int32_t);
-    region.Store<int32_t>(position, pointer - delta);
-  }
-
 private:
   DirectCallRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
       : AssemblerFixup(Kind, Sym) {}
@@ -77,6 +69,22 @@
 #endif
 }
 
+void AssemblerX86::alignFunction() {
+  intptr_t Pos = buffer_.GetPosition();
+  SizeT Align = 1 << getBundleAlignLog2Bytes();
+  intptr_t Mod = Pos & (Align - 1);
+  if (Mod == 0) {
+    return;
+  }
+  SizeT BytesNeeded = Align - Mod;
+  const SizeT HltSize = 1;
+  while (BytesNeeded > 0) {
+    hlt();
+    BytesNeeded -= HltSize;
+  }
+  assert((buffer_.GetPosition() & (Align - 1)) == 0);
+}
+
 Label *AssemblerX86::GetOrCreateLabel(SizeT Number, LabelVector &Labels) {
   Label *L = nullptr;
   if (Number == Labels.size()) {
diff --git a/src/assembler_ia32.h b/src/assembler_ia32.h
index a7ffc9e..d38dca7 100644
--- a/src/assembler_ia32.h
+++ b/src/assembler_ia32.h
@@ -56,12 +56,6 @@
         DisplacementRelocation(Kind, Sym);
   }
 
-  void Process(const MemoryRegion &region, intptr_t position) override {
-    (void)region;
-    (void)position;
-    llvm_unreachable("We might not be using this Process() method later.");
-  }
-
 private:
   DisplacementRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
       : AssemblerFixup(Kind, Sym) {}
@@ -374,6 +368,15 @@
   static const bool kNearJump = true;
   static const bool kFarJump = false;
 
+  void alignFunction() override;
+
+  SizeT getBundleAlignLog2Bytes() const override { return 5; }
+
+  llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const override {
+    static const uint8_t Padding[] = {0xF4};
+    return llvm::ArrayRef<uint8_t>(Padding, 1);
+  }
+
   Label *GetOrCreateCfgNodeLabel(SizeT NodeNumber);
   void BindCfgNodeLabel(SizeT NodeNumber) override;
   Label *GetOrCreateLocalLabel(SizeT Number);
@@ -825,10 +828,6 @@
 
   intptr_t CodeSize() const { return buffer_.Size(); }
 
-  void FinalizeInstructions(const MemoryRegion &region) {
-    buffer_.FinalizeInstructions(region);
-  }
-
 private:
   inline void EmitUint8(uint8_t value);
   inline void EmitInt16(int16_t value);
diff --git a/src/llvm2ice.cpp b/src/llvm2ice.cpp
index a28b07f..4caa6b8 100644
--- a/src/llvm2ice.cpp
+++ b/src/llvm2ice.cpp
@@ -20,6 +20,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IRReader/IRReader.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/raw_os_ostream.h"
 #include "llvm/Support/SourceMgr.h"
 
@@ -173,6 +174,11 @@
 static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"),
                         cl::NotHidden, cl::aliasopt(UseIntegratedAssembler));
 
+static cl::opt<bool>
+    UseELFWriter("elf-writer",
+                 cl::desc("Use ELF writer with the integrated assembler"),
+                 cl::init(false));
+
 static cl::opt<bool> AlwaysExitSuccess(
     "exit-success", cl::desc("Exit with success status, even if errors found"),
     cl::init(false));
@@ -200,7 +206,7 @@
 
 // Validates values of build attributes. Prints them to Stream if
 // Stream is non-null.
-static void ValidateAndGenerateBuildAttributes(raw_os_ostream *Stream) {
+static void ValidateAndGenerateBuildAttributes(Ice::Ostream *Stream) {
 
   if (Stream)
     *Stream << TargetArch << "\n";
@@ -242,25 +248,20 @@
       VMask |= VerboseList[i];
   }
 
-  std::ofstream Ofs;
-  if (OutputFilename != "-") {
-    Ofs.open(OutputFilename.c_str(), std::ofstream::out);
-  }
-  raw_os_ostream *Os =
-      new raw_os_ostream(OutputFilename == "-" ? std::cout : Ofs);
-  Os->SetUnbuffered();
-
-  ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Os : nullptr);
-  if (GenerateBuildAtts)
-    return GetReturnValue(0);
-
   std::ofstream Lfs;
+  std::unique_ptr<Ice::Ostream> Ls;
   if (LogFilename != "-") {
     Lfs.open(LogFilename.c_str(), std::ofstream::out);
+    Ls.reset(new raw_os_ostream(Lfs));
+  } else {
+    Ls.reset(new raw_os_ostream(std::cout));
   }
-  raw_os_ostream *Ls = new raw_os_ostream(LogFilename == "-" ? std::cout : Lfs);
   Ls->SetUnbuffered();
 
+  ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
+  if (GenerateBuildAtts)
+    return GetReturnValue(0);
+
   if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
     *Ls << "Error: Build doesn't allow --no-ir-gen when not "
         << "ALLOW_DISABLE_IR_GEN!\n";
@@ -274,6 +275,7 @@
   Flags.FunctionSections = FunctionSections;
   Flags.DataSections = DataSections;
   Flags.UseIntegratedAssembler = UseIntegratedAssembler;
+  Flags.UseELFWriter = UseELFWriter;
   Flags.UseSandboxing = UseSandboxing;
   Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
   Flags.DecorateAsm = DecorateAsm;
@@ -294,11 +296,45 @@
                          LLSuffix.length(), LLSuffix) == 0)
     BuildOnRead = false;
 
-  Ice::GlobalContext Ctx(Ls, Os, VMask, TargetArch, OptLevel, TestPrefix,
-                         Flags);
+  // With the ELF writer, use a raw_fd_ostream to allow seeking.
+  // Also don't buffer, otherwise it gets pretty slow.
+  std::unique_ptr<Ice::Ostream> Os;
+  std::unique_ptr<Ice::ELFStreamer> ELFStr;
+  std::ofstream Ofs;
+  if (UseELFWriter) {
+    if (OutputFilename == "-") {
+      *Ls << "Error: writing binary ELF to stdout is unsupported\n";
+      return GetReturnValue(1);
+    }
+    std::string ErrorInfo;
+    raw_fd_ostream *FdOs =
+        new raw_fd_ostream(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None);
+    Os.reset(FdOs);
+    if (!ErrorInfo.empty()) {
+      *Ls << "Failed to open output file: " << OutputFilename << ":\n"
+          << ErrorInfo << "\n";
+      return GetReturnValue(1);
+    }
+    ELFStr.reset(new Ice::ELFStreamer(*FdOs));
+  } else {
+    if (OutputFilename != "-") {
+      Ofs.open(OutputFilename.c_str(), std::ofstream::out);
+      Os.reset(new raw_os_ostream(Ofs));
+    } else {
+      Os.reset(new raw_os_ostream(std::cout));
+    }
+    Os->SetUnbuffered();
+  }
+
+  Ice::GlobalContext Ctx(Ls.get(), Os.get(), ELFStr.get(), VMask, TargetArch,
+                         OptLevel, TestPrefix, Flags);
 
   Ice::TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx);
 
+  if (UseELFWriter) {
+    Ctx.getObjectWriter()->writeInitialELFHeader();
+  }
+
   int ErrorStatus = 0;
   if (BuildOnRead) {
     Ice::PNaClTranslator Translator(&Ctx, Flags);
@@ -324,6 +360,9 @@
         << "--build-on-read=0 not allowed\n";
     return GetReturnValue(1);
   }
+  if (UseELFWriter) {
+    Ctx.getObjectWriter()->writeNonUserSections();
+  }
   if (SubzeroTimingEnabled)
     Ctx.dumpTimers();
   if (TimeEachFunction) {