Write out global initializers and data rel directly to ELF file.

The local symbol relocations are a bit different from
llvm-mc, which are section-relative. E.g., instead "bytes",
it will be ".data + offsetof(bytes, .data)". So the
contents of the text/data/rodata sections can also differ
since the offsets written in place are different.

Still need to fill the symbol table with undefined
symbols (e.g., memset, and szrt lib functions) before
trying to link.

BUG=none
R=kschimpf@google.com, stichnot@chromium.org

Review URL: https://codereview.chromium.org/874353006
diff --git a/src/IceConverter.cpp b/src/IceConverter.cpp
index 0be0f28..e8ec56d 100644
--- a/src/IceConverter.cpp
+++ b/src/IceConverter.cpp
@@ -643,9 +643,8 @@
   /// Converts global variables, and their initializers into ICE
   /// global variable declarations, for module Mod. Puts corresponding
   /// converted declarations into VariableDeclarations.
-  void convertGlobalsToIce(
-      Module *Mod,
-      Ice::Translator::VariableDeclarationListType &VariableDeclarations);
+  void convertGlobalsToIce(Module *Mod,
+                           Ice::VariableDeclarationList &VariableDeclarations);
 
 private:
   // Adds the Initializer to the list of initializers for the Global
@@ -681,8 +680,7 @@
 };
 
 void LLVM2ICEGlobalsConverter::convertGlobalsToIce(
-    Module *Mod,
-    Ice::Translator::VariableDeclarationListType &VariableDeclarations) {
+    Module *Mod, Ice::VariableDeclarationList &VariableDeclarations) {
   for (Module::const_global_iterator I = Mod->global_begin(),
                                      E = Mod->global_end();
        I != E; ++I) {
@@ -868,7 +866,7 @@
 
 void Converter::convertGlobals(Module *Mod) {
   LLVM2ICEGlobalsConverter GlobalsConverter(*this);
-  Translator::VariableDeclarationListType VariableDeclarations;
+  VariableDeclarationList VariableDeclarations;
   GlobalsConverter.convertGlobalsToIce(Mod, VariableDeclarations);
   lowerGlobals(VariableDeclarations);
 }
diff --git a/src/IceDefs.h b/src/IceDefs.h
index e7d97db..368e92c 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -60,7 +60,7 @@
 class LiveRange;
 class Liveness;
 class Operand;
-class TargetGlobalLowering;
+class TargetDataLowering;
 class TargetLowering;
 class Variable;
 class VariableDeclaration;
@@ -103,6 +103,8 @@
 typedef std::vector<CfgNode *, CfgLocalAllocator<CfgNode *>> NodeList;
 typedef std::vector<Constant *> ConstantList;
 
+typedef std::vector<VariableDeclaration *> VariableDeclarationList;
+
 // SizeT is for holding small-ish limits like number of source
 // operands in an instruction.  It is used instead of size_t (which
 // may be 64-bits wide) when we want to save space.
diff --git a/src/IceELFObjectWriter.cpp b/src/IceELFObjectWriter.cpp
index c4ff4f0..d15c735 100644
--- a/src/IceELFObjectWriter.cpp
+++ b/src/IceELFObjectWriter.cpp
@@ -11,6 +11,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/Support/MathExtras.h"
+
 #include "assembler.h"
 #include "IceDefs.h"
 #include "IceELFObjectWriter.h"
@@ -103,6 +105,28 @@
   return NewSection;
 }
 
+ELFRelocationSection *
+ELFObjectWriter::createRelocationSection(bool IsELF64,
+                                         const ELFSection *RelatedSection) {
+  // Choice of RELA vs REL is actually separate from elf64 vs elf32,
+  // but in practice we've only had .rela for elf64 (x86-64).
+  // In the future, the two properties may need to be decoupled
+  // and the ShEntSize can vary more.
+  const Elf64_Word ShType = IsELF64 ? SHT_RELA : SHT_REL;
+  IceString RelPrefix = IsELF64 ? ".rela" : ".rel";
+  IceString RelSectionName = RelPrefix + RelatedSection->getName();
+  const Elf64_Xword ShAlign = IsELF64 ? 8 : 4;
+  const Elf64_Xword ShEntSize =
+      IsELF64 ? sizeof(Elf64_Rela) : sizeof(Elf32_Rel);
+  static_assert(sizeof(Elf64_Rela) == 24 && sizeof(Elf32_Rel) == 8,
+                "Elf_Rel/Rela sizes cannot be derived from sizeof");
+  const Elf64_Xword ShFlags = 0;
+  ELFRelocationSection *RelSection = createSection<ELFRelocationSection>(
+      RelSectionName, ShType, ShFlags, ShAlign, ShEntSize);
+  RelSection->setRelatedSection(RelatedSection);
+  return RelSection;
+}
+
 template <typename UserSectionList>
 void ELFObjectWriter::assignRelSectionNumInPairs(SizeT &CurSectionNumber,
                                                  UserSectionList &UserSections,
@@ -150,8 +174,13 @@
                                               RelTextSections, AllSections);
   assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, DataSections,
                                               RelDataSections, AllSections);
-  assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, RoDataSections,
-                                              RelRoDataSections, AllSections);
+  for (ELFSection *BSSSection : BSSSections) {
+    BSSSection->setNumber(CurSectionNumber++);
+    BSSSection->setNameStrIndex(ShStrTab->getIndex(BSSSection->getName()));
+    AllSections.push_back(BSSSection);
+  }
+  assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, RODataSections,
+                                              RelRODataSections, AllSections);
 
   ShStrTab->setNumber(CurSectionNumber++);
   ShStrTab->setNameStrIndex(ShStrTab->getIndex(ShStrTab->getName()));
@@ -170,20 +199,17 @@
 
   assignRelLinkNum(SymTab->getNumber(), RelTextSections);
   assignRelLinkNum(SymTab->getNumber(), RelDataSections);
-  assignRelLinkNum(SymTab->getNumber(), RelRoDataSections);
+  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)
+  Elf64_Xword AlignDiff = Utils::OffsetToAlignment(OffsetInFile, Align);
+  if (AlignDiff == 0)
     return OffsetInFile;
-  Elf64_Xword AlignDiff = Align - Mod;
   Str.writeZeroPadding(AlignDiff);
   OffsetInFile += AlignDiff;
-  assert((OffsetInFile & (Align - 1)) == 0);
   return OffsetInFile;
 }
 
@@ -191,9 +217,11 @@
                                         bool IsInternal, const Assembler *Asm) {
   assert(!SectionNumbersAssigned);
   ELFTextSection *Section = nullptr;
-  // TODO(jvoung): handle ffunction-sections.
-  IceString SectionName = ".text";
-  if (TextSections.size() == 0) {
+  ELFRelocationSection *RelSection = nullptr;
+  if (TextSections.empty()) {
+    // TODO(jvoung): handle ffunction-sections.
+    IceString SectionName = ".text";
+    bool IsELF64 = isELF64(Ctx.getTargetArch());
     const Elf64_Xword ShFlags = SHF_ALLOC | SHF_EXECINSTR;
     // TODO(jvoung): Should be bundle size. Grab it from that target?
     const Elf64_Xword ShAlign = 32;
@@ -202,8 +230,11 @@
     Elf64_Off OffsetInFile = alignFileOffset(Section->getSectionAlign());
     Section->setFileOffset(OffsetInFile);
     TextSections.push_back(Section);
+    RelSection = createRelocationSection(IsELF64, Section);
+    RelTextSections.push_back(RelSection);
   } else {
     Section = TextSections[0];
+    RelSection = RelTextSections[0];
   }
   RelocOffsetT OffsetInSection = Section->getCurrentSize();
   // Function symbols are set to 0 size in the symbol table,
@@ -227,41 +258,158 @@
   // fixup information from per-function Assembler memory to the object
   // writer's memory, for writing later.
   if (!Asm->fixups().empty()) {
-    bool IsELF64 = isELF64(Ctx.getTargetArch());
-    IceString RelSectionName = IsELF64 ? ".rela" : ".rel";
-    RelSectionName += SectionName;
-    ELFRelocationSection *RelSection = nullptr;
-    // TODO(jvoung): Make this more efficient if -ffunction-sections
-    // efficiency becomes a problem.
-    auto RSI =
-        std::find_if(RelTextSections.begin(), RelTextSections.end(),
-                     [&RelSectionName](const ELFRelocationSection *S)
-                         -> bool { return S->getName() == RelSectionName; });
-    if (RSI != RelTextSections.end()) {
-      RelSection = *RSI;
-    } else {
-      const Elf64_Word ShType = IsELF64 ? SHT_RELA : SHT_REL;
-      const Elf64_Xword ShAlign = IsELF64 ? 8 : 4;
-      const Elf64_Xword ShEntSize =
-          IsELF64 ? sizeof(Elf64_Rela) : sizeof(Elf32_Rel);
-      static_assert(sizeof(Elf64_Rela) == 24 && sizeof(Elf32_Rel) == 8,
-                    "Elf_Rel/Rela sizes cannot be derived from sizeof");
-      const Elf64_Xword ShFlags = 0;
-      RelSection = createSection<ELFRelocationSection>(
-          RelSectionName, ShType, ShFlags, ShAlign, ShEntSize);
-      RelSection->setRelatedSection(Section);
-      RelTextSections.push_back(RelSection);
-    }
     RelSection->addRelocations(OffsetInSection, Asm->fixups());
   }
 }
 
-void ELFObjectWriter::writeDataInitializer(const IceString &VarName,
-                                           const llvm::StringRef Data) {
+namespace {
+
+ELFObjectWriter::SectionType
+classifyGlobalSection(const VariableDeclaration *Var) {
+  if (Var->getIsConstant())
+    return ELFObjectWriter::ROData;
+  if (Var->hasNonzeroInitializer())
+    return ELFObjectWriter::Data;
+  return ELFObjectWriter::BSS;
+}
+
+// Partition the Vars list by SectionType into VarsBySection.
+// If TranslateOnly is non-empty, then only the TranslateOnly variable
+// is kept for emission.
+void partitionGlobalsBySection(const VariableDeclarationList &Vars,
+                               VariableDeclarationList VarsBySection[],
+                               const IceString &TranslateOnly) {
+  for (VariableDeclaration *Var : Vars) {
+    if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
+      size_t Section = classifyGlobalSection(Var);
+      assert(Section < ELFObjectWriter::SectionType::NumSectionTypes);
+      VarsBySection[Section].push_back(Var);
+    }
+  }
+}
+
+} // end of anonymous namespace
+
+void ELFObjectWriter::writeDataSection(const VariableDeclarationList &Vars,
+                                       FixupKind RelocationKind) {
   assert(!SectionNumbersAssigned);
-  (void)Data;
-  llvm_unreachable("TODO");
-  StrTab->add(VarName);
+  VariableDeclarationList VarsBySection[ELFObjectWriter::NumSectionTypes];
+  for (auto &SectionList : VarsBySection)
+    SectionList.reserve(Vars.size());
+  partitionGlobalsBySection(Vars, VarsBySection, Ctx.getFlags().TranslateOnly);
+  bool IsELF64 = isELF64(Ctx.getTargetArch());
+  size_t I = 0;
+  for (auto &SectionList : VarsBySection) {
+    writeDataOfType(static_cast<SectionType>(I++), SectionList, RelocationKind,
+                    IsELF64);
+  }
+}
+
+void ELFObjectWriter::writeDataOfType(SectionType SectionType,
+                                      const VariableDeclarationList &Vars,
+                                      FixupKind RelocationKind, bool IsELF64) {
+  ELFDataSection *Section;
+  ELFRelocationSection *RelSection;
+  // TODO(jvoung): Handle fdata-sections.
+  IceString SectionName;
+  Elf64_Xword ShAddralign = 0;
+  for (VariableDeclaration *Var : Vars) {
+    Elf64_Xword Align = Var->getAlignment();
+    ShAddralign = std::max(ShAddralign, Align);
+  }
+  const Elf64_Xword ShEntsize = 0; // non-uniform data element size.
+  // Lift this out, so it can be re-used if we do fdata-sections?
+  switch (SectionType) {
+  case SectionType::ROData: {
+    SectionName = ".rodata";
+    // Only expecting to write the data sections all in one shot for now.
+    assert(RODataSections.empty());
+    const Elf64_Xword ShFlags = SHF_ALLOC;
+    Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
+                                            ShAddralign, ShEntsize);
+    Section->setFileOffset(alignFileOffset(ShAddralign));
+    RODataSections.push_back(Section);
+    RelSection = createRelocationSection(IsELF64, Section);
+    RelRODataSections.push_back(RelSection);
+    break;
+  }
+  case SectionType::Data: {
+    SectionName = ".data";
+    assert(DataSections.empty());
+    const Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
+    Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
+                                            ShAddralign, ShEntsize);
+    Section->setFileOffset(alignFileOffset(ShAddralign));
+    DataSections.push_back(Section);
+    RelSection = createRelocationSection(IsELF64, Section);
+    RelDataSections.push_back(RelSection);
+    break;
+  }
+  case SectionType::BSS: {
+    SectionName = ".bss";
+    assert(BSSSections.empty());
+    const Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
+    Section = createSection<ELFDataSection>(SectionName, SHT_NOBITS, ShFlags,
+                                            ShAddralign, ShEntsize);
+    Section->setFileOffset(alignFileOffset(ShAddralign));
+    BSSSections.push_back(Section);
+    break;
+  }
+  case SectionType::NumSectionTypes:
+    llvm::report_fatal_error("Unknown SectionType");
+    break;
+  }
+
+  const uint8_t SymbolType = STT_OBJECT;
+  for (VariableDeclaration *Var : Vars) {
+    Elf64_Xword Align = Var->getAlignment();
+    Section->padToAlignment(Str, Align);
+    SizeT SymbolSize = Var->getNumBytes();
+    bool IsExternal = Var->isExternal() || Ctx.getFlags().DisableInternal;
+    const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
+    IceString MangledName = Var->mangleName(&Ctx);
+    SymTab->createDefinedSym(MangledName, SymbolType, SymbolBinding, Section,
+                             Section->getCurrentSize(), SymbolSize);
+    StrTab->add(MangledName);
+    if (!Var->hasNonzeroInitializer()) {
+      assert(SectionType == SectionType::BSS ||
+             SectionType == SectionType::ROData);
+      if (SectionType == SectionType::ROData)
+        Section->appendZeros(Str, SymbolSize);
+      else
+        Section->setSize(Section->getCurrentSize() + SymbolSize);
+    } else {
+      assert(SectionType != SectionType::BSS);
+      for (VariableDeclaration::Initializer *Init : Var->getInitializers()) {
+        switch (Init->getKind()) {
+        case VariableDeclaration::Initializer::DataInitializerKind: {
+          const auto Data = llvm::cast<VariableDeclaration::DataInitializer>(
+                                Init)->getContents();
+          Section->appendData(Str, llvm::StringRef(Data.data(), Data.size()));
+          break;
+        }
+        case VariableDeclaration::Initializer::ZeroInitializerKind:
+          Section->appendZeros(Str, Init->getNumBytes());
+          break;
+        case VariableDeclaration::Initializer::RelocInitializerKind: {
+          const auto Reloc =
+              llvm::cast<VariableDeclaration::RelocInitializer>(Init);
+          AssemblerFixup NewFixup;
+          NewFixup.set_position(Section->getCurrentSize());
+          NewFixup.set_kind(RelocationKind);
+          const bool SuppressMangling = true;
+          NewFixup.set_value(Ctx.getConstantSym(
+              Reloc->getOffset(), Reloc->getDeclaration()->mangleName(&Ctx),
+              SuppressMangling));
+          RelSection->addRelocation(NewFixup);
+          Section->appendRelocationOffset(Str, RelSection->isRela(),
+                                          Reloc->getOffset());
+          break;
+        }
+        }
+      }
+    }
+  }
 }
 
 void ELFObjectWriter::writeInitialELFHeader() {
@@ -345,7 +493,7 @@
   SecStrBuf << ".rodata.cst" << WriteAmt;
   ELFDataSection *Section = createSection<ELFDataSection>(
       SecStrBuf.str(), SHT_PROGBITS, ShFlags, Align, WriteAmt);
-  RoDataSections.push_back(Section);
+  RODataSections.push_back(Section);
   SizeT OffsetInSection = 0;
   // The symbol table entry doesn't need to know the defined symbol's
   // size since this is in a section with a fixed Entry Size.
@@ -382,7 +530,7 @@
 void ELFObjectWriter::writeAllRelocationSections(bool IsELF64) {
   writeRelocationSections(IsELF64, RelTextSections);
   writeRelocationSections(IsELF64, RelDataSections);
-  writeRelocationSections(IsELF64, RelRoDataSections);
+  writeRelocationSections(IsELF64, RelRODataSections);
 }
 
 void ELFObjectWriter::writeRelocationSections(bool IsELF64,
diff --git a/src/IceELFObjectWriter.h b/src/IceELFObjectWriter.h
index 9f468cb..74fee5e 100644
--- a/src/IceELFObjectWriter.h
+++ b/src/IceELFObjectWriter.h
@@ -29,10 +29,23 @@
 // 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
+// (1) writeInitialELFHeader (invoke once)
+// (2) writeDataSection  (invoke once)
+// (3) writeFunctionCode (must invoke once per function)
+// (4) writeConstantPool (must invoke once per pooled primitive type)
+// (5) writeNonUserSections (invoke once)
+//
+// The requirement for writeDataSection to be invoked only once can
+// be relaxed if using -fdata-sections. The requirement to invoke only once
+// without -fdata-sections is so that variables that belong to each possible
+// SectionType are contiguous in the file. With -fdata-sections, each global
+// variable is in a separate section and therefore the sections will be
+// trivially contiguous.
+//
+// The motivation for requiring that writeFunctionCode happen after
+// writeDataSection: to keep the .text and .data sections contiguous in the
+// file. Having both -fdata-sections and -ffunction-sections does allow
+// relaxing this requirement.
 class ELFObjectWriter {
   ELFObjectWriter(const ELFObjectWriter &) = delete;
   ELFObjectWriter &operator=(const ELFObjectWriter &) = delete;
@@ -45,26 +58,32 @@
   // and data directly to the file and get the right file offsets.
   void writeInitialELFHeader();
 
+  // Copy initializer data for globals to file and note the offset and size
+  // of each global's definition in the symbol table.
+  // Use the given target's RelocationKind for any relocations.
+  void writeDataSection(const VariableDeclarationList &Vars,
+                        FixupKind RelocationKind);
+
   // Copy data of a function's text section to file and note the offset of the
   // symbol's definition in the symbol table.
   // Copy the text fixups for use after all functions are written.
+  // The text buffer and fixups are extracted from the Assembler object.
   void writeFunctionCode(const IceString &FuncName, bool IsInternal,
                          const Assembler *Asm);
 
-  // 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);
-
+  // Queries the GlobalContext for constant pools of the given type
+  // and writes out read-only data sections for those constants. This also
+  // fills the symbol table with labels for each constant pool entry.
   template <typename ConstType> void writeConstantPool(Type Ty);
 
-  // Do final layout and write out the rest of the object file, then
-  // patch up the initial ELF header with the final info.
+  // Do final layout and write out the rest of the object file.
+  // Finally, patch up the initial ELF header with the final info.
   void writeNonUserSections();
 
+  // Which type of ELF section a global variable initializer belongs to.
+  // This is used as an array index so should start at 0 and be contiguous.
+  enum SectionType { ROData = 0, Data, BSS, NumSectionTypes };
+
 private:
   GlobalContext &Ctx;
   ELFStreamer &Str;
@@ -79,8 +98,9 @@
   RelSectionList RelTextSections;
   DataSectionList DataSections;
   RelSectionList RelDataSections;
-  DataSectionList RoDataSections;
-  RelSectionList RelRoDataSections;
+  DataSectionList RODataSections;
+  RelSectionList RelRODataSections;
+  DataSectionList BSSSections;
 
   // Handles to special sections that need incremental bookkeeping.
   ELFSection *NullSection;
@@ -93,6 +113,11 @@
                    Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
                    Elf64_Xword ShEntsize);
 
+  // Create a relocation section, given the related section
+  // (e.g., .text, .data., .rodata).
+  ELFRelocationSection *
+  createRelocationSection(bool IsELF64, const ELFSection *RelatedSection);
+
   // Align the file position before writing out a section's data,
   // and return the position of the file.
   Elf64_Off alignFileOffset(Elf64_Xword Align);
@@ -116,6 +141,12 @@
   // Link the relocation sections to the symbol table.
   void assignRelLinkNum(SizeT SymTabNumber, RelSectionList &RelSections);
 
+  // Helper function for writeDataSection. Writes a data section of type
+  // SectionType, given the global variables Vars belonging to that SectionType.
+  void writeDataOfType(SectionType SectionType,
+                       const VariableDeclarationList &Vars,
+                       FixupKind RelocationKind, bool IsELF64);
+
   // Write the final relocation sections given the final symbol table.
   // May also be able to seek around the file and resolve function calls
   // that are for functions within the same section.
diff --git a/src/IceELFSection.cpp b/src/IceELFSection.cpp
index 5dcc206..f6fb526 100644
--- a/src/IceELFSection.cpp
+++ b/src/IceELFSection.cpp
@@ -11,6 +11,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/Support/MathExtras.h"
+
 #include "IceDefs.h"
 #include "IceELFSection.h"
 #include "IceELFStreamer.h"
@@ -35,6 +37,32 @@
   Header.sh_size += MoreData.size();
 }
 
+void ELFDataSection::appendZeros(ELFStreamer &Str, SizeT NumBytes) {
+  Str.writeZeroPadding(NumBytes);
+  Header.sh_size += NumBytes;
+}
+
+void ELFDataSection::appendRelocationOffset(ELFStreamer &Str, bool IsRela,
+                                            RelocOffsetT RelocOffset) {
+  if (IsRela) {
+    appendZeros(Str, RelocAddrSize);
+    return;
+  }
+  static_assert(RelocAddrSize == 4, " writeLE32 assumes RelocAddrSize is 4");
+  Str.writeLE32(RelocOffset);
+  Header.sh_size += RelocAddrSize;
+}
+
+void ELFDataSection::padToAlignment(ELFStreamer &Str, Elf64_Xword Align) {
+  assert(llvm::isPowerOf2_32(Align));
+  Elf64_Xword AlignDiff = Utils::OffsetToAlignment(Header.sh_size, Align);
+  if (AlignDiff == 0)
+    return;
+  if (Header.sh_type != llvm::ELF::SHT_NOBITS)
+    Str.writeZeroPadding(AlignDiff);
+  Header.sh_size += AlignDiff;
+}
+
 // Relocation sections.
 
 void ELFRelocationSection::addRelocations(RelocOffsetT BaseOff,
@@ -73,7 +101,7 @@
   NewSymbol.Section = Section;
   NewSymbol.Number = ELFSym::UnknownNumber;
   bool Unique;
-  if (Type == STB_LOCAL)
+  if (Binding == STB_LOCAL)
     Unique = LocalSymbols.insert(std::make_pair(Name, NewSymbol)).second;
   else
     Unique = GlobalSymbols.insert(std::make_pair(Name, NewSymbol)).second;
diff --git a/src/IceELFSection.h b/src/IceELFSection.h
index 5821a83..db000f4 100644
--- a/src/IceELFSection.h
+++ b/src/IceELFSection.h
@@ -117,6 +117,16 @@
   using ELFSection::ELFSection;
 
   void appendData(ELFStreamer &Str, const llvm::StringRef MoreData);
+
+  void appendZeros(ELFStreamer &Str, SizeT NumBytes);
+
+  void appendRelocationOffset(ELFStreamer &Str, bool IsRela,
+                              RelocOffsetT RelocOffset);
+
+  // Pad the next section offset for writing data elements to the requested
+  // alignment. If the section is NOBITS then do not actually write out
+  // the padding and only update the section size.
+  void padToAlignment(ELFStreamer &Str, Elf64_Xword Align);
 };
 
 // Model of ELF symbol table entries. Besides keeping track of the fields
@@ -195,13 +205,18 @@
 public:
   using ELFSection::ELFSection;
 
-  ELFSection *getRelatedSection() const { return RelatedSection; }
-  void setRelatedSection(ELFSection *Section) { RelatedSection = Section; }
+  const ELFSection *getRelatedSection() const { return RelatedSection; }
+  void setRelatedSection(const ELFSection *Section) {
+    RelatedSection = Section;
+  }
 
   // Track additional relocations which start out relative to offset 0,
   // but should be adjusted to be relative to BaseOff.
   void addRelocations(RelocOffsetT BaseOff, const FixupRefList &FixupRefs);
 
+  // Track a single additional relocation.
+  void addRelocation(const AssemblerFixup &Fixup) { Fixups.push_back(Fixup); }
+
   size_t getSectionDataSize(const GlobalContext &Ctx,
                             const ELFSymbolTableSection *SymTab) const;
 
@@ -209,8 +224,10 @@
   void writeData(const GlobalContext &Ctx, ELFStreamer &Str,
                  const ELFSymbolTableSection *SymTab);
 
+  bool isRela() const { return Header.sh_type == SHT_RELA; }
+
 private:
-  ELFSection *RelatedSection;
+  const ELFSection *RelatedSection;
   FixupList Fixups;
 };
 
diff --git a/src/IceGlobalInits.h b/src/IceGlobalInits.h
index 3478a98..36d4612 100644
--- a/src/IceGlobalInits.h
+++ b/src/IceGlobalInits.h
@@ -157,7 +157,7 @@
   };
 
   // Models the data in a data initializer.
-  typedef std::vector<uint8_t> DataVecType;
+  typedef std::vector<char> DataVecType;
 
   /// Defines a sequence of byte values as a data initializer.
   class DataInitializer : public Initializer {
@@ -170,14 +170,14 @@
         : Initializer(DataInitializerKind), Contents(Values.size()) {
       size_t i = 0;
       for (auto &V : Values) {
-        Contents[i] = static_cast<uint8_t>(V);
+        Contents[i] = static_cast<int8_t>(V);
         ++i;
       }
     }
     DataInitializer(const char *Str, size_t StrLen)
         : Initializer(DataInitializerKind), Contents(StrLen) {
       for (size_t i = 0; i < StrLen; ++i)
-        Contents[i] = static_cast<uint8_t>(Str[i]);
+        Contents[i] = Str[i];
     }
     ~DataInitializer() override {}
     const DataVecType &getContents() const { return Contents; }
diff --git a/src/IceTargetLowering.cpp b/src/IceTargetLowering.cpp
index 27a2d60..8b7c257 100644
--- a/src/IceTargetLowering.cpp
+++ b/src/IceTargetLowering.cpp
@@ -252,24 +252,24 @@
   LinearScan.scan(RegMask, RandomizeRegisterAllocation);
 }
 
-TargetGlobalLowering *TargetGlobalLowering::createLowering(GlobalContext *Ctx) {
+TargetDataLowering *TargetDataLowering::createLowering(GlobalContext *Ctx) {
   // These statements can be #ifdef'd to specialize the code generator
   // to a subset of the available targets.  TODO: use CRTP.
   TargetArch Target = Ctx->getTargetArch();
   if (Target == Target_X8632)
-    return TargetGlobalX8632::create(Ctx);
+    return TargetDataX8632::create(Ctx);
 #if 0
   if (Target == Target_X8664)
-    return TargetGlobalX8664::create(Ctx);
+    return TargetDataX8664::create(Ctx);
   if (Target == Target_ARM32)
-    return TargetGlobalARM32::create(Ctx);
+    return TargetDataARM32::create(Ctx);
   if (Target == Target_ARM64)
-    return TargetGlobalARM64::create(Ctx);
+    return TargetDataARM64::create(Ctx);
 #endif
   llvm_unreachable("Unsupported target");
   return nullptr;
 }
 
-TargetGlobalLowering::~TargetGlobalLowering() {}
+TargetDataLowering::~TargetDataLowering() {}
 
 } // end of namespace Ice
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index 17d785e..2dda5c5 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -238,23 +238,24 @@
   LoweringContext Context;
 };
 
-// TargetGlobalLowering is used for "lowering" global initializers,
-// including the internal constant pool.  It is separated out from
-// TargetLowering because it does not require a Cfg.
-class TargetGlobalLowering {
-  TargetGlobalLowering() = delete;
-  TargetGlobalLowering(const TargetGlobalLowering &) = delete;
-  TargetGlobalLowering &operator=(const TargetGlobalLowering &) = delete;
+// TargetDataLowering is used for "lowering" data including initializers
+// for global variables, and the internal constant pools.  It is separated
+// out from TargetLowering because it does not require a Cfg.
+class TargetDataLowering {
+  TargetDataLowering() = delete;
+  TargetDataLowering(const TargetDataLowering &) = delete;
+  TargetDataLowering &operator=(const TargetDataLowering &) = delete;
 
 public:
-  static TargetGlobalLowering *createLowering(GlobalContext *Ctx);
-  virtual ~TargetGlobalLowering();
+  static TargetDataLowering *createLowering(GlobalContext *Ctx);
+  virtual ~TargetDataLowering();
 
-  virtual void lowerInit(const VariableDeclaration &Var) const = 0;
+  virtual void lowerGlobal(const VariableDeclaration &Var) const = 0;
+  virtual void lowerGlobalsELF(const VariableDeclarationList &Vars) const = 0;
   virtual void lowerConstants(GlobalContext *Ctx) const = 0;
 
 protected:
-  TargetGlobalLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
+  TargetDataLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
   GlobalContext *Ctx;
 };
 
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index 2836865..8e57ba6 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -4560,25 +4560,19 @@
   llvm_unreachable("undef value encountered by emitter.");
 }
 
-TargetGlobalX8632::TargetGlobalX8632(GlobalContext *Ctx)
-    : TargetGlobalLowering(Ctx) {}
+TargetDataX8632::TargetDataX8632(GlobalContext *Ctx)
+    : TargetDataLowering(Ctx) {}
 
-void TargetGlobalX8632::lowerInit(const VariableDeclaration &Var) const {
-  // TODO(jvoung): handle this without text.
-  if (Ctx->getFlags().UseELFWriter)
-    return;
-
-  Ostream &Str = Ctx->getStrEmit();
-
-  const VariableDeclaration::InitializerListType &Initializers =
-      Var.getInitializers();
-
+void TargetDataX8632::lowerGlobal(const VariableDeclaration &Var) const {
   // If external and not initialized, this must be a cross test.
   // Don't generate a declaration for such cases.
   bool IsExternal = Var.isExternal() || Ctx->getFlags().DisableInternal;
   if (IsExternal && !Var.hasInitializer())
     return;
 
+  Ostream &Str = Ctx->getStrEmit();
+  const VariableDeclaration::InitializerListType &Initializers =
+      Var.getInitializers();
   bool HasNonzeroInitializer = Var.hasNonzeroInitializer();
   bool IsConstant = Var.getIsConstant();
   uint32_t Align = Var.getAlignment();
@@ -4645,6 +4639,12 @@
   Str << "\t.size\t" << MangledName << ", " << Size << "\n";
 }
 
+void
+TargetDataX8632::lowerGlobalsELF(const VariableDeclarationList &Vars) const {
+  ELFObjectWriter *Writer = Ctx->getObjectWriter();
+  Writer->writeDataSection(Vars, llvm::ELF::R_386_32);
+}
+
 template <typename T> struct PoolTypeConverter {};
 
 template <> struct PoolTypeConverter<float> {
@@ -4672,7 +4672,7 @@
 const char *PoolTypeConverter<double>::PrintfString = "0x%llx";
 
 template <typename T>
-void TargetGlobalX8632::emitConstantPool(GlobalContext *Ctx) {
+void TargetDataX8632::emitConstantPool(GlobalContext *Ctx) {
   // Note: Still used by emit IAS.
   Ostream &Str = Ctx->getStrEmit();
   Type Ty = T::Ty;
@@ -4701,7 +4701,7 @@
   }
 }
 
-void TargetGlobalX8632::lowerConstants(GlobalContext *Ctx) const {
+void TargetDataX8632::lowerConstants(GlobalContext *Ctx) const {
   if (Ctx->getFlags().DisableTranslation)
     return;
   // No need to emit constants from the int pool since (for x86) they
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h
index 1ee3c1d..de0e925 100644
--- a/src/IceTargetLoweringX8632.h
+++ b/src/IceTargetLoweringX8632.h
@@ -488,24 +488,25 @@
   ~TargetX8632() override {}
 };
 
-class TargetGlobalX8632 : public TargetGlobalLowering {
-  TargetGlobalX8632() = delete;
-  TargetGlobalX8632(const TargetGlobalX8632 &) = delete;
-  TargetGlobalX8632 &operator=(const TargetGlobalX8632 &) = delete;
+class TargetDataX8632 : public TargetDataLowering {
+  TargetDataX8632() = delete;
+  TargetDataX8632(const TargetDataX8632 &) = delete;
+  TargetDataX8632 &operator=(const TargetDataX8632 &) = delete;
 
 public:
-  static TargetGlobalLowering *create(GlobalContext *Ctx) {
-    return new TargetGlobalX8632(Ctx);
+  static TargetDataLowering *create(GlobalContext *Ctx) {
+    return new TargetDataX8632(Ctx);
   }
 
-  virtual void lowerInit(const VariableDeclaration &Var) const final;
-  virtual void lowerConstants(GlobalContext *Ctx) const final;
+  void lowerGlobal(const VariableDeclaration &Var) const final;
+  void lowerGlobalsELF(const VariableDeclarationList &Vars) const final;
+  void lowerConstants(GlobalContext *Ctx) const final;
 
 protected:
-  TargetGlobalX8632(GlobalContext *Ctx);
+  TargetDataX8632(GlobalContext *Ctx);
 
 private:
-  ~TargetGlobalX8632() override {}
+  ~TargetDataX8632() override {}
   template <typename T> static void emitConstantPool(GlobalContext *Ctx);
 };
 
diff --git a/src/IceTimerTree.def b/src/IceTimerTree.def
index 3139a81..dba5ae7 100644
--- a/src/IceTimerTree.def
+++ b/src/IceTimerTree.def
@@ -26,6 +26,7 @@
   X(doBranchOpt)               \
   X(doNopInsertion)            \
   X(emit)                      \
+  X(emitGlobalInitializers)    \
   X(genCode)                   \
   X(genFrame)                  \
   X(initUnhandled)             \
diff --git a/src/IceTranslator.cpp b/src/IceTranslator.cpp
index 4198442..c05f58d 100644
--- a/src/IceTranslator.cpp
+++ b/src/IceTranslator.cpp
@@ -23,8 +23,7 @@
 
 Translator::Translator(GlobalContext *Ctx, const ClFlags &Flags)
     : Ctx(Ctx), Flags(Flags),
-      GlobalLowering(TargetGlobalLowering::createLowering(Ctx)), ErrorStatus() {
-}
+      DataLowering(TargetDataLowering::createLowering(Ctx)), ErrorStatus() {}
 
 Translator::~Translator() {}
 
@@ -63,7 +62,7 @@
 
 void Translator::emitConstants() {
   if (!getErrorStatus())
-    GlobalLowering->lowerConstants(Ctx);
+    DataLowering->lowerConstants(Ctx);
 }
 
 void Translator::transferErrorCode() const {
@@ -71,19 +70,33 @@
     Ctx->getErrorStatus()->assign(getErrorStatus().value());
 }
 
-void Translator::lowerGlobals(
-    const VariableDeclarationListType &VariableDeclarations) {
+void
+Translator::lowerGlobals(const VariableDeclarationList &VariableDeclarations) {
+  TimerMarker T(TimerStack::TT_emitGlobalInitializers, Ctx);
   bool DisableTranslation = Ctx->getFlags().DisableTranslation;
   const bool DumpGlobalVariables =
       ALLOW_DUMP && Ctx->getVerbose() && Ctx->getFlags().VerboseFocusOn.empty();
-  OstreamLocker L(Ctx);
-  Ostream &Stream = Ctx->getStrDump();
-  const IceString &TranslateOnly = Ctx->getFlags().TranslateOnly;
-  for (const Ice::VariableDeclaration *Global : VariableDeclarations) {
-    if (DumpGlobalVariables)
-      Global->dump(getContext(), Stream);
-    if (!DisableTranslation &&
-        GlobalContext::matchSymbolName(Global->getName(), TranslateOnly))
-      GlobalLowering->lowerInit(*Global);
+  if (Ctx->getFlags().UseELFWriter) {
+    // Dump all globals if requested, but don't interleave w/ emission.
+    if (DumpGlobalVariables) {
+      OstreamLocker L(Ctx);
+      Ostream &Stream = Ctx->getStrDump();
+      for (const Ice::VariableDeclaration *Global : VariableDeclarations) {
+        Global->dump(getContext(), Stream);
+      }
+    }
+    DataLowering->lowerGlobalsELF(VariableDeclarations);
+  } else {
+    const IceString &TranslateOnly = Ctx->getFlags().TranslateOnly;
+    OstreamLocker L(Ctx);
+    Ostream &Stream = Ctx->getStrDump();
+    for (const Ice::VariableDeclaration *Global : VariableDeclarations) {
+      // Interleave dump output w/ emit output.
+      if (DumpGlobalVariables)
+        Global->dump(getContext(), Stream);
+      if (!DisableTranslation &&
+          GlobalContext::matchSymbolName(Global->getName(), TranslateOnly))
+        DataLowering->lowerGlobal(*Global);
+    }
   }
 }
diff --git a/src/IceTranslator.h b/src/IceTranslator.h
index 630997b..cd5a8d6 100644
--- a/src/IceTranslator.h
+++ b/src/IceTranslator.h
@@ -34,9 +34,8 @@
   Translator &operator=(const Translator &) = delete;
 
 public:
-  typedef std::vector<VariableDeclaration *> VariableDeclarationListType;
-
   Translator(GlobalContext *Ctx, const ClFlags &Flags);
+
   ~Translator();
   const ErrorCode &getErrorStatus() const { return ErrorStatus; }
 
@@ -57,7 +56,7 @@
 
   /// Lowers the given list of global addresses to target. Generates
   /// list of corresponding variable declarations.
-  void lowerGlobals(const VariableDeclarationListType &VariableDeclarations);
+  void lowerGlobals(const VariableDeclarationList &VariableDeclarations);
 
   /// Creates a name using the given prefix and corresponding index.
   std::string createUnnamedName(const IceString &Prefix, SizeT Index);
@@ -71,7 +70,7 @@
 protected:
   GlobalContext *Ctx;
   const ClFlags &Flags;
-  std::unique_ptr<TargetGlobalLowering> GlobalLowering;
+  std::unique_ptr<TargetDataLowering> DataLowering;
   // Exit status of the translation. False is successful. True otherwise.
   ErrorCode ErrorStatus;
 };
diff --git a/src/IceUtils.h b/src/IceUtils.h
index 35dd7f7..dc3a5ff 100644
--- a/src/IceUtils.h
+++ b/src/IceUtils.h
@@ -53,6 +53,14 @@
     return ((X > 0 && Y > 0 && (X > std::numeric_limits<T>::max() - Y)) ||
             (X < 0 && Y < 0 && (X < std::numeric_limits<T>::min() - Y)));
   }
+
+  static inline uint64_t OffsetToAlignment(uint64_t Pos, uint64_t Align) {
+    assert(llvm::isPowerOf2_64(Align));
+    uint64_t Mod = Pos & (Align - 1);
+    if (Mod == 0)
+      return 0;
+    return Align - Mod;
+  }
 };
 
 // BoundedProducerConsumerQueue is a work queue that allows multiple
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index 80dccb1..893f912 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -351,7 +351,7 @@
   }
 
   /// Returns the list of parsed global variable declarations.
-  const Ice::Translator::VariableDeclarationListType &getGlobalVariables() {
+  const Ice::VariableDeclarationList &getGlobalVariables() {
     return VariableDeclarations;
   }
 
@@ -376,7 +376,7 @@
   // actually-defined function.
   size_t NextDefiningFunctionID;
   // The set of global variables.
-  Ice::Translator::VariableDeclarationListType VariableDeclarations;
+  Ice::VariableDeclarationList VariableDeclarations;
   // Relocatable constants associated with global declarations.
   std::vector<Ice::Constant *> ValueIDConstants;
   // Error recovery value to use when getFuncSigTypeByID fails.
diff --git a/src/assembler_ia32.cpp b/src/assembler_ia32.cpp
index 88ad7ef..d4fff33 100644
--- a/src/assembler_ia32.cpp
+++ b/src/assembler_ia32.cpp
@@ -43,19 +43,13 @@
 }
 
 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;
+  SizeT BytesNeeded = Utils::OffsetToAlignment(buffer_.GetPosition(), Align);
   const SizeT HltSize = 1;
   while (BytesNeeded > 0) {
     hlt();
     BytesNeeded -= HltSize;
   }
-  assert((buffer_.GetPosition() & (Align - 1)) == 0);
 }
 
 Label *AssemblerX86::GetOrCreateLabel(SizeT Number, LabelVector &Labels) {