Merge "Refactor ElfWriter and introduce ElfWriterQuick" into dalvik-dev
diff --git a/build/Android.libart-compiler.mk b/build/Android.libart-compiler.mk
index 5e34573..07fc14f 100644
--- a/build/Android.libart-compiler.mk
+++ b/build/Android.libart-compiler.mk
@@ -71,7 +71,11 @@
 	src/compiler/llvm/runtime_support_builder_thumb2.cc \
 	src/compiler/llvm/runtime_support_builder_x86.cc \
 	src/compiler/llvm/runtime_support_llvm.cc \
-	src/elf_writer.cc
+	src/elf_fixup.cc \
+	src/elf_stripper.cc \
+	src/elf_writer.cc \
+	src/elf_writer_mclinker.cc \
+	src/elf_writer_quick.cc
 
 LIBART_COMPILER_CFLAGS :=
 ifeq ($(ART_USE_PORTABLE_COMPILER),true)
diff --git a/src/compiler/dex/write_elf.cc b/src/compiler/dex/write_elf.cc
index acec531..d9e45a9 100644
--- a/src/compiler/dex/write_elf.cc
+++ b/src/compiler/dex/write_elf.cc
@@ -14,9 +14,16 @@
  * limitations under the License.
  */
 
-#include "elf_writer.h"
+#include "elf_fixup.h"
+#include "elf_stripper.h"
 #include "os.h"
 
+#if defined(ART_USE_PORTABLE_COMPILER)
+#include "elf_writer_mclinker.h"
+#else
+#include "elf_writer_quick.h"
+#endif
+
 namespace art {
 class CompilerDriver;
 class DexFile;
@@ -29,10 +36,14 @@
                          std::vector<uint8_t>& oat_contents,
                          art::File* file)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return art::ElfWriter::Create(file, oat_contents, dex_files, android_root, is_host, driver);
+#if defined(ART_USE_PORTABLE_COMPILER)
+  return art::ElfWriterMclinker::Create(file, oat_contents, dex_files, android_root, is_host, driver);
+#else
+  return art::ElfWriterQuick::Create(file, oat_contents, dex_files, android_root, is_host, driver);
+#endif
 }
 extern "C" bool FixupElf(art::File* file, uintptr_t oat_data_begin) {
-  return art::ElfWriter::Fixup(file, oat_data_begin);
+  return art::ElfFixup::Fixup(file, oat_data_begin);
 }
 extern "C" void GetOatElfInformation(art::File* file,
                                      size_t& oat_loaded_size,
@@ -40,5 +51,5 @@
   art::ElfWriter::GetOatElfInformation(file, oat_loaded_size, oat_data_offset);
 }
 extern "C" bool StripElf(art::File* file) {
-  return art::ElfWriter::Strip(file);
+  return art::ElfStripper::Strip(file);
 }
diff --git a/src/elf_file.cc b/src/elf_file.cc
index 6f9d53c..cb4ef81 100644
--- a/src/elf_file.cc
+++ b/src/elf_file.cc
@@ -616,24 +616,28 @@
     // which either was specified in the file because we already set
     // base_address_ after the first zero segment).
     if (program_header.p_vaddr == 0) {
-        std::string reservation_name("ElfFile reservation for ");
-        reservation_name += file_->GetPath();
-        UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
-                                                       NULL, GetLoadedSize(), PROT_NONE));
-        CHECK(reserve.get() != NULL) << file_->GetPath();
-        base_address_ = reserve->Begin();
-        segments_.push_back(reserve.release());
+      std::string reservation_name("ElfFile reservation for ");
+      reservation_name += file_->GetPath();
+      UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
+                                                     NULL, GetLoadedSize(), PROT_NONE));
+      CHECK(reserve.get() != NULL) << file_->GetPath();
+      base_address_ = reserve->Begin();
+      segments_.push_back(reserve.release());
+    }
+    // empty segment, nothing to map
+    if (program_header.p_memsz == 0) {
+      continue;
     }
     byte* p_vaddr = base_address_ + program_header.p_vaddr;
     int prot = 0;
     if ((program_header.p_flags & llvm::ELF::PF_X) != 0) {
-        prot |= PROT_EXEC;
+      prot |= PROT_EXEC;
     }
     if ((program_header.p_flags & llvm::ELF::PF_W) != 0) {
-        prot |= PROT_WRITE;
+      prot |= PROT_WRITE;
     }
     if ((program_header.p_flags & llvm::ELF::PF_R) != 0) {
-        prot |= PROT_READ;
+      prot |= PROT_READ;
     }
     int flags = MAP_FIXED;
     if (writable_) {
diff --git a/src/elf_fixup.cc b/src/elf_fixup.cc
new file mode 100644
index 0000000..127bc85
--- /dev/null
+++ b/src/elf_fixup.cc
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "elf_fixup.h"
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "elf_file.h"
+#include "elf_writer.h"
+#include "UniquePtr.h"
+
+namespace art {
+
+static const bool DEBUG_FIXUP = false;
+
+bool ElfFixup::Fixup(File* file, uintptr_t oat_data_begin) {
+  UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
+  CHECK(elf_file.get() != NULL);
+
+  // Lookup "oatdata" symbol address.
+  ::llvm::ELF::Elf32_Addr oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
+  ::llvm::ELF::Elf32_Off base_address = oat_data_begin - oatdata_address;
+
+  if (!FixupDynamic(*elf_file.get(), base_address)) {
+      LOG(WARNING) << "Failed fo fixup .dynamic in " << file->GetPath();
+      return false;
+  }
+  if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
+      LOG(WARNING) << "Failed fo fixup section headers in " << file->GetPath();
+      return false;
+  }
+  if (!FixupProgramHeaders(*elf_file.get(), base_address)) {
+      LOG(WARNING) << "Failed fo fixup program headers in " << file->GetPath();
+      return false;
+  }
+  if (!FixupSymbols(*elf_file.get(), base_address, true)) {
+      LOG(WARNING) << "Failed fo fixup .dynsym in " << file->GetPath();
+      return false;
+  }
+  if (!FixupSymbols(*elf_file.get(), base_address, false)) {
+      LOG(WARNING) << "Failed fo fixup .symtab in " << file->GetPath();
+      return false;
+  }
+  if (!FixupRelocations(*elf_file.get(), base_address)) {
+      LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath();
+      return false;
+  }
+  return true;
+}
+
+// MIPS seems to break the rules d_val vs d_ptr even though their values are between DT_LOPROC and DT_HIPROC
+#define DT_MIPS_RLD_VERSION  0x70000001 // d_val
+#define DT_MIPS_TIME_STAMP   0x70000002 // d_val
+#define DT_MIPS_ICHECKSUM    0x70000003 // d_val
+#define DT_MIPS_IVERSION     0x70000004 // d_val
+#define DT_MIPS_FLAGS        0x70000005 // d_val
+#define DT_MIPS_BASE_ADDRESS 0x70000006 // d_ptr
+#define DT_MIPS_CONFLICT     0x70000008 // d_ptr
+#define DT_MIPS_LIBLIST      0x70000009 // d_ptr
+#define DT_MIPS_LOCAL_GOTNO  0x7000000A // d_val
+#define DT_MIPS_CONFLICTNO   0x7000000B // d_val
+#define DT_MIPS_LIBLISTNO    0x70000010 // d_val
+#define DT_MIPS_SYMTABNO     0x70000011 // d_val
+#define DT_MIPS_UNREFEXTNO   0x70000012 // d_val
+#define DT_MIPS_GOTSYM       0x70000013 // d_val
+#define DT_MIPS_HIPAGENO     0x70000014 // d_val
+#define DT_MIPS_RLD_MAP      0x70000016 // d_ptr
+
+bool ElfFixup::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) {
+  // TODO: C++0x auto.
+  for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetDynamicNum(); i++) {
+    ::llvm::ELF::Elf32_Dyn& elf_dyn = elf_file.GetDynamic(i);
+    ::llvm::ELF::Elf32_Word d_tag = elf_dyn.d_tag;
+    bool elf_dyn_needs_fixup = false;
+    switch (d_tag) {
+      // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr
+      case ::llvm::ELF::DT_PLTGOT:
+      case ::llvm::ELF::DT_HASH:
+      case ::llvm::ELF::DT_STRTAB:
+      case ::llvm::ELF::DT_SYMTAB:
+      case ::llvm::ELF::DT_RELA:
+      case ::llvm::ELF::DT_INIT:
+      case ::llvm::ELF::DT_FINI:
+      case ::llvm::ELF::DT_REL:
+      case ::llvm::ELF::DT_DEBUG:
+      case ::llvm::ELF::DT_JMPREL: {
+        elf_dyn_needs_fixup = true;
+        break;
+      }
+      // d_val or ignored values
+      case ::llvm::ELF::DT_NULL:
+      case ::llvm::ELF::DT_NEEDED:
+      case ::llvm::ELF::DT_PLTRELSZ:
+      case ::llvm::ELF::DT_RELASZ:
+      case ::llvm::ELF::DT_RELAENT:
+      case ::llvm::ELF::DT_STRSZ:
+      case ::llvm::ELF::DT_SYMENT:
+      case ::llvm::ELF::DT_SONAME:
+      case ::llvm::ELF::DT_RPATH:
+      case ::llvm::ELF::DT_SYMBOLIC:
+      case ::llvm::ELF::DT_RELSZ:
+      case ::llvm::ELF::DT_RELENT:
+      case ::llvm::ELF::DT_PLTREL:
+      case ::llvm::ELF::DT_TEXTREL:
+      case ::llvm::ELF::DT_BIND_NOW:
+      case ::llvm::ELF::DT_INIT_ARRAYSZ:
+      case ::llvm::ELF::DT_FINI_ARRAYSZ:
+      case ::llvm::ELF::DT_RUNPATH:
+      case ::llvm::ELF::DT_FLAGS: {
+        break;
+      }
+      // boundary values that should not be used
+      case ::llvm::ELF::DT_ENCODING:
+      case ::llvm::ELF::DT_LOOS:
+      case ::llvm::ELF::DT_HIOS:
+      case ::llvm::ELF::DT_LOPROC:
+      case ::llvm::ELF::DT_HIPROC: {
+        LOG(FATAL) << "Illegal d_tag value 0x" << std::hex << d_tag;
+        break;
+      }
+      default: {
+        // case 2: "regular" DT_* ranges where even d_tag values imply an address in d_ptr
+        if ((::llvm::ELF::DT_ENCODING  < d_tag && d_tag < ::llvm::ELF::DT_LOOS)
+            || (::llvm::ELF::DT_LOOS   < d_tag && d_tag < ::llvm::ELF::DT_HIOS)
+            || (::llvm::ELF::DT_LOPROC < d_tag && d_tag < ::llvm::ELF::DT_HIPROC)) {
+          // Special case for MIPS which breaks the regular rules between DT_LOPROC and DT_HIPROC
+          if (elf_file.GetHeader().e_machine == ::llvm::ELF::EM_MIPS) {
+            switch (d_tag) {
+              case DT_MIPS_RLD_VERSION:
+              case DT_MIPS_TIME_STAMP:
+              case DT_MIPS_ICHECKSUM:
+              case DT_MIPS_IVERSION:
+              case DT_MIPS_FLAGS:
+              case DT_MIPS_LOCAL_GOTNO:
+              case DT_MIPS_CONFLICTNO:
+              case DT_MIPS_LIBLISTNO:
+              case DT_MIPS_SYMTABNO:
+              case DT_MIPS_UNREFEXTNO:
+              case DT_MIPS_GOTSYM:
+              case DT_MIPS_HIPAGENO: {
+                break;
+              }
+              case DT_MIPS_BASE_ADDRESS:
+              case DT_MIPS_CONFLICT:
+              case DT_MIPS_LIBLIST:
+              case DT_MIPS_RLD_MAP: {
+                elf_dyn_needs_fixup = true;
+                break;
+              }
+              default: {
+                LOG(FATAL) << "Unknown MIPS d_tag value 0x" << std::hex << d_tag;
+                break;
+              }
+            }
+          } else if ((elf_dyn.d_tag % 2) == 0) {
+            elf_dyn_needs_fixup = true;
+          }
+        } else {
+          LOG(FATAL) << "Unknown d_tag value 0x" << std::hex << d_tag;
+        }
+        break;
+      }
+    }
+    if (elf_dyn_needs_fixup) {
+      uint32_t d_ptr = elf_dyn.d_un.d_ptr;
+      if (DEBUG_FIXUP) {
+        LOG(INFO) << StringPrintf("In %s moving Elf32_Dyn[%d] from 0x%08x to 0x%08x",
+                                  elf_file.GetFile().GetPath().c_str(), i,
+                                  d_ptr, d_ptr + base_address);
+      }
+      d_ptr += base_address;
+      elf_dyn.d_un.d_ptr = d_ptr;
+    }
+  }
+  return true;
+}
+
+bool ElfFixup::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) {
+  for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
+    ::llvm::ELF::Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
+    // 0 implies that the section will not exist in the memory of the process
+    if (sh.sh_addr == 0) {
+      continue;
+    }
+    if (DEBUG_FIXUP) {
+      LOG(INFO) << StringPrintf("In %s moving Elf32_Shdr[%d] from 0x%08x to 0x%08x",
+                                elf_file.GetFile().GetPath().c_str(), i,
+                                sh.sh_addr, sh.sh_addr + base_address);
+    }
+    sh.sh_addr += base_address;
+  }
+  return true;
+}
+
+bool ElfFixup::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) {
+  // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now.
+  for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) {
+    ::llvm::ELF::Elf32_Phdr& ph = elf_file.GetProgramHeader(i);
+    CHECK_EQ(ph.p_vaddr, ph.p_paddr) << elf_file.GetFile().GetPath() << " i=" << i;
+    CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))))
+            << elf_file.GetFile().GetPath() << " i=" << i;
+    if (DEBUG_FIXUP) {
+      LOG(INFO) << StringPrintf("In %s moving Elf32_Phdr[%d] from 0x%08x to 0x%08x",
+                                elf_file.GetFile().GetPath().c_str(), i,
+                                ph.p_vaddr, ph.p_vaddr + base_address);
+    }
+    ph.p_vaddr += base_address;
+    ph.p_paddr += base_address;
+    CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))))
+            << elf_file.GetFile().GetPath() << " i=" << i;
+  }
+  return true;
+}
+
+bool ElfFixup::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic) {
+  ::llvm::ELF::Elf32_Word section_type = dynamic ? ::llvm::ELF::SHT_DYNSYM : ::llvm::ELF::SHT_SYMTAB;
+  // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
+  ::llvm::ELF::Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type);
+  if (symbol_section == NULL) {
+    // file is missing optional .symtab
+    CHECK(!dynamic) << elf_file.GetFile().GetPath();
+    return true;
+  }
+  for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) {
+    ::llvm::ELF::Elf32_Sym& symbol = elf_file.GetSymbol(section_type, i);
+    if (symbol.st_value != 0) {
+      if (DEBUG_FIXUP) {
+        LOG(INFO) << StringPrintf("In %s moving Elf32_Sym[%d] from 0x%08x to 0x%08x",
+                                  elf_file.GetFile().GetPath().c_str(), i,
+                                  symbol.st_value, symbol.st_value + base_address);
+      }
+      symbol.st_value += base_address;
+    }
+  }
+  return true;
+}
+
+bool ElfFixup::FixupRelocations(ElfFile& elf_file, uintptr_t base_address) {
+  for (llvm::ELF::Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
+    llvm::ELF::Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
+    if (sh.sh_type == llvm::ELF::SHT_REL) {
+      for (uint32_t i = 0; i < elf_file.GetRelNum(sh); i++) {
+        llvm::ELF::Elf32_Rel& rel = elf_file.GetRel(sh, i);
+        if (DEBUG_FIXUP) {
+          LOG(INFO) << StringPrintf("In %s moving Elf32_Rel[%d] from 0x%08x to 0x%08x",
+                                    elf_file.GetFile().GetPath().c_str(), i,
+                                    rel.r_offset, rel.r_offset + base_address);
+        }
+        rel.r_offset += base_address;
+      }
+    } else if (sh.sh_type == llvm::ELF::SHT_RELA) {
+      for (uint32_t i = 0; i < elf_file.GetRelaNum(sh); i++) {
+        llvm::ELF::Elf32_Rela& rela = elf_file.GetRela(sh, i);
+        if (DEBUG_FIXUP) {
+          LOG(INFO) << StringPrintf("In %s moving Elf32_Rela[%d] from 0x%08x to 0x%08x",
+                                    elf_file.GetFile().GetPath().c_str(), i,
+                                    rela.r_offset, rela.r_offset + base_address);
+        }
+        rela.r_offset += base_address;
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace art
diff --git a/src/elf_fixup.h b/src/elf_fixup.h
new file mode 100644
index 0000000..79c45c1
--- /dev/null
+++ b/src/elf_fixup.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_ELF_FIXUP_H_
+#define ART_SRC_ELF_FIXUP_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "os.h"
+
+namespace art {
+
+class ElfFile;
+
+class ElfFixup {
+ public:
+  // Fixup an ELF file so that that oat header will be loaded at oat_begin.
+  // Returns true on success, false on failure.
+  static bool Fixup(File* file, uintptr_t oat_data_begin);
+
+ private:
+  // Fixup .dynamic d_ptr values for the expected base_address.
+  static bool FixupDynamic(ElfFile& elf_file, uintptr_t base_address);
+
+  // Fixup Elf32_Shdr p_vaddr to load at the desired address.
+  static bool FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address);
+
+  // Fixup Elf32_Phdr p_vaddr to load at the desired address.
+  static bool FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address);
+
+  // Fixup symbol table
+  static bool FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic);
+
+  // Fixup dynamic relocations
+  static bool FixupRelocations(ElfFile& elf_file, uintptr_t base_address);
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfFixup);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_ELF_FIXUP_H_
diff --git a/src/elf_stripper.cc b/src/elf_stripper.cc
new file mode 100644
index 0000000..7fc662c
--- /dev/null
+++ b/src/elf_stripper.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "elf_stripper.h"
+
+#include <vector>
+
+#include <llvm/Support/ELF.h>
+
+#include "UniquePtr.h"
+#include "base/logging.h"
+#include "elf_file.h"
+#include "utils.h"
+
+namespace art {
+
+bool ElfStripper::Strip(File* file) {
+  UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
+  CHECK(elf_file.get() != NULL);
+
+  // ELF files produced by MCLinker look roughly like this
+  //
+  // +------------+
+  // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first
+  // +------------+
+  // | Elf32_Phdr | program headers
+  // | Elf32_Phdr |
+  // | ...        |
+  // | Elf32_Phdr |
+  // +------------+
+  // | section    | mixture of needed and unneeded sections
+  // +------------+
+  // | section    |
+  // +------------+
+  // | ...        |
+  // +------------+
+  // | section    |
+  // +------------+
+  // | Elf32_Shdr | section headers
+  // | Elf32_Shdr |
+  // | ...        | contains offset to section start
+  // | Elf32_Shdr |
+  // +------------+
+  //
+  // To strip:
+  // - leave the Elf32_Ehdr and Elf32_Phdr values in place.
+  // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep
+  // - move the sections are keeping up to fill in gaps of sections we want to strip
+  // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr
+  // - truncate rest of file
+  //
+
+  std::vector<llvm::ELF::Elf32_Shdr> section_headers;
+  std::vector<llvm::ELF::Elf32_Word> section_headers_original_indexes;
+  section_headers.reserve(elf_file->GetSectionHeaderNum());
+
+
+  llvm::ELF::Elf32_Shdr& string_section = elf_file->GetSectionNameStringSection();
+  for (llvm::ELF::Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) {
+    llvm::ELF::Elf32_Shdr& sh = elf_file->GetSectionHeader(i);
+    const char* name = elf_file->GetString(string_section, sh.sh_name);
+    if (name == NULL) {
+      CHECK_EQ(0U, i);
+      section_headers.push_back(sh);
+      section_headers_original_indexes.push_back(0);
+      continue;
+    }
+    if (StartsWith(name, ".debug")
+        || (strcmp(name, ".strtab") == 0)
+        || (strcmp(name, ".symtab") == 0)) {
+      continue;
+    }
+    section_headers.push_back(sh);
+    section_headers_original_indexes.push_back(i);
+  }
+  CHECK_NE(0U, section_headers.size());
+  CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
+
+  // section 0 is the NULL section, sections start at offset of first section
+  llvm::ELF::Elf32_Off offset = elf_file->GetSectionHeader(1).sh_offset;
+  for (size_t i = 1; i < section_headers.size(); i++) {
+    llvm::ELF::Elf32_Shdr& new_sh = section_headers[i];
+    llvm::ELF::Elf32_Shdr& old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]);
+    CHECK_EQ(new_sh.sh_name, old_sh.sh_name);
+    if (old_sh.sh_addralign > 1) {
+      offset = RoundUp(offset, old_sh.sh_addralign);
+    }
+    if (old_sh.sh_offset == offset) {
+      // already in place
+      offset += old_sh.sh_size;
+      continue;
+    }
+    // shift section earlier
+    memmove(elf_file->Begin() + offset,
+            elf_file->Begin() + old_sh.sh_offset,
+            old_sh.sh_size);
+    new_sh.sh_offset = offset;
+    offset += old_sh.sh_size;
+  }
+
+  llvm::ELF::Elf32_Off shoff = offset;
+  size_t section_headers_size_in_bytes = section_headers.size() * sizeof(llvm::ELF::Elf32_Shdr);
+  memcpy(elf_file->Begin() + offset, &section_headers[0], section_headers_size_in_bytes);
+  offset += section_headers_size_in_bytes;
+
+  elf_file->GetHeader().e_shnum = section_headers.size();
+  elf_file->GetHeader().e_shoff = shoff;
+  int result = ftruncate(file->Fd(), offset);
+  if (result != 0) {
+    PLOG(ERROR) << "Failed to truncate while stripping ELF file: " << file->GetPath();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace art
diff --git a/src/elf_stripper.h b/src/elf_stripper.h
new file mode 100644
index 0000000..b202e6e
--- /dev/null
+++ b/src/elf_stripper.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_ELF_STRIPPER_H_
+#define ART_SRC_ELF_STRIPPER_H_
+
+#include "base/macros.h"
+#include "os.h"
+
+namespace art {
+
+class ElfStripper {
+ public:
+  // Strip an ELF file of unneeded debugging information.
+  // Returns true on success, false on failure.
+  static bool Strip(File* file);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_ELF_STRIPPER_H_
diff --git a/src/elf_writer.cc b/src/elf_writer.cc
index 7647da9..7592eb5 100644
--- a/src/elf_writer.cc
+++ b/src/elf_writer.cc
@@ -16,17 +16,6 @@
 
 #include "elf_writer.h"
 
-#include <llvm/Support/TargetSelect.h>
-
-#include <mcld/Environment.h>
-#include <mcld/IRBuilder.h>
-#include <mcld/Linker.h>
-#include <mcld/LinkerConfig.h>
-#include <mcld/MC/ZOption.h>
-#include <mcld/Module.h>
-#include <mcld/Support/Path.h>
-#include <mcld/Support/TargetSelect.h>
-
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "compiler/driver/compiler_driver.h"
@@ -43,301 +32,12 @@
 
 namespace art {
 
-bool ElfWriter::Create(File* elf_file,
-                       std::vector<uint8_t>& oat_contents,
-                       const std::vector<const DexFile*>& dex_files,
-                       const std::string& android_root,
-                       bool is_host,
-                       const CompilerDriver& driver) {
-  ElfWriter elf_writer(driver, elf_file);
-  return elf_writer.Write(oat_contents, dex_files, android_root, is_host);
-}
-
 ElfWriter::ElfWriter(const CompilerDriver& driver, File* elf_file)
-  : compiler_driver_(&driver), elf_file_(elf_file), oat_input_(NULL) {}
+  : compiler_driver_(&driver), elf_file_(elf_file) {}
 
 ElfWriter::~ElfWriter() {}
 
-bool ElfWriter::Write(std::vector<uint8_t>& oat_contents,
-                      const std::vector<const DexFile*>& dex_files,
-                      const std::string& android_root,
-                      bool is_host) {
-  Init();
-  AddOatInput(oat_contents);
-#if defined(ART_USE_PORTABLE_COMPILER)
-  AddMethodInputs(dex_files);
-  AddRuntimeInputs(android_root, is_host);
-#endif
-  if (!Link()) {
-    return false;
-  }
-#if defined(ART_USE_PORTABLE_COMPILER)
-  FixupOatMethodOffsets(dex_files);
-#endif
-  return true;
-}
-
-static void InitializeLLVM() {
-  // TODO: this is lifted from art's compiler_llvm.cc, should be factored out
-  if (kIsTargetBuild) {
-    llvm::InitializeNativeTarget();
-    // TODO: odd that there is no InitializeNativeTargetMC?
-  } else {
-    llvm::InitializeAllTargets();
-    llvm::InitializeAllTargetMCs();
-  }
-}
-
-void ElfWriter::Init() {
-  std::string target_triple;
-  std::string target_cpu;
-  std::string target_attr;
-  CompilerDriver::InstructionSetToLLVMTarget(compiler_driver_->GetInstructionSet(),
-                                       target_triple,
-                                       target_cpu,
-                                       target_attr);
-
-  // Based on mclinker's llvm-mcld.cpp main() and LinkerTest
-  //
-  // TODO: LinkerTest uses mcld::Initialize(), but it does an
-  // llvm::InitializeAllTargets, which we don't want. Basically we
-  // want mcld::InitializeNative, but it doesn't exist yet, so we
-  // inline the minimal we need here.
-  InitializeLLVM();
-  mcld::InitializeAllTargets();
-  mcld::InitializeAllLinkers();
-  mcld::InitializeAllEmulations();
-  mcld::InitializeAllDiagnostics();
-
-  linker_config_.reset(new mcld::LinkerConfig(target_triple));
-  CHECK(linker_config_.get() != NULL);
-  linker_config_->setCodeGenType(mcld::LinkerConfig::DynObj);
-  linker_config_->options().setSOName(elf_file_->GetPath());
-
-  // error on undefined symbols.
-  // TODO: should this just be set if kIsDebugBuild?
-  linker_config_->options().setNoUndefined(true);
-
-  if (compiler_driver_->GetInstructionSet() == kMips) {
-     // MCLinker defaults MIPS section alignment to 0x10000, not
-     // 0x1000.  The ABI says this is because the max page size is
-     // general is 64k but that isn't true on Android.
-     mcld::ZOption z_option;
-     z_option.setKind(mcld::ZOption::MaxPageSize);
-     z_option.setPageSize(kPageSize);
-     linker_config_->options().addZOption(z_option);
-  }
-
-  // TODO: Wire up mcld DiagnosticEngine to LOG?
-  linker_config_->options().setColor(false);
-  if (false) {
-    // enables some tracing of input file processing
-    linker_config_->options().setTrace(true);
-  }
-
-  // Based on alone::Linker::config
-  module_.reset(new mcld::Module(linker_config_->options().soname()));
-  CHECK(module_.get() != NULL);
-  ir_builder_.reset(new mcld::IRBuilder(*module_.get(), *linker_config_.get()));
-  CHECK(ir_builder_.get() != NULL);
-  linker_.reset(new mcld::Linker());
-  CHECK(linker_.get() != NULL);
-  linker_->config(*linker_config_.get());
-}
-
-void ElfWriter::AddOatInput(std::vector<uint8_t>& oat_contents) {
-  // Add an artificial memory input. Based on LinkerTest.
-  UniquePtr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath()));
-  CHECK(oat_file.get() != NULL) << elf_file_->GetPath();
-
-  const char* oat_data_start = reinterpret_cast<const char*>(&oat_file->GetOatHeader());
-  const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset();
-  const char* oat_code_start = oat_data_start + oat_data_length;
-  const size_t oat_code_length = oat_file->Size() - oat_data_length;
-
-  // TODO: ownership of oat_input?
-  oat_input_ = ir_builder_->CreateInput("oat contents",
-                                        mcld::sys::fs::Path("oat contents path"),
-                                        mcld::Input::Object);
-  CHECK(oat_input_ != NULL);
-
-  // TODO: ownership of null_section?
-  mcld::LDSection* null_section = ir_builder_->CreateELFHeader(*oat_input_,
-                                                               "",
-                                                               mcld::LDFileFormat::Null,
-                                                               llvm::ELF::SHT_NULL,
-                                                               0);
-  CHECK(null_section != NULL);
-
-  // TODO: we should split readonly data from readonly executable
-  // code like .oat does.  We need to control section layout with
-  // linker script like functionality to guarantee references
-  // between sections maintain relative position which isn't
-  // possible right now with the mclinker APIs.
-  CHECK(oat_code_start != NULL);
-
-  // we need to ensure that oatdata is page aligned so when we
-  // fixup the segment load addresses, they remain page aligned.
-  uint32_t alignment = kPageSize;
-
-  // TODO: ownership of text_section?
-  mcld::LDSection* text_section = ir_builder_->CreateELFHeader(*oat_input_,
-                                                               ".text",
-                                                               llvm::ELF::SHT_PROGBITS,
-                                                               llvm::ELF::SHF_EXECINSTR
-                                                               | llvm::ELF::SHF_ALLOC,
-                                                               alignment);
-  CHECK(text_section != NULL);
-
-  mcld::SectionData* text_sectiondata = ir_builder_->CreateSectionData(*text_section);
-  CHECK(text_sectiondata != NULL);
-
-  // TODO: why does IRBuilder::CreateRegion take a non-const pointer?
-  mcld::Fragment* text_fragment = ir_builder_->CreateRegion(const_cast<char*>(oat_data_start),
-                                                            oat_file->Size());
-  CHECK(text_fragment != NULL);
-  ir_builder_->AppendFragment(*text_fragment, *text_sectiondata);
-
-  ir_builder_->AddSymbol(*oat_input_,
-                         "oatdata",
-                         mcld::ResolveInfo::Object,
-                         mcld::ResolveInfo::Define,
-                         mcld::ResolveInfo::Global,
-                         oat_data_length,  // size
-                         0,                // offset
-                         text_section);
-
-  ir_builder_->AddSymbol(*oat_input_,
-                         "oatexec",
-                         mcld::ResolveInfo::Function,
-                         mcld::ResolveInfo::Define,
-                         mcld::ResolveInfo::Global,
-                         oat_code_length,  // size
-                         oat_data_length,  // offset
-                         text_section);
-
-  ir_builder_->AddSymbol(*oat_input_,
-                         "oatlastword",
-                         mcld::ResolveInfo::Object,
-                         mcld::ResolveInfo::Define,
-                         mcld::ResolveInfo::Global,
-                         0,                // size
-                         // subtract a word so symbol is within section
-                         (oat_data_length + oat_code_length) - sizeof(uint32_t),  // offset
-                         text_section);
-}
-
-#if defined(ART_USE_PORTABLE_COMPILER)
-void ElfWriter::AddMethodInputs(const std::vector<const DexFile*>& dex_files) {
-  DCHECK(oat_input_ != NULL);
-
-  DexMethodIterator it(dex_files);
-  while (it.HasNext()) {
-    const DexFile& dex_file = it.GetDexFile();
-    uint32_t method_idx = it.GetMemberIndex();
-    const CompiledMethod* compiled_method =
-      compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx));
-    if (compiled_method != NULL) {
-      AddCompiledCodeInput(*compiled_method);
-    }
-    it.Next();
-  }
-  added_symbols_.clear();
-}
-
-void ElfWriter::AddCompiledCodeInput(const CompiledCode& compiled_code) {
-  // Check if we've seen this compiled code before. If so skip
-  // it. This can happen for reused code such as invoke stubs.
-  const std::string& symbol = compiled_code.GetSymbol();
-  SafeMap<const std::string*, const std::string*>::iterator it = added_symbols_.find(&symbol);
-  if (it != added_symbols_.end()) {
-    return;
-  }
-  added_symbols_.Put(&symbol, &symbol);
-
-  // Add input to supply code for symbol
-  const std::vector<uint8_t>& code = compiled_code.GetCode();
-  // TODO: ownership of code_input?
-  // TODO: why does IRBuilder::ReadInput take a non-const pointer?
-  mcld::Input* code_input = ir_builder_->ReadInput(symbol,
-                                                   const_cast<uint8_t*>(&code[0]),
-                                                   code.size());
-  CHECK(code_input != NULL);
-}
-
-void ElfWriter::AddRuntimeInputs(const std::string& android_root, bool is_host) {
-  std::string libart_so(android_root);
-  libart_so += kIsDebugBuild ? "/lib/libartd.so" : "/lib/libart.so";
-  // TODO: ownership of libart_so_input?
-  mcld::Input* libart_so_input = ir_builder_->ReadInput(libart_so, libart_so);
-  CHECK(libart_so_input != NULL);
-
-  std::string host_prebuilt_dir("prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6");
-
-  std::string compiler_runtime_lib;
-  if (is_host) {
-    compiler_runtime_lib += host_prebuilt_dir;
-    compiler_runtime_lib += "/lib/gcc/i686-linux/4.6.x-google/libgcc.a";
-  } else {
-    compiler_runtime_lib += android_root;
-    compiler_runtime_lib += "/lib/libcompiler_rt.a";
-  }
-  // TODO: ownership of compiler_runtime_lib_input?
-  mcld::Input* compiler_runtime_lib_input = ir_builder_->ReadInput(compiler_runtime_lib,
-                                                                   compiler_runtime_lib);
-  CHECK(compiler_runtime_lib_input != NULL);
-
-  std::string libc_lib;
-  if (is_host) {
-    libc_lib += host_prebuilt_dir;
-    libc_lib += "/sysroot/usr/lib/libc.so.6";
-  } else {
-    libc_lib += android_root;
-    libc_lib += "/lib/libc.so";
-  }
-  // TODO: ownership of libc_lib_input?
-  mcld::Input* libc_lib_input_input = ir_builder_->ReadInput(libc_lib, libc_lib);
-  CHECK(libc_lib_input_input != NULL);
-
-  std::string libm_lib;
-  if (is_host) {
-    libm_lib += host_prebuilt_dir;
-    libm_lib += "/sysroot/usr/lib/libm.so";
-  } else {
-    libm_lib += android_root;
-    libm_lib += "/lib/libm.so";
-  }
-  // TODO: ownership of libm_lib_input?
-  mcld::Input* libm_lib_input_input = ir_builder_->ReadInput(libm_lib, libm_lib);
-  CHECK(libm_lib_input_input != NULL);
-
-}
-#endif
-
-bool ElfWriter::Link() {
-  // link inputs
-  if (!linker_->link(*module_.get(), *ir_builder_.get())) {
-    LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
-    return false;
-  }
-
-  // emit linked output
-  // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
-  int fd = dup(elf_file_->Fd());
-  if (fd == -1) {
-    PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
-    return false;
-  }
-  if (!linker_->emit(fd)) {
-    LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
-    return false;
-  }
-  mcld::Finalize();
-  LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
-  return true;
-}
-
-static llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file) {
+llvm::ELF::Elf32_Addr ElfWriter::GetOatDataAddress(ElfFile* elf_file) {
   llvm::ELF::Elf32_Addr oatdata_address = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM,
                                                                       "oatdata",
                                                                       false);
@@ -345,417 +45,6 @@
   return oatdata_address;
 }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
-void ElfWriter::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
-  UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false));
-  CHECK(elf_file.get() != NULL) << elf_file_->GetPath();
-
-  llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get());
-  DexMethodIterator it(dex_files);
-  while (it.HasNext()) {
-    const DexFile& dex_file = it.GetDexFile();
-    uint32_t method_idx = it.GetMemberIndex();
-    InvokeType invoke_type = it.GetInvokeType();
-    mirror::AbstractMethod* method = NULL;
-    if (compiler_driver_->IsImage()) {
-      ClassLinker* linker = Runtime::Current()->GetClassLinker();
-      mirror::DexCache* dex_cache = linker->FindDexCache(dex_file);
-      // Unchecked as we hold mutator_lock_ on entry.
-      ScopedObjectAccessUnchecked soa(Thread::Current());
-      method = linker->ResolveMethod(dex_file, method_idx, dex_cache, NULL, NULL, invoke_type);
-      CHECK(method != NULL);
-    }
-    const CompiledMethod* compiled_method =
-      compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx));
-    if (compiled_method != NULL) {
-      uint32_t offset = FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_method);
-      // Don't overwrite static method trampoline
-      if (method != NULL &&
-          (!method->IsStatic() ||
-           method->IsConstructor() ||
-           method->GetDeclaringClass()->IsInitialized())) {
-        method->SetOatCodeOffset(offset);
-      }
-    }
-    it.Next();
-  }
-  symbol_to_compiled_code_offset_.clear();
-}
-
-uint32_t ElfWriter::FixupCompiledCodeOffset(ElfFile& elf_file,
-                                            llvm::ELF::Elf32_Addr oatdata_address,
-                                            const CompiledCode& compiled_code) {
-  const std::string& symbol = compiled_code.GetSymbol();
-  SafeMap<const std::string*, uint32_t>::iterator it = symbol_to_compiled_code_offset_.find(&symbol);
-  if (it != symbol_to_compiled_code_offset_.end()) {
-    return it->second;
-  }
-
-  llvm::ELF::Elf32_Addr compiled_code_address = elf_file.FindSymbolAddress(llvm::ELF::SHT_SYMTAB,
-                                                                           symbol,
-                                                                           true);
-  CHECK_NE(0U, compiled_code_address) << symbol;
-  CHECK_LT(oatdata_address, compiled_code_address) << symbol;
-  uint32_t compiled_code_offset = compiled_code_address - oatdata_address;
-  symbol_to_compiled_code_offset_.Put(&symbol, compiled_code_offset);
-
-  const std::vector<uint32_t>& offsets = compiled_code.GetOatdataOffsetsToCompliledCodeOffset();
-  for (uint32_t i = 0; i < offsets.size(); i++) {
-    uint32_t oatdata_offset = oatdata_address + offsets[i];
-    uint32_t* addr = reinterpret_cast<uint32_t*>(elf_file.Begin() + oatdata_offset);
-    *addr = compiled_code_offset;
-  }
-  return compiled_code_offset;
-}
-#endif
-
-static const bool DEBUG_FIXUP = false;
-
-bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
-  UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
-  CHECK(elf_file.get() != NULL);
-
-  // Lookup "oatdata" symbol address.
-  ::llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get());
-  ::llvm::ELF::Elf32_Off base_address = oat_data_begin - oatdata_address;
-
-  if (!FixupDynamic(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup .dynamic in " << file->GetPath();
-      return false;
-  }
-  if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup section headers in " << file->GetPath();
-      return false;
-  }
-  if (!FixupProgramHeaders(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup program headers in " << file->GetPath();
-      return false;
-  }
-  if (!FixupSymbols(*elf_file.get(), base_address, true)) {
-      LOG(WARNING) << "Failed fo fixup .dynsym in " << file->GetPath();
-      return false;
-  }
-  if (!FixupSymbols(*elf_file.get(), base_address, false)) {
-      LOG(WARNING) << "Failed fo fixup .symtab in " << file->GetPath();
-      return false;
-  }
-  if (!FixupRelocations(*elf_file.get(), base_address)) {
-      LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath();
-      return false;
-  }
-  return true;
-}
-
-// MIPS seems to break the rules d_val vs d_ptr even though their values are between DT_LOPROC and DT_HIPROC
-#define DT_MIPS_RLD_VERSION  0x70000001 // d_val
-#define DT_MIPS_TIME_STAMP   0x70000002 // d_val
-#define DT_MIPS_ICHECKSUM    0x70000003 // d_val
-#define DT_MIPS_IVERSION     0x70000004 // d_val
-#define DT_MIPS_FLAGS        0x70000005 // d_val
-#define DT_MIPS_BASE_ADDRESS 0x70000006 // d_ptr
-#define DT_MIPS_CONFLICT     0x70000008 // d_ptr
-#define DT_MIPS_LIBLIST      0x70000009 // d_ptr
-#define DT_MIPS_LOCAL_GOTNO  0x7000000A // d_val
-#define DT_MIPS_CONFLICTNO   0x7000000B // d_val
-#define DT_MIPS_LIBLISTNO    0x70000010 // d_val
-#define DT_MIPS_SYMTABNO     0x70000011 // d_val
-#define DT_MIPS_UNREFEXTNO   0x70000012 // d_val
-#define DT_MIPS_GOTSYM       0x70000013 // d_val
-#define DT_MIPS_HIPAGENO     0x70000014 // d_val
-#define DT_MIPS_RLD_MAP      0x70000016 // d_ptr
-
-bool ElfWriter::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) {
-  // TODO: C++0x auto.
-  for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetDynamicNum(); i++) {
-    ::llvm::ELF::Elf32_Dyn& elf_dyn = elf_file.GetDynamic(i);
-    ::llvm::ELF::Elf32_Word d_tag = elf_dyn.d_tag;
-    bool elf_dyn_needs_fixup = false;
-    switch (d_tag) {
-      // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr
-      case ::llvm::ELF::DT_PLTGOT:
-      case ::llvm::ELF::DT_HASH:
-      case ::llvm::ELF::DT_STRTAB:
-      case ::llvm::ELF::DT_SYMTAB:
-      case ::llvm::ELF::DT_RELA:
-      case ::llvm::ELF::DT_INIT:
-      case ::llvm::ELF::DT_FINI:
-      case ::llvm::ELF::DT_REL:
-      case ::llvm::ELF::DT_DEBUG:
-      case ::llvm::ELF::DT_JMPREL: {
-        elf_dyn_needs_fixup = true;
-        break;
-      }
-      // d_val or ignored values
-      case ::llvm::ELF::DT_NULL:
-      case ::llvm::ELF::DT_NEEDED:
-      case ::llvm::ELF::DT_PLTRELSZ:
-      case ::llvm::ELF::DT_RELASZ:
-      case ::llvm::ELF::DT_RELAENT:
-      case ::llvm::ELF::DT_STRSZ:
-      case ::llvm::ELF::DT_SYMENT:
-      case ::llvm::ELF::DT_SONAME:
-      case ::llvm::ELF::DT_RPATH:
-      case ::llvm::ELF::DT_SYMBOLIC:
-      case ::llvm::ELF::DT_RELSZ:
-      case ::llvm::ELF::DT_RELENT:
-      case ::llvm::ELF::DT_PLTREL:
-      case ::llvm::ELF::DT_TEXTREL:
-      case ::llvm::ELF::DT_BIND_NOW:
-      case ::llvm::ELF::DT_INIT_ARRAYSZ:
-      case ::llvm::ELF::DT_FINI_ARRAYSZ:
-      case ::llvm::ELF::DT_RUNPATH:
-      case ::llvm::ELF::DT_FLAGS: {
-        break;
-      }
-      // boundary values that should not be used
-      case ::llvm::ELF::DT_ENCODING:
-      case ::llvm::ELF::DT_LOOS:
-      case ::llvm::ELF::DT_HIOS:
-      case ::llvm::ELF::DT_LOPROC:
-      case ::llvm::ELF::DT_HIPROC: {
-        LOG(FATAL) << "Illegal d_tag value 0x" << std::hex << d_tag;
-        break;
-      }
-      default: {
-        // case 2: "regular" DT_* ranges where even d_tag values imply an address in d_ptr
-        if ((::llvm::ELF::DT_ENCODING  < d_tag && d_tag < ::llvm::ELF::DT_LOOS)
-            || (::llvm::ELF::DT_LOOS   < d_tag && d_tag < ::llvm::ELF::DT_HIOS)
-            || (::llvm::ELF::DT_LOPROC < d_tag && d_tag < ::llvm::ELF::DT_HIPROC)) {
-          // Special case for MIPS which breaks the regular rules between DT_LOPROC and DT_HIPROC
-          if (elf_file.GetHeader().e_machine == ::llvm::ELF::EM_MIPS) {
-            switch (d_tag) {
-              case DT_MIPS_RLD_VERSION:
-              case DT_MIPS_TIME_STAMP:
-              case DT_MIPS_ICHECKSUM:
-              case DT_MIPS_IVERSION:
-              case DT_MIPS_FLAGS:
-              case DT_MIPS_LOCAL_GOTNO:
-              case DT_MIPS_CONFLICTNO:
-              case DT_MIPS_LIBLISTNO:
-              case DT_MIPS_SYMTABNO:
-              case DT_MIPS_UNREFEXTNO:
-              case DT_MIPS_GOTSYM:
-              case DT_MIPS_HIPAGENO: {
-                break;
-              }
-              case DT_MIPS_BASE_ADDRESS:
-              case DT_MIPS_CONFLICT:
-              case DT_MIPS_LIBLIST:
-              case DT_MIPS_RLD_MAP: {
-                elf_dyn_needs_fixup = true;
-                break;
-              }
-              default: {
-                LOG(FATAL) << "Unknown MIPS d_tag value 0x" << std::hex << d_tag;
-                break;
-              }
-            }
-          } else if ((elf_dyn.d_tag % 2) == 0) {
-            elf_dyn_needs_fixup = true;
-          }
-        } else {
-          LOG(FATAL) << "Unknown d_tag value 0x" << std::hex << d_tag;
-        }
-        break;
-      }
-    }
-    if (elf_dyn_needs_fixup) {
-      uint32_t d_ptr = elf_dyn.d_un.d_ptr;
-      if (DEBUG_FIXUP) {
-        LOG(INFO) << StringPrintf("In %s moving Elf32_Dyn[%d] from 0x%08x to 0x%08x",
-                                  elf_file.GetFile().GetPath().c_str(), i,
-                                  d_ptr, d_ptr + base_address);
-      }
-      d_ptr += base_address;
-      elf_dyn.d_un.d_ptr = d_ptr;
-    }
-  }
-  return true;
-}
-
-bool ElfWriter::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) {
-  for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
-    ::llvm::ELF::Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
-    // 0 implies that the section will not exist in the memory of the process
-    if (sh.sh_addr == 0) {
-      continue;
-    }
-    if (DEBUG_FIXUP) {
-      LOG(INFO) << StringPrintf("In %s moving Elf32_Shdr[%d] from 0x%08x to 0x%08x",
-                                elf_file.GetFile().GetPath().c_str(), i,
-                                sh.sh_addr, sh.sh_addr + base_address);
-    }
-    sh.sh_addr += base_address;
-  }
-  return true;
-}
-
-bool ElfWriter::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) {
-  // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now.
-  for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) {
-    ::llvm::ELF::Elf32_Phdr& ph = elf_file.GetProgramHeader(i);
-    CHECK_EQ(ph.p_vaddr, ph.p_paddr) << elf_file.GetFile().GetPath() << " i=" << i;
-    CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))))
-            << elf_file.GetFile().GetPath() << " i=" << i;
-    if (DEBUG_FIXUP) {
-      LOG(INFO) << StringPrintf("In %s moving Elf32_Phdr[%d] from 0x%08x to 0x%08x",
-                                elf_file.GetFile().GetPath().c_str(), i,
-                                ph.p_vaddr, ph.p_vaddr + base_address);
-    }
-    ph.p_vaddr += base_address;
-    ph.p_paddr += base_address;
-    CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))))
-            << elf_file.GetFile().GetPath() << " i=" << i;
-  }
-  return true;
-}
-
-bool ElfWriter::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic) {
-  ::llvm::ELF::Elf32_Word section_type = dynamic ? ::llvm::ELF::SHT_DYNSYM : ::llvm::ELF::SHT_SYMTAB;
-  // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
-  ::llvm::ELF::Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type);
-  CHECK(symbol_section != NULL) << elf_file.GetFile().GetPath();
-  for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) {
-    ::llvm::ELF::Elf32_Sym& symbol = elf_file.GetSymbol(section_type, i);
-    if (symbol.st_value != 0) {
-      if (DEBUG_FIXUP) {
-        LOG(INFO) << StringPrintf("In %s moving Elf32_Sym[%d] from 0x%08x to 0x%08x",
-                                  elf_file.GetFile().GetPath().c_str(), i,
-                                  symbol.st_value, symbol.st_value + base_address);
-      }
-      symbol.st_value += base_address;
-    }
-  }
-  return true;
-}
-
-bool ElfWriter::FixupRelocations(ElfFile& elf_file, uintptr_t base_address) {
-  for (llvm::ELF::Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
-    llvm::ELF::Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
-    if (sh.sh_type == llvm::ELF::SHT_REL) {
-      for (uint32_t i = 0; i < elf_file.GetRelNum(sh); i++) {
-        llvm::ELF::Elf32_Rel& rel = elf_file.GetRel(sh, i);
-        if (DEBUG_FIXUP) {
-          LOG(INFO) << StringPrintf("In %s moving Elf32_Rel[%d] from 0x%08x to 0x%08x",
-                                    elf_file.GetFile().GetPath().c_str(), i,
-                                    rel.r_offset, rel.r_offset + base_address);
-        }
-        rel.r_offset += base_address;
-      }
-    } else if (sh.sh_type == llvm::ELF::SHT_RELA) {
-      for (uint32_t i = 0; i < elf_file.GetRelaNum(sh); i++) {
-        llvm::ELF::Elf32_Rela& rela = elf_file.GetRela(sh, i);
-        if (DEBUG_FIXUP) {
-          LOG(INFO) << StringPrintf("In %s moving Elf32_Rela[%d] from 0x%08x to 0x%08x",
-                                    elf_file.GetFile().GetPath().c_str(), i,
-                                    rela.r_offset, rela.r_offset + base_address);
-        }
-        rela.r_offset += base_address;
-      }
-    }
-  }
-  return true;
-}
-
-bool ElfWriter::Strip(File* file) {
-  UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
-  CHECK(elf_file.get() != NULL);
-
-  // ELF files produced by MCLinker look roughly like this
-  //
-  // +------------+
-  // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first
-  // +------------+
-  // | Elf32_Phdr | program headers
-  // | Elf32_Phdr |
-  // | ...        |
-  // | Elf32_Phdr |
-  // +------------+
-  // | section    | mixture of needed and unneeded sections
-  // +------------+
-  // | section    |
-  // +------------+
-  // | ...        |
-  // +------------+
-  // | section    |
-  // +------------+
-  // | Elf32_Shdr | section headers
-  // | Elf32_Shdr |
-  // | ...        | contains offset to section start
-  // | Elf32_Shdr |
-  // +------------+
-  //
-  // To strip:
-  // - leave the Elf32_Ehdr and Elf32_Phdr values in place.
-  // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep
-  // - move the sections are keeping up to fill in gaps of sections we want to strip
-  // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr
-  // - truncate rest of file
-  //
-
-  std::vector<llvm::ELF::Elf32_Shdr> section_headers;
-  std::vector<llvm::ELF::Elf32_Word> section_headers_original_indexes;
-  section_headers.reserve(elf_file->GetSectionHeaderNum());
-
-
-  llvm::ELF::Elf32_Shdr& string_section = elf_file->GetSectionNameStringSection();
-  for (llvm::ELF::Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) {
-    llvm::ELF::Elf32_Shdr& sh = elf_file->GetSectionHeader(i);
-    const char* name = elf_file->GetString(string_section, sh.sh_name);
-    if (name == NULL) {
-      CHECK_EQ(0U, i);
-      section_headers.push_back(sh);
-      section_headers_original_indexes.push_back(0);
-      continue;
-    }
-    if (StartsWith(name, ".debug")
-        || (strcmp(name, ".strtab") == 0)
-        || (strcmp(name, ".symtab") == 0)) {
-      continue;
-    }
-    section_headers.push_back(sh);
-    section_headers_original_indexes.push_back(i);
-  }
-  CHECK_NE(0U, section_headers.size());
-  CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
-
-  // section 0 is the NULL section, sections start at offset of first section
-  llvm::ELF::Elf32_Off offset = elf_file->GetSectionHeader(1).sh_offset;
-  for (size_t i = 1; i < section_headers.size(); i++) {
-    llvm::ELF::Elf32_Shdr& new_sh = section_headers[i];
-    llvm::ELF::Elf32_Shdr& old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]);
-    CHECK_EQ(new_sh.sh_name, old_sh.sh_name);
-    if (old_sh.sh_addralign > 1) {
-      offset = RoundUp(offset, old_sh.sh_addralign);
-    }
-    if (old_sh.sh_offset == offset) {
-      // already in place
-      offset += old_sh.sh_size;
-      continue;
-    }
-    // shift section earlier
-    memmove(elf_file->Begin() + offset,
-            elf_file->Begin() + old_sh.sh_offset,
-            old_sh.sh_size);
-    new_sh.sh_offset = offset;
-    offset += old_sh.sh_size;
-  }
-
-  llvm::ELF::Elf32_Off shoff = offset;
-  size_t section_headers_size_in_bytes = section_headers.size() * sizeof(llvm::ELF::Elf32_Shdr);
-  memcpy(elf_file->Begin() + offset, &section_headers[0], section_headers_size_in_bytes);
-  offset += section_headers_size_in_bytes;
-
-  elf_file->GetHeader().e_shnum = section_headers.size();
-  elf_file->GetHeader().e_shoff = shoff;
-  int result = ftruncate(file->Fd(), offset);
-  if (result != 0) {
-    PLOG(ERROR) << "Failed to truncate while stripping ELF file: " << file->GetPath();
-    return false;
-  }
-  return true;
-}
-
 void ElfWriter::GetOatElfInformation(File* file,
                                      size_t& oat_loaded_size,
                                      size_t& oat_data_offset) {
diff --git a/src/elf_writer.h b/src/elf_writer.h
index 1dd0131..7392e67 100644
--- a/src/elf_writer.h
+++ b/src/elf_writer.h
@@ -20,116 +20,44 @@
 #include <stdint.h>
 
 #include <cstddef>
+#include <string>
 #include <vector>
 
 #include <llvm/Support/ELF.h>
 
-#include "UniquePtr.h"
-#include "dex_file.h"
+#include "base/macros.h"
 #include "os.h"
 
-namespace mcld {
-class IRBuilder;
-class Input;
-class LDSection;
-class LDSymbol;
-class Linker;
-class LinkerConfig;
-class Module;
-} // namespace mcld
-
 namespace art {
 
-class CompiledCode;
 class CompilerDriver;
+class DexFile;
 class ElfFile;
 
 class ElfWriter {
  public:
-  // Write an ELF file. Returns true on success, false on failure.
-  static bool Create(File* file,
-                     std::vector<uint8_t>& oat_contents,
-                     const std::vector<const DexFile*>& dex_files,
-                     const std::string& android_root,
-                     bool is_host,
-                     const CompilerDriver& driver)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  // Fixup an ELF file so that that oat header will be loaded at oat_begin.
-  // Returns true on success, false on failure.
-  static bool Fixup(File* file, uintptr_t oat_data_begin);
-
-  // Strip an ELF file of unneeded debugging information.
-  // Returns true on success, false on failure.
-  static bool Strip(File* file);
-
   // Looks up information about location of oat file in elf file container.
   // Used for ImageWriter to perform memory layout.
   static void GetOatElfInformation(File* file,
                                    size_t& oat_loaded_size,
                                    size_t& oat_data_offset);
 
- private:
+  // Returns runtime oat_data runtime address for an opened ElfFile.
+  static llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file);
+
+ protected:
   ElfWriter(const CompilerDriver& driver, File* elf_file);
   ~ElfWriter();
 
-  bool Write(std::vector<uint8_t>& oat_contents,
-             const std::vector<const DexFile*>& dex_files,
-             const std::string& android_root,
-             bool is_host)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void Init();
-  void AddOatInput(std::vector<uint8_t>& oat_contents);
-  void AddMethodInputs(const std::vector<const DexFile*>& dex_files);
-  void AddCompiledCodeInput(const CompiledCode& compiled_code);
-  void AddRuntimeInputs(const std::string& android_root, bool is_host);
-  bool Link();
-#if defined(ART_USE_PORTABLE_COMPILER)
-  void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  uint32_t FixupCompiledCodeOffset(ElfFile& elf_file,
-                                   llvm::ELF::Elf32_Addr oatdata_address,
-                                   const CompiledCode& compiled_code);
-#endif
-
-  // Fixup .dynamic d_ptr values for the expected base_address.
-  static bool FixupDynamic(ElfFile& elf_file, uintptr_t base_address);
-
-  // Fixup Elf32_Shdr p_vaddr to load at the desired address.
-  static bool FixupSectionHeaders(ElfFile& elf_file,uintptr_t base_address);
-
-  // Fixup Elf32_Phdr p_vaddr to load at the desired address.
-  static bool FixupProgramHeaders(ElfFile& elf_file,uintptr_t base_address);
-
-  // Fixup symbol table
-  static bool FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic);
-
-  // Fixup dynamic relocations
-  static bool FixupRelocations(ElfFile& elf_file, uintptr_t base_address);
+  virtual bool Write(std::vector<uint8_t>& oat_contents,
+                     const std::vector<const DexFile*>& dex_files,
+                     const std::string& android_root,
+                     bool is_host)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
 
   // Setup by constructor
   const CompilerDriver* compiler_driver_;
   File* elf_file_;
-
-  // Setup by Init()
-  UniquePtr<mcld::LinkerConfig> linker_config_;
-  UniquePtr<mcld::Module> module_;
-  UniquePtr<mcld::IRBuilder> ir_builder_;
-  UniquePtr<mcld::Linker> linker_;
-
-  // Setup by AddOatInput()
-  // TODO: ownership of oat_input_?
-  mcld::Input* oat_input_;
-
-  // Setup by AddCompiledCodeInput
-  // set of symbols for already added mcld::Inputs
-  SafeMap<const std::string*, const std::string*> added_symbols_;
-
-  // Setup by FixupCompiledCodeOffset
-  // map of symbol names to oatdata offset
-  SafeMap<const std::string*, uint32_t> symbol_to_compiled_code_offset_;
-
 };
 
 }  // namespace art
diff --git a/src/elf_writer_mclinker.cc b/src/elf_writer_mclinker.cc
new file mode 100644
index 0000000..bb3a865
--- /dev/null
+++ b/src/elf_writer_mclinker.cc
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "elf_writer_mclinker.h"
+
+#include <llvm/Support/TargetSelect.h>
+
+#include <mcld/Environment.h>
+#include <mcld/IRBuilder.h>
+#include <mcld/Linker.h>
+#include <mcld/LinkerConfig.h>
+#include <mcld/MC/ZOption.h>
+#include <mcld/Module.h>
+#include <mcld/Support/Path.h>
+#include <mcld/Support/TargetSelect.h>
+
+#include "base/unix_file/fd_file.h"
+#include "compiler/driver/compiler_driver.h"
+#include "globals.h"
+
+namespace art {
+
+ElfWriterMclinker::ElfWriterMclinker(const CompilerDriver& driver, File* elf_file)
+  : ElfWriter(driver, elf_file), oat_input_(NULL) {}
+
+ElfWriterMclinker::~ElfWriterMclinker() {}
+
+bool ElfWriterMclinker::Create(File* elf_file,
+                               std::vector<uint8_t>& oat_contents,
+                               const std::vector<const DexFile*>& dex_files,
+                               const std::string& android_root,
+                               bool is_host,
+                               const CompilerDriver& driver) {
+  ElfWriterMclinker elf_writer(driver, elf_file);
+  return elf_writer.Write(oat_contents, dex_files, android_root, is_host);
+}
+
+bool ElfWriterMclinker::Write(std::vector<uint8_t>& oat_contents,
+                              const std::vector<const DexFile*>& dex_files,
+                              const std::string& android_root,
+                              bool is_host) {
+  Init();
+  AddOatInput(oat_contents);
+#if defined(ART_USE_PORTABLE_COMPILER)
+  AddMethodInputs(dex_files);
+  AddRuntimeInputs(android_root, is_host);
+#endif
+  if (!Link()) {
+    return false;
+  }
+#if defined(ART_USE_PORTABLE_COMPILER)
+  FixupOatMethodOffsets(dex_files);
+#endif
+  return true;
+}
+
+static void InitializeLLVM() {
+  // TODO: this is lifted from art's compiler_llvm.cc, should be factored out
+  if (kIsTargetBuild) {
+    llvm::InitializeNativeTarget();
+    // TODO: odd that there is no InitializeNativeTargetMC?
+  } else {
+    llvm::InitializeAllTargets();
+    llvm::InitializeAllTargetMCs();
+  }
+}
+
+void ElfWriterMclinker::Init() {
+  std::string target_triple;
+  std::string target_cpu;
+  std::string target_attr;
+  CompilerDriver::InstructionSetToLLVMTarget(compiler_driver_->GetInstructionSet(),
+                                             target_triple,
+                                             target_cpu,
+                                             target_attr);
+
+  // Based on mclinker's llvm-mcld.cpp main() and LinkerTest
+  //
+  // TODO: LinkerTest uses mcld::Initialize(), but it does an
+  // llvm::InitializeAllTargets, which we don't want. Basically we
+  // want mcld::InitializeNative, but it doesn't exist yet, so we
+  // inline the minimal we need here.
+  InitializeLLVM();
+  mcld::InitializeAllTargets();
+  mcld::InitializeAllLinkers();
+  mcld::InitializeAllEmulations();
+  mcld::InitializeAllDiagnostics();
+
+  linker_config_.reset(new mcld::LinkerConfig(target_triple));
+  CHECK(linker_config_.get() != NULL);
+  linker_config_->setCodeGenType(mcld::LinkerConfig::DynObj);
+  linker_config_->options().setSOName(elf_file_->GetPath());
+
+  // error on undefined symbols.
+  // TODO: should this just be set if kIsDebugBuild?
+  linker_config_->options().setNoUndefined(true);
+
+  if (compiler_driver_->GetInstructionSet() == kMips) {
+     // MCLinker defaults MIPS section alignment to 0x10000, not
+     // 0x1000.  The ABI says this is because the max page size is
+     // general is 64k but that isn't true on Android.
+     mcld::ZOption z_option;
+     z_option.setKind(mcld::ZOption::MaxPageSize);
+     z_option.setPageSize(kPageSize);
+     linker_config_->options().addZOption(z_option);
+  }
+
+  // TODO: Wire up mcld DiagnosticEngine to LOG?
+  linker_config_->options().setColor(false);
+  if (false) {
+    // enables some tracing of input file processing
+    linker_config_->options().setTrace(true);
+  }
+
+  // Based on alone::Linker::config
+  module_.reset(new mcld::Module(linker_config_->options().soname()));
+  CHECK(module_.get() != NULL);
+  ir_builder_.reset(new mcld::IRBuilder(*module_.get(), *linker_config_.get()));
+  CHECK(ir_builder_.get() != NULL);
+  linker_.reset(new mcld::Linker());
+  CHECK(linker_.get() != NULL);
+  linker_->config(*linker_config_.get());
+}
+
+void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) {
+  // Add an artificial memory input. Based on LinkerTest.
+  UniquePtr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath()));
+  CHECK(oat_file.get() != NULL) << elf_file_->GetPath();
+
+  const char* oat_data_start = reinterpret_cast<const char*>(&oat_file->GetOatHeader());
+  const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset();
+  const char* oat_code_start = oat_data_start + oat_data_length;
+  const size_t oat_code_length = oat_file->Size() - oat_data_length;
+
+  // TODO: ownership of oat_input?
+  oat_input_ = ir_builder_->CreateInput("oat contents",
+                                        mcld::sys::fs::Path("oat contents path"),
+                                        mcld::Input::Object);
+  CHECK(oat_input_ != NULL);
+
+  // TODO: ownership of null_section?
+  mcld::LDSection* null_section = ir_builder_->CreateELFHeader(*oat_input_,
+                                                               "",
+                                                               mcld::LDFileFormat::Null,
+                                                               llvm::ELF::SHT_NULL,
+                                                               0);
+  CHECK(null_section != NULL);
+
+  // TODO: we should split readonly data from readonly executable
+  // code like .oat does.  We need to control section layout with
+  // linker script like functionality to guarantee references
+  // between sections maintain relative position which isn't
+  // possible right now with the mclinker APIs.
+  CHECK(oat_code_start != NULL);
+
+  // we need to ensure that oatdata is page aligned so when we
+  // fixup the segment load addresses, they remain page aligned.
+  uint32_t alignment = kPageSize;
+
+  // TODO: ownership of text_section?
+  mcld::LDSection* text_section = ir_builder_->CreateELFHeader(*oat_input_,
+                                                               ".text",
+                                                               llvm::ELF::SHT_PROGBITS,
+                                                               llvm::ELF::SHF_EXECINSTR
+                                                               | llvm::ELF::SHF_ALLOC,
+                                                               alignment);
+  CHECK(text_section != NULL);
+
+  mcld::SectionData* text_sectiondata = ir_builder_->CreateSectionData(*text_section);
+  CHECK(text_sectiondata != NULL);
+
+  // TODO: why does IRBuilder::CreateRegion take a non-const pointer?
+  mcld::Fragment* text_fragment = ir_builder_->CreateRegion(const_cast<char*>(oat_data_start),
+                                                            oat_file->Size());
+  CHECK(text_fragment != NULL);
+  ir_builder_->AppendFragment(*text_fragment, *text_sectiondata);
+
+  ir_builder_->AddSymbol(*oat_input_,
+                         "oatdata",
+                         mcld::ResolveInfo::Object,
+                         mcld::ResolveInfo::Define,
+                         mcld::ResolveInfo::Global,
+                         oat_data_length,  // size
+                         0,                // offset
+                         text_section);
+
+  ir_builder_->AddSymbol(*oat_input_,
+                         "oatexec",
+                         mcld::ResolveInfo::Function,
+                         mcld::ResolveInfo::Define,
+                         mcld::ResolveInfo::Global,
+                         oat_code_length,  // size
+                         oat_data_length,  // offset
+                         text_section);
+
+  ir_builder_->AddSymbol(*oat_input_,
+                         "oatlastword",
+                         mcld::ResolveInfo::Object,
+                         mcld::ResolveInfo::Define,
+                         mcld::ResolveInfo::Global,
+                         0,                // size
+                         // subtract a word so symbol is within section
+                         (oat_data_length + oat_code_length) - sizeof(uint32_t),  // offset
+                         text_section);
+}
+
+#if defined(ART_USE_PORTABLE_COMPILER)
+void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) {
+  DCHECK(oat_input_ != NULL);
+
+  DexMethodIterator it(dex_files);
+  while (it.HasNext()) {
+    const DexFile& dex_file = it.GetDexFile();
+    uint32_t method_idx = it.GetMemberIndex();
+    const CompiledMethod* compiled_method =
+      compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx));
+    if (compiled_method != NULL) {
+      AddCompiledCodeInput(*compiled_method);
+    }
+    it.Next();
+  }
+  added_symbols_.clear();
+}
+
+void ElfWriterMclinker::AddCompiledCodeInput(const CompiledCode& compiled_code) {
+  // Check if we've seen this compiled code before. If so skip
+  // it. This can happen for reused code such as invoke stubs.
+  const std::string& symbol = compiled_code.GetSymbol();
+  SafeMap<const std::string*, const std::string*>::iterator it = added_symbols_.find(&symbol);
+  if (it != added_symbols_.end()) {
+    return;
+  }
+  added_symbols_.Put(&symbol, &symbol);
+
+  // Add input to supply code for symbol
+  const std::vector<uint8_t>& code = compiled_code.GetCode();
+  // TODO: ownership of code_input?
+  // TODO: why does IRBuilder::ReadInput take a non-const pointer?
+  mcld::Input* code_input = ir_builder_->ReadInput(symbol,
+                                                   const_cast<uint8_t*>(&code[0]),
+                                                   code.size());
+  CHECK(code_input != NULL);
+}
+
+void ElfWriterMclinker::AddRuntimeInputs(const std::string& android_root, bool is_host) {
+  std::string libart_so(android_root);
+  libart_so += kIsDebugBuild ? "/lib/libartd.so" : "/lib/libart.so";
+  // TODO: ownership of libart_so_input?
+  mcld::Input* libart_so_input = ir_builder_->ReadInput(libart_so, libart_so);
+  CHECK(libart_so_input != NULL);
+
+  std::string host_prebuilt_dir("prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6");
+
+  std::string compiler_runtime_lib;
+  if (is_host) {
+    compiler_runtime_lib += host_prebuilt_dir;
+    compiler_runtime_lib += "/lib/gcc/i686-linux/4.6.x-google/libgcc.a";
+  } else {
+    compiler_runtime_lib += android_root;
+    compiler_runtime_lib += "/lib/libcompiler_rt.a";
+  }
+  // TODO: ownership of compiler_runtime_lib_input?
+  mcld::Input* compiler_runtime_lib_input = ir_builder_->ReadInput(compiler_runtime_lib,
+                                                                   compiler_runtime_lib);
+  CHECK(compiler_runtime_lib_input != NULL);
+
+  std::string libc_lib;
+  if (is_host) {
+    libc_lib += host_prebuilt_dir;
+    libc_lib += "/sysroot/usr/lib/libc.so.6";
+  } else {
+    libc_lib += android_root;
+    libc_lib += "/lib/libc.so";
+  }
+  // TODO: ownership of libc_lib_input?
+  mcld::Input* libc_lib_input_input = ir_builder_->ReadInput(libc_lib, libc_lib);
+  CHECK(libc_lib_input_input != NULL);
+
+  std::string libm_lib;
+  if (is_host) {
+    libm_lib += host_prebuilt_dir;
+    libm_lib += "/sysroot/usr/lib/libm.so";
+  } else {
+    libm_lib += android_root;
+    libm_lib += "/lib/libm.so";
+  }
+  // TODO: ownership of libm_lib_input?
+  mcld::Input* libm_lib_input_input = ir_builder_->ReadInput(libm_lib, libm_lib);
+  CHECK(libm_lib_input_input != NULL);
+
+}
+#endif
+
+bool ElfWriterMclinker::Link() {
+  // link inputs
+  if (!linker_->link(*module_.get(), *ir_builder_.get())) {
+    LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
+    return false;
+  }
+
+  // emit linked output
+  // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
+  int fd = dup(elf_file_->Fd());
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!linker_->emit(fd)) {
+    LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
+    return false;
+  }
+  mcld::Finalize();
+  LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
+  return true;
+}
+
+#if defined(ART_USE_PORTABLE_COMPILER)
+void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
+  UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false));
+  CHECK(elf_file.get() != NULL) << elf_file_->GetPath();
+
+  llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get());
+  DexMethodIterator it(dex_files);
+  while (it.HasNext()) {
+    const DexFile& dex_file = it.GetDexFile();
+    uint32_t method_idx = it.GetMemberIndex();
+    InvokeType invoke_type = it.GetInvokeType();
+    mirror::AbstractMethod* method = NULL;
+    if (compiler_driver_->IsImage()) {
+      ClassLinker* linker = Runtime::Current()->GetClassLinker();
+      mirror::DexCache* dex_cache = linker->FindDexCache(dex_file);
+      // Unchecked as we hold mutator_lock_ on entry.
+      ScopedObjectAccessUnchecked soa(Thread::Current());
+      method = linker->ResolveMethod(dex_file, method_idx, dex_cache, NULL, NULL, invoke_type);
+      CHECK(method != NULL);
+    }
+    const CompiledMethod* compiled_method =
+      compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx));
+    if (compiled_method != NULL) {
+      uint32_t offset = FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_method);
+      // Don't overwrite static method trampoline
+      if (method != NULL &&
+          (!method->IsStatic() ||
+           method->IsConstructor() ||
+           method->GetDeclaringClass()->IsInitialized())) {
+        method->SetOatCodeOffset(offset);
+      }
+    }
+    it.Next();
+  }
+  symbol_to_compiled_code_offset_.clear();
+}
+
+uint32_t ElfWriterMclinker::FixupCompiledCodeOffset(ElfFile& elf_file,
+                                                    llvm::ELF::Elf32_Addr oatdata_address,
+                                                    const CompiledCode& compiled_code) {
+  const std::string& symbol = compiled_code.GetSymbol();
+  SafeMap<const std::string*, uint32_t>::iterator it = symbol_to_compiled_code_offset_.find(&symbol);
+  if (it != symbol_to_compiled_code_offset_.end()) {
+    return it->second;
+  }
+
+  llvm::ELF::Elf32_Addr compiled_code_address = elf_file.FindSymbolAddress(llvm::ELF::SHT_SYMTAB,
+                                                                           symbol,
+                                                                           true);
+  CHECK_NE(0U, compiled_code_address) << symbol;
+  CHECK_LT(oatdata_address, compiled_code_address) << symbol;
+  uint32_t compiled_code_offset = compiled_code_address - oatdata_address;
+  symbol_to_compiled_code_offset_.Put(&symbol, compiled_code_offset);
+
+  const std::vector<uint32_t>& offsets = compiled_code.GetOatdataOffsetsToCompliledCodeOffset();
+  for (uint32_t i = 0; i < offsets.size(); i++) {
+    uint32_t oatdata_offset = oatdata_address + offsets[i];
+    uint32_t* addr = reinterpret_cast<uint32_t*>(elf_file.Begin() + oatdata_offset);
+    *addr = compiled_code_offset;
+  }
+  return compiled_code_offset;
+}
+#endif
+
+}  // namespace art
diff --git a/src/elf_writer_mclinker.h b/src/elf_writer_mclinker.h
new file mode 100644
index 0000000..21f23e1
--- /dev/null
+++ b/src/elf_writer_mclinker.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_ELF_WRITER_MCLINKER_H_
+#define ART_SRC_ELF_WRITER_MCLINKER_H_
+
+#include "elf_writer.h"
+
+#include "UniquePtr.h"
+#include "safe_map.h"
+
+namespace mcld {
+class IRBuilder;
+class Input;
+class LDSection;
+class LDSymbol;
+class Linker;
+class LinkerConfig;
+class Module;
+} // namespace mcld
+
+namespace art {
+
+class CompiledCode;
+
+class ElfWriterMclinker : public ElfWriter {
+ public:
+
+  // Write an ELF file. Returns true on success, false on failure.
+  static bool Create(File* file,
+                     std::vector<uint8_t>& oat_contents,
+                     const std::vector<const DexFile*>& dex_files,
+                     const std::string& android_root,
+                     bool is_host,
+                     const CompilerDriver& driver)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ protected:
+  virtual bool Write(std::vector<uint8_t>& oat_contents,
+                     const std::vector<const DexFile*>& dex_files,
+                     const std::string& android_root,
+                     bool is_host)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  ElfWriterMclinker(const CompilerDriver& driver, File* elf_file);
+  ~ElfWriterMclinker();
+
+  void Init();
+  void AddOatInput(std::vector<uint8_t>& oat_contents);
+  void AddMethodInputs(const std::vector<const DexFile*>& dex_files);
+  void AddCompiledCodeInput(const CompiledCode& compiled_code);
+  void AddRuntimeInputs(const std::string& android_root, bool is_host);
+  bool Link();
+#if defined(ART_USE_PORTABLE_COMPILER)
+  void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  uint32_t FixupCompiledCodeOffset(ElfFile& elf_file,
+                                   llvm::ELF::Elf32_Addr oatdata_address,
+                                   const CompiledCode& compiled_code);
+#endif
+
+  // Setup by Init()
+  UniquePtr<mcld::LinkerConfig> linker_config_;
+  UniquePtr<mcld::Module> module_;
+  UniquePtr<mcld::IRBuilder> ir_builder_;
+  UniquePtr<mcld::Linker> linker_;
+
+  // Setup by AddOatInput()
+  // TODO: ownership of oat_input_?
+  mcld::Input* oat_input_;
+
+  // Setup by AddCompiledCodeInput
+  // set of symbols for already added mcld::Inputs
+  SafeMap<const std::string*, const std::string*> added_symbols_;
+
+  // Setup by FixupCompiledCodeOffset
+  // map of symbol names to oatdata offset
+  SafeMap<const std::string*, uint32_t> symbol_to_compiled_code_offset_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterMclinker);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_ELF_WRITER_MCLINKER_H_
diff --git a/src/elf_writer_quick.cc b/src/elf_writer_quick.cc
new file mode 100644
index 0000000..c3c5415
--- /dev/null
+++ b/src/elf_writer_quick.cc
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "elf_writer_quick.h"
+
+#include "base/logging.h"
+#include "base/unix_file/fd_file.h"
+#include "compiler/driver/compiler_driver.h"
+#include "globals.h"
+#include "oat.h"
+#include "utils.h"
+
+namespace art {
+
+ElfWriterQuick::ElfWriterQuick(const CompilerDriver& driver, File* elf_file)
+  : ElfWriter(driver, elf_file) {}
+
+ElfWriterQuick::~ElfWriterQuick() {}
+
+bool ElfWriterQuick::Create(File* elf_file,
+                            std::vector<uint8_t>& oat_contents,
+                            const std::vector<const DexFile*>& dex_files,
+                            const std::string& android_root,
+                            bool is_host,
+                            const CompilerDriver& driver) {
+  ElfWriterQuick elf_writer(driver, elf_file);
+  return elf_writer.Write(oat_contents, dex_files, android_root, is_host);
+}
+
+bool ElfWriterQuick::Write(std::vector<uint8_t>& oat_contents,
+                           const std::vector<const DexFile*>& dex_files_unused,
+                           const std::string& android_root_unused,
+                           bool is_host_unused) {
+  const bool debug = false;
+  // +-------------------------+
+  // | Elf32_Ehdr              |
+  // +-------------------------+
+  // | Elf32_Phdr PHDR         |
+  // | Elf32_Phdr LOAD R       | .dynsym .dynstr .hash .rodata
+  // | Elf32_Phdr LOAD R X     | .text
+  // | Elf32_Phdr LOAD RW      | .dynamic
+  // | Elf32_Phdr DYNAMIC      | .dynamic
+  // +-------------------------+
+  // | .dynsym                 |
+  // | Elf32_Sym  STN_UNDEF    |
+  // | Elf32_Sym  oatdata      |
+  // | Elf32_Sym  oatexec      |
+  // | Elf32_Sym  oatlastword  |
+  // +-------------------------+
+  // | .dynstr                 |
+  // | \0                      |
+  // | oatdata\0               |
+  // | oatexec\0               |
+  // | oatlastword\0           |
+  // | boot.oat\0              |
+  // +-------------------------+
+  // | .hash                   |
+  // | Elf32_Word nbucket = 1  |
+  // | Elf32_Word nchain  = 3  |
+  // | Elf32_Word bucket[0] = 0|
+  // | Elf32_Word chain[0]  = 1|
+  // | Elf32_Word chain[1]  = 2|
+  // | Elf32_Word chain[2]  = 3|
+  // +-------------------------+
+  // | .rodata                 |
+  // | oatdata..oatexec-4      |
+  // +-------------------------+
+  // | .text                   |
+  // | oatexec..oatlastword    |
+  // +-------------------------+
+  // | .dynamic                |
+  // | Elf32_Dyn DT_SONAME     |
+  // | Elf32_Dyn DT_HASH       |
+  // | Elf32_Dyn DT_SYMTAB     |
+  // | Elf32_Dyn DT_SYMENT     |
+  // | Elf32_Dyn DT_STRTAB     |
+  // | Elf32_Dyn DT_STRSZ      |
+  // | Elf32_Dyn DT_NULL       |
+  // +-------------------------+
+  // | .shstrtab               |
+  // | \0                      |
+  // | .dynamic\0              |
+  // | .dynsym\0               |
+  // | .dynstr\0               |
+  // | .hash\0                 |
+  // | .rodata\0               |
+  // | .text\0                 |
+  // | .shstrtab\0             |
+  // +-------------------------+
+  // | Elf32_Shdr NULL         |
+  // | Elf32_Shdr .dynsym      |
+  // | Elf32_Shdr .dynstr      |
+  // | Elf32_Shdr .hash        |
+  // | Elf32_Shdr .text        |
+  // | Elf32_Shdr .rodata      |
+  // | Elf32_Shdr .dynamic     |
+  // | Elf32_Shdr .shstrtab    |
+  // +-------------------------+
+
+  // phase 1: computing offsets
+  uint32_t expected_offset = 0;
+
+  // Elf32_Ehdr
+  expected_offset += sizeof(llvm::ELF::Elf32_Ehdr);
+
+  // PHDR
+  uint32_t phdr_alignment = sizeof(llvm::ELF::Elf32_Word);
+  uint32_t phdr_offset = expected_offset;
+  const uint8_t PH_PHDR     = 0;
+  const uint8_t PH_LOAD_R__ = 1;
+  const uint8_t PH_LOAD_R_X = 2;
+  const uint8_t PH_LOAD_RW_ = 3;
+  const uint8_t PH_DYNAMIC  = 4;
+  const uint8_t PH_NUM      = 5;
+  uint32_t phdr_size = sizeof(llvm::ELF::Elf32_Phdr) * PH_NUM;
+  expected_offset += phdr_size;
+  if (debug) {
+    LOG(INFO) << "phdr_offset=" << phdr_offset << std::hex << " " << phdr_offset;
+    LOG(INFO) << "phdr_size=" << phdr_size << std::hex << " " << phdr_size;
+  }
+
+  // .dynsym
+  uint32_t dynsym_alignment = sizeof(llvm::ELF::Elf32_Word);
+  uint32_t dynsym_offset = expected_offset = RoundUp(expected_offset, dynsym_alignment);
+  const uint8_t SYM_UNDEF       = 0; // aka STN_UNDEF
+  const uint8_t SYM_OATDATA     = 1;
+  const uint8_t SYM_OATEXEC     = 2;
+  const uint8_t SYM_OATLASTWORD = 3;
+  const uint8_t SYM_NUM         = 4;
+  uint32_t dynsym_size = sizeof(llvm::ELF::Elf32_Sym) * SYM_NUM;
+  expected_offset += dynsym_size;
+  if (debug) {
+    LOG(INFO) << "dynsym_offset=" << dynsym_offset << std::hex << " " << dynsym_offset;
+    LOG(INFO) << "dynsym_size=" << dynsym_size << std::hex << " " << dynsym_size;
+  }
+
+  // .dynstr
+  uint32_t dynstr_alignment = 1;
+  uint32_t dynstr_offset = expected_offset = RoundUp(expected_offset, dynstr_alignment);
+  std::string dynstr;
+  dynstr += '\0';
+  uint32_t dynstr_oatdata_offset = dynstr.size();
+  dynstr += "oatdata";
+  dynstr += '\0';
+  uint32_t dynstr_oatexec_offset = dynstr.size();
+  dynstr += "oatexec";
+  dynstr += '\0';
+  uint32_t dynstr_oatlastword_offset = dynstr.size();
+  dynstr += "oatlastword";
+  dynstr += '\0';
+  uint32_t dynstr_soname_offset = dynstr.size();
+  std::string file_name(elf_file_->GetPath());
+  size_t directory_separator_pos = file_name.rfind('/');
+  if (directory_separator_pos != std::string::npos) {
+    file_name = file_name.substr(directory_separator_pos + 1);
+  }
+  dynstr += file_name;
+  dynstr += '\0';
+  uint32_t dynstr_size = dynstr.size();
+  expected_offset += dynstr_size;
+  if (debug) {
+    LOG(INFO) << "dynstr_offset=" << dynstr_offset << std::hex << " " << dynstr_offset;
+    LOG(INFO) << "dynstr_size=" << dynstr_size << std::hex << " " << dynstr_size;
+  }
+
+  // .hash
+  uint32_t hash_alignment = sizeof(llvm::ELF::Elf32_Word);  // Even for 64-bit
+  uint32_t hash_offset = expected_offset = RoundUp(expected_offset, hash_alignment);
+  const uint8_t HASH_NBUCKET = 0;
+  const uint8_t HASH_NCHAIN  = 1;
+  const uint8_t HASH_BUCKET0 = 2;
+  const uint8_t HASH_NUM     = HASH_BUCKET0 + 1 + SYM_NUM;
+  uint32_t hash_size = sizeof(llvm::ELF::Elf32_Word) * HASH_NUM;
+  expected_offset += hash_size;
+  if (debug) {
+    LOG(INFO) << "hash_offset=" << hash_offset << std::hex << " " << hash_offset;
+    LOG(INFO) << "hash_size=" << hash_size << std::hex << " " << hash_size;
+  }
+
+  // .rodata
+  uint32_t oat_data_alignment = kPageSize;
+  uint32_t oat_data_offset = expected_offset = RoundUp(expected_offset, oat_data_alignment);
+  const OatHeader* oat_header = reinterpret_cast<OatHeader*>(&oat_contents[0]);
+  CHECK(oat_header->IsValid());
+  uint32_t oat_data_size = oat_header->GetExecutableOffset();
+  expected_offset += oat_data_size;
+  if (debug) {
+    LOG(INFO) << "oat_data_offset=" << oat_data_offset << std::hex << " " << oat_data_offset;
+    LOG(INFO) << "oat_data_size=" << oat_data_size << std::hex << " " << oat_data_size;
+  }
+
+  // .text
+  uint32_t oat_exec_alignment = kPageSize;
+  CHECK_ALIGNED(expected_offset, kPageSize);
+  uint32_t oat_exec_offset = expected_offset = RoundUp(expected_offset, oat_exec_alignment);
+  uint32_t oat_exec_size = oat_contents.size() - oat_data_size;
+  expected_offset += oat_exec_size;
+  CHECK_EQ(oat_data_offset + oat_contents.size(), expected_offset);
+  if (debug) {
+    LOG(INFO) << "oat_exec_offset=" << oat_exec_offset << std::hex << " " << oat_exec_offset;
+    LOG(INFO) << "oat_exec_size=" << oat_exec_size << std::hex << " " << oat_exec_size;
+  }
+
+  // .dynamic
+  // alignment would naturally be sizeof(llvm::ELF::Elf32_Word), but we want this in a new segment
+  uint32_t dynamic_alignment = kPageSize;
+  uint32_t dynamic_offset = expected_offset = RoundUp(expected_offset, dynamic_alignment);
+  const uint8_t DH_SONAME = 0;
+  const uint8_t DH_HASH   = 1;
+  const uint8_t DH_SYMTAB = 2;
+  const uint8_t DH_SYMENT = 3;
+  const uint8_t DH_STRTAB = 4;
+  const uint8_t DH_STRSZ  = 5;
+  const uint8_t DH_NULL   = 6;
+  const uint8_t DH_NUM    = 7;
+  uint32_t dynamic_size = sizeof(llvm::ELF::Elf32_Dyn) * DH_NUM;
+  expected_offset += dynamic_size;
+  if (debug) {
+    LOG(INFO) << "dynamic_offset=" << dynamic_offset << std::hex << " " << dynamic_offset;
+    LOG(INFO) << "dynamic_size=" << dynamic_size << std::hex << " " << dynamic_size;
+  }
+
+  // .shstrtab
+  uint32_t shstrtab_alignment = 1;
+  uint32_t shstrtab_offset = expected_offset = RoundUp(expected_offset, shstrtab_alignment);
+  std::string shstrtab;
+  shstrtab += '\0';
+  uint32_t shstrtab_dynamic_offset = shstrtab.size();
+  CHECK_EQ(1U, shstrtab_dynamic_offset);
+  shstrtab += ".dynamic";
+  shstrtab += '\0';
+  uint32_t shstrtab_dynsym_offset = shstrtab.size();
+  shstrtab += ".dynsym";
+  shstrtab += '\0';
+  uint32_t shstrtab_dynstr_offset = shstrtab.size();
+  shstrtab += ".dynstr";
+  shstrtab += '\0';
+  uint32_t shstrtab_hash_offset = shstrtab.size();
+  shstrtab += ".hash";
+  shstrtab += '\0';
+  uint32_t shstrtab_rodata_offset = shstrtab.size();
+  shstrtab += ".rodata";
+  shstrtab += '\0';
+  uint32_t shstrtab_text_offset = shstrtab.size();
+  shstrtab += ".text";
+  shstrtab += '\0';
+  uint32_t shstrtab_shstrtab_offset = shstrtab.size();
+  shstrtab += ".shstrtab";
+  shstrtab += '\0';
+  uint32_t shstrtab_size = shstrtab.size();
+  expected_offset += shstrtab_size;
+  if (debug) {
+    LOG(INFO) << "shstrtab_offset=" << shstrtab_offset << std::hex << " " << shstrtab_offset;
+    LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size;
+  }
+
+  // section headers (after all sections)
+  uint32_t shdr_alignment = sizeof(llvm::ELF::Elf32_Word);
+  uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment);
+  const uint8_t SH_NULL     = 0;
+  const uint8_t SH_DYNSYM   = 1;
+  const uint8_t SH_DYNSTR   = 2;
+  const uint8_t SH_HASH     = 3;
+  const uint8_t SH_RODATA   = 4;
+  const uint8_t SH_TEXT     = 5;
+  const uint8_t SH_DYNAMIC  = 6;
+  const uint8_t SH_SHSTRTAB = 7;
+  const uint8_t SH_NUM      = 8;
+  uint32_t shdr_size = sizeof(llvm::ELF::Elf32_Shdr) * SH_NUM;
+  expected_offset += shdr_size;
+  if (debug) {
+    LOG(INFO) << "shdr_offset=" << shdr_offset << std::hex << " " << shdr_offset;
+    LOG(INFO) << "shdr_size=" << shdr_size << std::hex << " " << shdr_size;
+  }
+
+  // phase 2: initializing data
+
+  // Elf32_Ehdr
+  llvm::ELF::Elf32_Ehdr elf_header;
+  memset(&elf_header, 0, sizeof(elf_header));
+  elf_header.e_ident[llvm::ELF::EI_MAG0]       = llvm::ELF::ElfMagic[0];
+  elf_header.e_ident[llvm::ELF::EI_MAG1]       = llvm::ELF::ElfMagic[1];
+  elf_header.e_ident[llvm::ELF::EI_MAG2]       = llvm::ELF::ElfMagic[2];
+  elf_header.e_ident[llvm::ELF::EI_MAG3]       = llvm::ELF::ElfMagic[3];
+  elf_header.e_ident[llvm::ELF::EI_CLASS]      = llvm::ELF::ELFCLASS32;
+  elf_header.e_ident[llvm::ELF::EI_DATA]       = llvm::ELF::ELFDATA2LSB;
+  elf_header.e_ident[llvm::ELF::EI_VERSION]    = llvm::ELF::EV_CURRENT;
+  elf_header.e_ident[llvm::ELF::EI_OSABI]      = llvm::ELF::ELFOSABI_LINUX;
+  elf_header.e_ident[llvm::ELF::EI_ABIVERSION] = 0;
+  elf_header.e_type = llvm::ELF::ET_DYN;
+  switch (compiler_driver_->GetInstructionSet()) {
+    case kThumb2: {
+      elf_header.e_machine = llvm::ELF::EM_ARM;
+      elf_header.e_flags = llvm::ELF::EF_ARM_EABI_VER5;
+      break;
+    }
+    case kX86: {
+      elf_header.e_machine = llvm::ELF::EM_386;
+      elf_header.e_flags = 0;
+      break;
+    }
+    case kMips: {
+      elf_header.e_machine = llvm::ELF::EM_MIPS;
+      elf_header.e_flags = (llvm::ELF::EF_MIPS_NOREORDER |
+                            llvm::ELF::EF_MIPS_PIC       |
+                            llvm::ELF::EF_MIPS_CPIC      |
+                            llvm::ELF::EF_MIPS_ABI_O32   |
+                            llvm::ELF::EF_MIPS_ARCH_32R2);
+      break;
+    }
+    case kArm:
+    default: {
+      LOG(FATAL) << "Unknown instruction set: " << compiler_driver_->GetInstructionSet();
+      break;
+    }
+  }
+  elf_header.e_version = 1;
+  elf_header.e_entry = 0;
+  elf_header.e_phoff = phdr_offset;
+  elf_header.e_shoff = shdr_offset;
+  elf_header.e_ehsize = sizeof(llvm::ELF::Elf32_Ehdr);
+  elf_header.e_phentsize = sizeof(llvm::ELF::Elf32_Phdr);
+  elf_header.e_phnum = PH_NUM;
+  elf_header.e_shentsize = sizeof(llvm::ELF::Elf32_Shdr);
+  elf_header.e_shnum = SH_NUM;
+  elf_header.e_shstrndx = SH_SHSTRTAB;
+
+  // PHDR
+  llvm::ELF::Elf32_Phdr program_headers[PH_NUM];
+  memset(&program_headers, 0, sizeof(program_headers));
+
+  program_headers[PH_PHDR].p_type    = llvm::ELF::PT_PHDR;
+  program_headers[PH_PHDR].p_offset  = phdr_offset;
+  program_headers[PH_PHDR].p_vaddr   = phdr_offset;
+  program_headers[PH_PHDR].p_paddr   = phdr_offset;
+  program_headers[PH_PHDR].p_filesz  = sizeof(program_headers);
+  program_headers[PH_PHDR].p_memsz   = sizeof(program_headers);
+  program_headers[PH_PHDR].p_flags   = llvm::ELF::PF_R;
+  program_headers[PH_PHDR].p_align   = phdr_alignment;
+
+  program_headers[PH_LOAD_R__].p_type    = llvm::ELF::PT_LOAD;
+  program_headers[PH_LOAD_R__].p_offset  = 0;
+  program_headers[PH_LOAD_R__].p_vaddr   = 0;
+  program_headers[PH_LOAD_R__].p_paddr   = 0;
+  program_headers[PH_LOAD_R__].p_filesz  = oat_data_offset + oat_data_size;
+  program_headers[PH_LOAD_R__].p_memsz   = oat_data_offset + oat_data_size;
+  program_headers[PH_LOAD_R__].p_flags   = llvm::ELF::PF_R;
+  program_headers[PH_LOAD_R__].p_align   = oat_data_alignment;
+
+  program_headers[PH_LOAD_R_X].p_type    = llvm::ELF::PT_LOAD;
+  program_headers[PH_LOAD_R_X].p_offset  = oat_exec_offset;
+  program_headers[PH_LOAD_R_X].p_vaddr   = oat_exec_offset;
+  program_headers[PH_LOAD_R_X].p_paddr   = oat_exec_offset;
+  program_headers[PH_LOAD_R_X].p_filesz  = oat_exec_size;
+  program_headers[PH_LOAD_R_X].p_memsz   = oat_exec_size;
+  program_headers[PH_LOAD_R_X].p_flags   = llvm::ELF::PF_R | llvm::ELF::PF_X;
+  program_headers[PH_LOAD_R_X].p_align   = oat_exec_alignment;
+
+  // TODO: PF_W for DYNAMIC is considered processor specific, do we need it?
+  program_headers[PH_LOAD_RW_].p_type    = llvm::ELF::PT_LOAD;
+  program_headers[PH_LOAD_RW_].p_offset  = dynamic_offset;
+  program_headers[PH_LOAD_RW_].p_vaddr   = dynamic_offset;
+  program_headers[PH_LOAD_RW_].p_paddr   = dynamic_offset;
+  program_headers[PH_LOAD_RW_].p_filesz  = dynamic_size;
+  program_headers[PH_LOAD_RW_].p_memsz   = dynamic_size;
+  program_headers[PH_LOAD_RW_].p_flags   = llvm::ELF::PF_R | llvm::ELF::PF_W;
+  program_headers[PH_LOAD_RW_].p_align   = dynamic_alignment;
+
+  // TODO: PF_W for DYNAMIC is considered processor specific, do we need it?
+  program_headers[PH_DYNAMIC].p_type    = llvm::ELF::PT_DYNAMIC;
+  program_headers[PH_DYNAMIC].p_offset  = dynamic_offset;
+  program_headers[PH_DYNAMIC].p_vaddr   = dynamic_offset;
+  program_headers[PH_DYNAMIC].p_paddr   = dynamic_offset;
+  program_headers[PH_DYNAMIC].p_filesz  = dynamic_size;
+  program_headers[PH_DYNAMIC].p_memsz   = dynamic_size;
+  program_headers[PH_DYNAMIC].p_flags   = llvm::ELF::PF_R | llvm::ELF::PF_W;
+  program_headers[PH_DYNAMIC].p_align   = dynamic_alignment;
+
+  // .dynsym
+  llvm::ELF::Elf32_Sym dynsym[SYM_NUM];
+  memset(&dynsym, 0, sizeof(dynsym));
+
+  dynsym[SYM_UNDEF].st_name  = 0;
+  dynsym[SYM_UNDEF].st_value = 0;
+  dynsym[SYM_UNDEF].st_size  = 0;
+  dynsym[SYM_UNDEF].st_info  = 0;
+  dynsym[SYM_UNDEF].st_other = 0;
+  dynsym[SYM_UNDEF].st_shndx = 0;
+
+  dynsym[SYM_OATDATA].st_name  = dynstr_oatdata_offset;
+  dynsym[SYM_OATDATA].st_value = oat_data_offset;
+  dynsym[SYM_OATDATA].st_size  = oat_data_size;
+  dynsym[SYM_OATDATA].setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT);
+  dynsym[SYM_OATDATA].st_other = llvm::ELF::STV_DEFAULT;
+  dynsym[SYM_OATDATA].st_shndx = SH_RODATA;
+
+  dynsym[SYM_OATEXEC].st_name  = dynstr_oatexec_offset;
+  dynsym[SYM_OATEXEC].st_value = oat_exec_offset;
+  dynsym[SYM_OATEXEC].st_size  = oat_exec_size;
+  dynsym[SYM_OATEXEC].setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT);
+  dynsym[SYM_OATEXEC].st_other = llvm::ELF::STV_DEFAULT;
+  dynsym[SYM_OATEXEC].st_shndx = SH_TEXT;
+
+  dynsym[SYM_OATLASTWORD].st_name  = dynstr_oatlastword_offset;
+  dynsym[SYM_OATLASTWORD].st_value = oat_exec_offset + oat_exec_size - 4;
+  dynsym[SYM_OATLASTWORD].st_size  = 4;
+  dynsym[SYM_OATLASTWORD].setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT);
+  dynsym[SYM_OATLASTWORD].st_other = llvm::ELF::STV_DEFAULT;
+  dynsym[SYM_OATLASTWORD].st_shndx = SH_TEXT;
+
+  // .dynstr initialized above as dynstr
+
+  // .hash
+  llvm::ELF::Elf32_Word hash[HASH_NUM];  // Note this is Elf32_Word even on 64-bit
+  hash[HASH_NBUCKET] = 1;
+  hash[HASH_NCHAIN]  = SYM_NUM;
+  hash[HASH_BUCKET0] = SYM_OATDATA;
+  hash[HASH_BUCKET0 + 1 + SYM_UNDEF]       = SYM_UNDEF;
+  hash[HASH_BUCKET0 + 1 + SYM_OATDATA]     = SYM_OATEXEC;
+  hash[HASH_BUCKET0 + 1 + SYM_OATEXEC]     = SYM_OATLASTWORD;
+  hash[HASH_BUCKET0 + 1 + SYM_OATLASTWORD] = SYM_UNDEF;
+
+  // .rodata and .text content come from oat_contents
+
+  // .dynamic
+  llvm::ELF::Elf32_Dyn dynamic_headers[DH_NUM];
+  memset(&dynamic_headers, 0, sizeof(dynamic_headers));
+
+  dynamic_headers[DH_SONAME].d_tag = llvm::ELF::DT_SONAME;
+  dynamic_headers[DH_SONAME].d_un.d_val = dynstr_soname_offset;
+
+  dynamic_headers[DH_HASH].d_tag = llvm::ELF::DT_HASH;
+  dynamic_headers[DH_HASH].d_un.d_ptr = hash_offset;
+
+  dynamic_headers[DH_SYMTAB].d_tag = llvm::ELF::DT_SYMTAB;
+  dynamic_headers[DH_SYMTAB].d_un.d_ptr = dynsym_offset;
+
+  dynamic_headers[DH_SYMENT].d_tag = llvm::ELF::DT_SYMENT;
+  dynamic_headers[DH_SYMENT].d_un.d_val = sizeof(llvm::ELF::Elf32_Sym);
+
+  dynamic_headers[DH_STRTAB].d_tag = llvm::ELF::DT_STRTAB;
+  dynamic_headers[DH_STRTAB].d_un.d_ptr = dynstr_offset;
+
+  dynamic_headers[DH_STRSZ].d_tag = llvm::ELF::DT_STRSZ;
+  dynamic_headers[DH_STRSZ].d_un.d_val = dynstr_size;
+
+  dynamic_headers[DH_NULL].d_tag = llvm::ELF::DT_NULL;
+  dynamic_headers[DH_NULL].d_un.d_val = 0;
+
+  // .shstrtab initialized above as shstrtab
+
+  // section headers (after all sections)
+  llvm::ELF::Elf32_Shdr section_headers[SH_NUM];
+  memset(&section_headers, 0, sizeof(section_headers));
+
+  section_headers[SH_NULL].sh_name      = 0;
+  section_headers[SH_NULL].sh_type      = llvm::ELF::SHT_NULL;
+  section_headers[SH_NULL].sh_flags     = 0;
+  section_headers[SH_NULL].sh_addr      = 0;
+  section_headers[SH_NULL].sh_offset    = 0;
+  section_headers[SH_NULL].sh_size      = 0;
+  section_headers[SH_NULL].sh_link      = 0;
+  section_headers[SH_NULL].sh_info      = 0;
+  section_headers[SH_NULL].sh_addralign = 0;
+  section_headers[SH_NULL].sh_entsize   = 0;
+
+  section_headers[SH_DYNSYM].sh_name      = shstrtab_dynsym_offset;
+  section_headers[SH_DYNSYM].sh_type      = llvm::ELF::SHT_DYNSYM;
+  section_headers[SH_DYNSYM].sh_flags     = llvm::ELF::SHF_ALLOC;
+  section_headers[SH_DYNSYM].sh_addr      = dynsym_offset;
+  section_headers[SH_DYNSYM].sh_offset    = dynsym_offset;
+  section_headers[SH_DYNSYM].sh_size      = dynsym_size;
+  section_headers[SH_DYNSYM].sh_link      = SH_DYNSTR;
+  section_headers[SH_DYNSYM].sh_info      = 1;  // 1 because we have not STB_LOCAL symbols
+  section_headers[SH_DYNSYM].sh_addralign = dynsym_alignment;
+  section_headers[SH_DYNSYM].sh_entsize   = sizeof(llvm::ELF::Elf32_Sym);
+
+  section_headers[SH_DYNSTR].sh_name      = shstrtab_dynstr_offset;
+  section_headers[SH_DYNSTR].sh_type      = llvm::ELF::SHT_STRTAB;
+  section_headers[SH_DYNSTR].sh_flags     = llvm::ELF::SHF_ALLOC;
+  section_headers[SH_DYNSTR].sh_addr      = dynstr_offset;
+  section_headers[SH_DYNSTR].sh_offset    = dynstr_offset;
+  section_headers[SH_DYNSTR].sh_size      = dynstr_size;
+  section_headers[SH_DYNSTR].sh_link      = 0;
+  section_headers[SH_DYNSTR].sh_info      = 0;
+  section_headers[SH_DYNSTR].sh_addralign = dynstr_alignment;
+  section_headers[SH_DYNSTR].sh_entsize   = 0;
+
+  section_headers[SH_HASH].sh_name      = shstrtab_hash_offset;
+  section_headers[SH_HASH].sh_type      = llvm::ELF::SHT_HASH;
+  section_headers[SH_HASH].sh_flags     = llvm::ELF::SHF_ALLOC;
+  section_headers[SH_HASH].sh_addr      = hash_offset;
+  section_headers[SH_HASH].sh_offset    = hash_offset;
+  section_headers[SH_HASH].sh_size      = hash_size;
+  section_headers[SH_HASH].sh_link      = SH_DYNSYM;
+  section_headers[SH_HASH].sh_info      = 0;
+  section_headers[SH_HASH].sh_addralign = hash_alignment;
+  section_headers[SH_HASH].sh_entsize   = sizeof(llvm::ELF::Elf32_Word); // This is Elf32_Word even on 64-bit
+
+  section_headers[SH_RODATA].sh_name      = shstrtab_rodata_offset;
+  section_headers[SH_RODATA].sh_type      = llvm::ELF::SHT_PROGBITS;
+  section_headers[SH_RODATA].sh_flags     = llvm::ELF::SHF_ALLOC;
+  section_headers[SH_RODATA].sh_addr      = oat_data_offset;
+  section_headers[SH_RODATA].sh_offset    = oat_data_offset;
+  section_headers[SH_RODATA].sh_size      = oat_data_size;
+  section_headers[SH_RODATA].sh_link      = 0;
+  section_headers[SH_RODATA].sh_info      = 0;
+  section_headers[SH_RODATA].sh_addralign = oat_data_alignment;
+  section_headers[SH_RODATA].sh_entsize   = 0;
+
+  section_headers[SH_TEXT].sh_name      = shstrtab_text_offset;
+  section_headers[SH_TEXT].sh_type      = llvm::ELF::SHT_PROGBITS;
+  section_headers[SH_TEXT].sh_flags     = llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR;
+  section_headers[SH_TEXT].sh_addr      = oat_exec_offset;
+  section_headers[SH_TEXT].sh_offset    = oat_exec_offset;
+  section_headers[SH_TEXT].sh_size      = oat_exec_size;
+  section_headers[SH_TEXT].sh_link      = 0;
+  section_headers[SH_TEXT].sh_info      = 0;
+  section_headers[SH_TEXT].sh_addralign = oat_exec_alignment;
+  section_headers[SH_TEXT].sh_entsize   = 0;
+
+  // TODO: SHF_WRITE for .dynamic is considered processor specific, do we need it?
+  section_headers[SH_DYNAMIC].sh_name      = shstrtab_dynamic_offset;
+  section_headers[SH_DYNAMIC].sh_type      = llvm::ELF::SHT_DYNAMIC;
+  section_headers[SH_DYNAMIC].sh_flags     = llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC;
+  section_headers[SH_DYNAMIC].sh_addr      = dynamic_offset;
+  section_headers[SH_DYNAMIC].sh_offset    = dynamic_offset;
+  section_headers[SH_DYNAMIC].sh_size      = dynamic_size;
+  section_headers[SH_DYNAMIC].sh_link      = SH_DYNSTR;
+  section_headers[SH_DYNAMIC].sh_info      = 0;
+  section_headers[SH_DYNAMIC].sh_addralign = dynamic_alignment;
+  section_headers[SH_DYNAMIC].sh_entsize   = sizeof(llvm::ELF::Elf32_Dyn);
+
+  section_headers[SH_SHSTRTAB].sh_name      = shstrtab_shstrtab_offset;
+  section_headers[SH_SHSTRTAB].sh_type      = llvm::ELF::SHT_STRTAB;
+  section_headers[SH_SHSTRTAB].sh_flags     = 0;
+  section_headers[SH_SHSTRTAB].sh_addr      = shstrtab_offset;
+  section_headers[SH_SHSTRTAB].sh_offset    = shstrtab_offset;
+  section_headers[SH_SHSTRTAB].sh_size      = shstrtab_size;
+  section_headers[SH_SHSTRTAB].sh_link      = 0;
+  section_headers[SH_SHSTRTAB].sh_info      = 0;
+  section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment;
+  section_headers[SH_SHSTRTAB].sh_entsize   = 0;
+
+  // phase 3: writing file
+
+  // Elf32_Ehdr
+  if (!elf_file_->WriteFully(&elf_header, sizeof(elf_header))) {
+    PLOG(ERROR) << "Failed to write ELF header for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // PHDR
+  if (static_cast<off_t>(phdr_offset) != lseek(elf_file_->Fd(), 0, SEEK_CUR)) {
+    PLOG(ERROR) << "Failed to be at expected ELF program header offset phdr_offset "
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(program_headers, sizeof(program_headers))) {
+    PLOG(ERROR) << "Failed to write ELF program headers for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // .dynsym
+  DCHECK_LE(phdr_offset + phdr_size, dynsym_offset);
+  if (static_cast<off_t>(dynsym_offset) != lseek(elf_file_->Fd(), dynsym_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to .dynsym offset location " << dynsym_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(dynsym, sizeof(dynsym))) {
+    PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // .dynstr
+  DCHECK_LE(dynsym_offset + dynsym_size, dynstr_offset);
+  if (static_cast<off_t>(dynstr_offset) != lseek(elf_file_->Fd(), dynstr_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to .dynstr offset " << dynstr_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(&dynstr[0], dynsym_size)) {
+    PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // .hash
+  DCHECK_LE(dynstr_offset + dynstr_size, hash_offset);
+  if (static_cast<off_t>(hash_offset) != lseek(elf_file_->Fd(), hash_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to .hash offset " << hash_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(hash, sizeof(hash))) {
+    PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // .rodata .text
+  DCHECK_LE(hash_offset + hash_size, oat_data_offset);
+  if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(&oat_contents[0], oat_contents.size())) {
+    PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // .dynamic
+  DCHECK_LE(oat_data_offset + oat_contents.size(), dynamic_offset);
+  if (static_cast<off_t>(dynamic_offset) != lseek(elf_file_->Fd(), dynamic_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to .dynamic offset " << dynamic_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(&dynamic_headers[0], dynamic_size)) {
+    PLOG(ERROR) << "Failed to write .dynamic for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // .shstrtab
+  DCHECK_LE(dynamic_offset + dynamic_size, shstrtab_offset);
+  if (static_cast<off_t>(shstrtab_offset) != lseek(elf_file_->Fd(), shstrtab_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to .shstrtab offset " << shstrtab_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(&shstrtab[0], shstrtab_size)) {
+    PLOG(ERROR) << "Failed to write .shstrtab for " << elf_file_->GetPath();
+    return false;
+  }
+
+  // section headers (after all sections)
+  DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset);
+  if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) {
+    PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset
+                << " for " << elf_file_->GetPath();
+    return false;
+  }
+  if (!elf_file_->WriteFully(section_headers, sizeof(section_headers))) {
+    PLOG(ERROR) << "Failed to write ELF section headers for " << elf_file_->GetPath();
+    return false;
+  }
+
+  LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
+  return true;
+}
+
+}  // namespace art
diff --git a/src/elf_writer_quick.h b/src/elf_writer_quick.h
new file mode 100644
index 0000000..a1a386b
--- /dev/null
+++ b/src/elf_writer_quick.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_ELF_WRITER_MCLINKER_H_
+#define ART_SRC_ELF_WRITER_MCLINKER_H_
+
+#include "elf_writer.h"
+
+namespace art {
+
+class ElfWriterQuick : public ElfWriter {
+ public:
+  // Write an ELF file. Returns true on success, false on failure.
+  static bool Create(File* file,
+                     std::vector<uint8_t>& oat_contents,
+                     const std::vector<const DexFile*>& dex_files,
+                     const std::string& android_root,
+                     bool is_host,
+                     const CompilerDriver& driver)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ protected:
+  virtual bool Write(std::vector<uint8_t>& oat_contents,
+                     const std::vector<const DexFile*>& dex_files,
+                     const std::string& android_root,
+                     bool is_host)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  ElfWriterQuick(const CompilerDriver& driver, File* elf_file);
+  ~ElfWriterQuick();
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_ELF_WRITER_MCLINKER_H_
diff --git a/src/elf_writer_test.cc b/src/elf_writer_test.cc
index a47bc717..d4486d2 100644
--- a/src/elf_writer_test.cc
+++ b/src/elf_writer_test.cc
@@ -31,21 +31,10 @@
 };
 
 #define EXPECT_ELF_FILE_ADDRESS(ef, value, name, build_map) \
-  EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(::llvm::ELF::SHT_SYMTAB, name, build_map))); \
   EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(::llvm::ELF::SHT_DYNSYM, name, build_map))); \
   EXPECT_EQ(value, ef->FindDynamicSymbolAddress(name)); \
 
-/*
- * TODO: Reenable dlopen when it works again on MIPS. It may have broken from this change:
- * commit 818d98eb563ad5d7293b8b5c40f3dabf745e611f
- * Author: Brian Carlstrom <bdc@google.com>
- * Date:   Sun Feb 10 21:38:12 2013 -0800
- *
- *    Fix MIPS to use standard kPageSize=0x1000 section alignment for ELF sections
- *
- *    Change-Id: I905f0c5f75921a65bd7426a54d6258c780d85d0e
- */
-TEST_F(ElfWriterTest, DISABLED_dlsym) {
+TEST_F(ElfWriterTest, dlsym) {
   std::string elf_filename;
   if (IsHost()) {
     const char* host_dir = getenv("ANDROID_HOST_OUT");
diff --git a/src/oat_file.cc b/src/oat_file.cc
index 414813c..7bbf94c 100644
--- a/src/oat_file.cc
+++ b/src/oat_file.cc
@@ -346,8 +346,7 @@
       oat_method_offsets.fp_spill_mask_,
       oat_method_offsets.mapping_table_offset_,
       oat_method_offsets.vmap_table_offset_,
-      oat_method_offsets.gc_map_offset_
-      );
+      oat_method_offsets.gc_map_offset_);
 }
 
 OatFile::OatMethod::OatMethod(const byte* base,
@@ -357,8 +356,7 @@
                               const uint32_t fp_spill_mask,
                               const uint32_t mapping_table_offset,
                               const uint32_t vmap_table_offset,
-                              const uint32_t gc_map_offset
-                              )
+                              const uint32_t gc_map_offset)
   : begin_(base),
     code_offset_(code_offset),
     frame_size_in_bytes_(frame_size_in_bytes),
diff --git a/src/oat_file.h b/src/oat_file.h
index 80a2920..46aad4f 100644
--- a/src/oat_file.h
+++ b/src/oat_file.h
@@ -115,8 +115,7 @@
               const uint32_t fp_spill_mask,
               const uint32_t mapping_table_offset,
               const uint32_t vmap_table_offset,
-              const uint32_t gc_map_offset
-              );
+              const uint32_t gc_map_offset);
 
    private:
     template<class T>