[lld-macho][nfc] Refactor in preparation for 32-bit support

The main challenge was handling the different on-disk structures (e.g.
`mach_header` vs `mach_header_64`). I tried to strike a balance between
sprinkling `target->wordSize == 8` checks everywhere (branchy = slow, and ugly)
and templatizing everything (causes code bloat, also ugly). I think I struck a
decent balance by judicious use of type erasure.

Note that LLD-ELF has a similar architecture, though it seems to use more templating.

Linking chromium_framework takes about the same time before and after this
change:

      N           Min           Max        Median           Avg        Stddev
  x  20          4.52          4.67         4.595        4.5945   0.044423204
  +  20           4.5          4.71         4.575         4.582   0.056344803
  No difference proven at 95.0% confidence

Reviewed By: #lld-macho, oontvoo

Differential Revision: https://reviews.llvm.org/D99633

GitOrigin-RevId: 817d98d84186c3508ba7a9a58202e616b23f344a
diff --git a/MachO/Arch/ARM64.cpp b/MachO/Arch/ARM64.cpp
index 55c688b..bbb426a 100644
--- a/MachO/Arch/ARM64.cpp
+++ b/MachO/Arch/ARM64.cpp
@@ -28,7 +28,7 @@
 struct ARM64 : TargetInfo {
   ARM64();
 
-  int64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &,
+  int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,
                             const relocation_info) const override;
   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
                    uint64_t pc) const override;
@@ -77,7 +77,7 @@
   return relocAttrsArray[type];
 }
 
-int64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
+int64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
                                  const relocation_info rel) const {
   if (rel.r_type != ARM64_RELOC_UNSIGNED &&
       rel.r_type != ARM64_RELOC_SUBTRACTOR) {
@@ -88,7 +88,7 @@
   }
 
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
-  const uint8_t *loc = buf + sec.offset + rel.r_address;
+  const uint8_t *loc = buf + offset + rel.r_address;
   switch (rel.r_length) {
   case 2:
     return static_cast<int32_t>(read32le(loc));
@@ -221,7 +221,8 @@
   auto *buf32 = reinterpret_cast<uint32_t *>(buf8);
   uint64_t pcPageBits =
       pageBits(in.stubs->addr + sym.stubsIndex * sizeof(stubCode));
-  uint64_t lazyPointerVA = in.lazyPointers->addr + sym.stubsIndex * WordSize;
+  uint64_t lazyPointerVA =
+      in.lazyPointers->addr + sym.stubsIndex * LP64::wordSize;
   buf32[0] = encodePage21({&sym, "stub"}, stubCode[0],
                           pageBits(lazyPointerVA) - pcPageBits);
   buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA);
@@ -249,7 +250,7 @@
   buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA);
   buf32[2] = stubHelperHeaderCode[2];
   uint64_t binderVA =
-      in.got->addr + in.stubHelper->stubBinder->gotIndex * WordSize;
+      in.got->addr + in.stubHelper->stubBinder->gotIndex * LP64::wordSize;
   buf32[3] = encodePage21(d, stubHelperHeaderCode[3],
                           pageBits(binderVA) - pcPageBits(3));
   buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA);
@@ -291,7 +292,7 @@
   write32le(loc, instruction);
 }
 
-ARM64::ARM64() {
+ARM64::ARM64() : TargetInfo(LP64()) {
   cpuType = CPU_TYPE_ARM64;
   cpuSubtype = CPU_SUBTYPE_ARM64_ALL;
 
diff --git a/MachO/Arch/X86_64.cpp b/MachO/Arch/X86_64.cpp
index 44e7d37..2e1120d 100644
--- a/MachO/Arch/X86_64.cpp
+++ b/MachO/Arch/X86_64.cpp
@@ -25,7 +25,7 @@
 struct X86_64 : TargetInfo {
   X86_64();
 
-  int64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &,
+  int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,
                             const relocation_info) const override;
   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
                    uint64_t relocVA) const override;
@@ -77,10 +77,10 @@
   }
 }
 
-int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec,
+int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
                                   relocation_info rel) const {
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
-  const uint8_t *loc = buf + sec.offset + rel.r_address;
+  const uint8_t *loc = buf + offset + rel.r_address;
 
   switch (rel.r_length) {
   case 2:
@@ -142,7 +142,7 @@
   memcpy(buf, stub, 2); // just copy the two nonzero bytes
   uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
   writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub),
-                   in.lazyPointers->addr + sym.stubsIndex * WordSize);
+                   in.lazyPointers->addr + sym.stubsIndex * LP64::wordSize);
 }
 
 static constexpr uint8_t stubHelperHeader[] = {
@@ -159,7 +159,7 @@
                    in.imageLoaderCache->getVA());
   writeRipRelative(d, buf, in.stubHelper->addr, 0xf,
                    in.got->addr +
-                       in.stubHelper->stubBinder->gotIndex * WordSize);
+                       in.stubHelper->stubBinder->gotIndex * LP64::wordSize);
 }
 
 static constexpr uint8_t stubHelperEntry[] = {
@@ -182,7 +182,7 @@
   loc[-2] = 0x8d;
 }
 
-X86_64::X86_64() {
+X86_64::X86_64() : TargetInfo(LP64()) {
   cpuType = CPU_TYPE_X86_64;
   cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
 
diff --git a/MachO/Driver.cpp b/MachO/Driver.cpp
index befaf67..d0666d6 100644
--- a/MachO/Driver.cpp
+++ b/MachO/Driver.cpp
@@ -1104,7 +1104,11 @@
             "\n>>> referenced from option -exported_symbol(s_list)");
     }
 
-    createSyntheticSections();
+    if (target->wordSize == 8)
+      createSyntheticSections<LP64>();
+    else
+      createSyntheticSections<ILP32>();
+
     createSyntheticSymbols();
 
     for (const Arg *arg : args.filtered(OPT_sectcreate)) {
@@ -1127,7 +1131,10 @@
     }
 
     // Write to an output file.
-    writeResult();
+    if (target->wordSize == 8)
+      writeResult<LP64>();
+    else
+      writeResult<ILP32>();
 
     depTracker->write(getLLDVersion(), inputFiles, config->outputFile);
   }
diff --git a/MachO/DriverUtils.cpp b/MachO/DriverUtils.cpp
index 49bd83e..fe1585f 100644
--- a/MachO/DriverUtils.cpp
+++ b/MachO/DriverUtils.cpp
@@ -10,6 +10,7 @@
 #include "Driver.h"
 #include "InputFiles.h"
 #include "ObjC.h"
+#include "Target.h"
 
 #include "lld/Common/Args.h"
 #include "lld/Common/ErrorHandler.h"
diff --git a/MachO/InputFiles.cpp b/MachO/InputFiles.cpp
index c4db50f..a663a50 100644
--- a/MachO/InputFiles.cpp
+++ b/MachO/InputFiles.cpp
@@ -153,11 +153,12 @@
 InputFile::InputFile(Kind kind, const InterfaceFile &interface)
     : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {}
 
-void ObjFile::parseSections(ArrayRef<section_64> sections) {
+template <class Section>
+void ObjFile::parseSections(ArrayRef<Section> sections) {
   subsections.reserve(sections.size());
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
 
-  for (const section_64 &sec : sections) {
+  for (const Section &sec : sections) {
     InputSection *isec = make<InputSection>();
     isec->file = this;
     isec->name =
@@ -204,7 +205,8 @@
   return it->isec;
 }
 
-static bool validateRelocationInfo(InputFile *file, const section_64 &sec,
+template <class Section>
+static bool validateRelocationInfo(InputFile *file, const Section &sec,
                                    relocation_info rel) {
   const RelocAttrs &relocAttrs = target->getRelocAttrs(rel.r_type);
   bool valid = true;
@@ -235,7 +237,9 @@
   return valid;
 }
 
-void ObjFile::parseRelocations(const section_64 &sec,
+template <class Section>
+void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
+                               const Section &sec,
                                SubsectionMapping &subsecMap) {
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
   ArrayRef<relocation_info> relInfos(
@@ -279,7 +283,7 @@
     if (relInfo.r_address & R_SCATTERED)
       fatal("TODO: Scattered relocations not supported");
 
-    int64_t embeddedAddend = target->getEmbeddedAddend(mb, sec, relInfo);
+    int64_t embeddedAddend = target->getEmbeddedAddend(mb, sec.offset, relInfo);
     assert(!(embeddedAddend && pairedAddend));
     int64_t totalAddend = pairedAddend + embeddedAddend;
     Reloc r;
@@ -293,7 +297,7 @@
     } else {
       SubsectionMapping &referentSubsecMap =
           subsections[relInfo.r_symbolnum - 1];
-      const section_64 &referentSec = sectionHeaders[relInfo.r_symbolnum - 1];
+      const Section &referentSec = sectionHeaders[relInfo.r_symbolnum - 1];
       uint64_t referentOffset;
       if (relInfo.r_pcrel) {
         // The implicit addend for pcrel section relocations is the pcrel offset
@@ -330,9 +334,10 @@
   }
 }
 
-static macho::Symbol *createDefined(const structs::nlist_64 &sym,
-                                    StringRef name, InputSection *isec,
-                                    uint64_t value, uint64_t size) {
+template <class NList>
+static macho::Symbol *createDefined(const NList &sym, StringRef name,
+                                    InputSection *isec, uint64_t value,
+                                    uint64_t size) {
   // Symbol scope is determined by sym.n_type & (N_EXT | N_PEXT):
   // N_EXT: Global symbols
   // N_EXT | N_PEXT: Linkage unit (think: dylib) scoped
@@ -378,8 +383,9 @@
 
 // Absolute symbols are defined symbols that do not have an associated
 // InputSection. They cannot be weak.
-static macho::Symbol *createAbsolute(const structs::nlist_64 &sym,
-                                     InputFile *file, StringRef name) {
+template <class NList>
+static macho::Symbol *createAbsolute(const NList &sym, InputFile *file,
+                                     StringRef name) {
   if (sym.n_type & (N_EXT | N_PEXT)) {
     assert((sym.n_type & N_EXT) && "invalid input");
     return symtab->addDefined(name, file, nullptr, sym.n_value, /*size=*/0,
@@ -390,7 +396,8 @@
                        /*isExternal=*/false, /*isPrivateExtern=*/false);
 }
 
-macho::Symbol *ObjFile::parseNonSectionSymbol(const structs::nlist_64 &sym,
+template <class NList>
+macho::Symbol *ObjFile::parseNonSectionSymbol(const NList &sym,
                                               StringRef name) {
   uint8_t type = sym.n_type & N_TYPE;
   switch (type) {
@@ -414,14 +421,18 @@
   }
 }
 
-void ObjFile::parseSymbols(ArrayRef<structs::nlist_64> nList,
+template <class LP>
+void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
+                           ArrayRef<typename LP::nlist> nList,
                            const char *strtab, bool subsectionsViaSymbols) {
+  using Section = typename LP::section;
+  using NList = typename LP::nlist;
+
   // Precompute the boundaries of symbols within a section.
   // If subsectionsViaSymbols is True then the corresponding subsections will be
   // created, otherwise these boundaries are used for the calculation of symbols
   // sizes only.
-
-  for (const structs::nlist_64 &sym : nList) {
+  for (const NList &sym : nList) {
     if ((sym.n_type & N_TYPE) == N_SECT && !(sym.n_desc & N_ALT_ENTRY) &&
         !subsections[sym.n_sect - 1].empty()) {
       SubsectionMapping &subsectionMapping = subsections[sym.n_sect - 1];
@@ -462,7 +473,7 @@
 
   symbols.resize(nList.size());
   for (size_t i = 0, n = nList.size(); i < n; ++i) {
-    const structs::nlist_64 &sym = nList[i];
+    const NList &sym = nList[i];
     StringRef name = strtab + sym.n_strx;
 
     if ((sym.n_type & N_TYPE) != N_SECT) {
@@ -470,7 +481,7 @@
       continue;
     }
 
-    const section_64 &sec = sectionHeaders[sym.n_sect - 1];
+    const Section &sec = sectionHeaders[sym.n_sect - 1];
     SubsectionMapping &subsecMap = subsections[sym.n_sect - 1];
 
     // parseSections() may have chosen not to parse this section.
@@ -521,9 +532,20 @@
 ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName)
     : InputFile(ObjKind, mb), modTime(modTime) {
   this->archiveName = std::string(archiveName);
+  if (target->wordSize == 8)
+    parse<LP64>();
+  else
+    parse<ILP32>();
+}
+
+template <class LP> void ObjFile::parse() {
+  using Header = typename LP::mach_header;
+  using SegmentCommand = typename LP::segment_command;
+  using Section = typename LP::section;
+  using NList = typename LP::nlist;
 
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
-  auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
+  auto *hdr = reinterpret_cast<const Header *>(mb.getBufferStart());
 
   Architecture arch = getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype);
   if (arch != config->target.Arch) {
@@ -546,28 +568,29 @@
     parseLCLinkerOption(this, c->count, data);
   }
 
-  if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) {
-    auto *c = reinterpret_cast<const segment_command_64 *>(cmd);
-    sectionHeaders = ArrayRef<section_64>{
-        reinterpret_cast<const section_64 *>(c + 1), c->nsects};
+  ArrayRef<Section> sectionHeaders;
+  if (const load_command *cmd = findCommand(hdr, LP::segmentLCType)) {
+    auto *c = reinterpret_cast<const SegmentCommand *>(cmd);
+    sectionHeaders =
+        ArrayRef<Section>{reinterpret_cast<const Section *>(c + 1), c->nsects};
     parseSections(sectionHeaders);
   }
 
   // TODO: Error on missing LC_SYMTAB?
   if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) {
     auto *c = reinterpret_cast<const symtab_command *>(cmd);
-    ArrayRef<structs::nlist_64> nList(
-        reinterpret_cast<const structs::nlist_64 *>(buf + c->symoff), c->nsyms);
+    ArrayRef<NList> nList(reinterpret_cast<const NList *>(buf + c->symoff),
+                          c->nsyms);
     const char *strtab = reinterpret_cast<const char *>(buf) + c->stroff;
     bool subsectionsViaSymbols = hdr->flags & MH_SUBSECTIONS_VIA_SYMBOLS;
-    parseSymbols(nList, strtab, subsectionsViaSymbols);
+    parseSymbols<LP>(sectionHeaders, nList, strtab, subsectionsViaSymbols);
   }
 
   // The relocations may refer to the symbols, so we parse them after we have
   // parsed all the symbols.
   for (size_t i = 0, n = subsections.size(); i < n; ++i)
     if (!subsections[i].empty())
-      parseRelocations(sectionHeaders[i], subsections[i]);
+      parseRelocations(sectionHeaders, sectionHeaders[i], subsections[i]);
 
   parseDebugInfo();
 }
@@ -678,8 +701,16 @@
   if (umbrella == nullptr)
     umbrella = this;
 
+  if (target->wordSize == 8)
+    parse<LP64>(umbrella);
+  else
+    parse<ILP32>(umbrella);
+}
+
+template <class LP> void DylibFile::parse(DylibFile *umbrella) {
+  using Header = typename LP::mach_header;
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
-  auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
+  auto *hdr = reinterpret_cast<const Header *>(mb.getBufferStart());
 
   // Initialize dylibName.
   if (const load_command *cmd = findCommand(hdr, LC_ID_DYLIB)) {
@@ -716,8 +747,7 @@
     return;
   }
 
-  const uint8_t *p =
-      reinterpret_cast<const uint8_t *>(hdr) + sizeof(mach_header_64);
+  const uint8_t *p = reinterpret_cast<const uint8_t *>(hdr) + sizeof(Header);
   for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
     auto *cmd = reinterpret_cast<const load_command *>(p);
     p += cmd->cmdsize;
@@ -888,3 +918,6 @@
   for (const lto::InputFile::Symbol &objSym : obj->symbols())
     symbols.push_back(createBitcodeSymbol(objSym, *this));
 }
+
+template void ObjFile::parse<LP64>();
+template void DylibFile::parse<LP64>(DylibFile *umbrella);
diff --git a/MachO/InputFiles.h b/MachO/InputFiles.h
index 5b301d3..d9b3479 100644
--- a/MachO/InputFiles.h
+++ b/MachO/InputFiles.h
@@ -101,15 +101,20 @@
 
   llvm::DWARFUnit *compileUnit = nullptr;
   const uint32_t modTime;
-  ArrayRef<llvm::MachO::section_64> sectionHeaders;
   std::vector<InputSection *> debugSections;
 
 private:
-  void parseSections(ArrayRef<llvm::MachO::section_64>);
-  void parseSymbols(ArrayRef<lld::structs::nlist_64> nList, const char *strtab,
+  template <class LP> void parse();
+  template <class Section> void parseSections(ArrayRef<Section>);
+  template <class LP>
+  void parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
+                    ArrayRef<typename LP::nlist> nList, const char *strtab,
                     bool subsectionsViaSymbols);
-  Symbol *parseNonSectionSymbol(const structs::nlist_64 &sym, StringRef name);
-  void parseRelocations(const llvm::MachO::section_64 &, SubsectionMapping &);
+  template <class NList>
+  Symbol *parseNonSectionSymbol(const NList &sym, StringRef name);
+  template <class Section>
+  void parseRelocations(ArrayRef<Section> sectionHeaders, const Section &,
+                        SubsectionMapping &);
   void parseDebugInfo();
 };
 
@@ -130,7 +135,7 @@
   // the root dylib to ensure symbols in the child library are correctly bound
   // to the root. On the other hand, if a dylib is being directly loaded
   // (through an -lfoo flag), then `umbrella` should be a nullptr.
-  explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella = nullptr,
+  explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
                      bool isBundleLoader = false);
 
   explicit DylibFile(const llvm::MachO::InterfaceFile &interface,
@@ -152,6 +157,9 @@
   // implemented in the bundle. When used like this, it is very similar
   // to a Dylib, so we re-used the same class to represent it.
   bool isBundleLoader;
+
+private:
+  template <class LP> void parse(DylibFile *umbrella = nullptr);
 };
 
 // .a file
@@ -180,11 +188,9 @@
 
 llvm::Optional<MemoryBufferRef> readFile(StringRef path);
 
-template <class CommandType = llvm::MachO::load_command>
-const CommandType *findCommand(const llvm::MachO::mach_header_64 *hdr,
-                               uint32_t type) {
-  const uint8_t *p = reinterpret_cast<const uint8_t *>(hdr) +
-                     sizeof(llvm::MachO::mach_header_64);
+template <class CommandType = llvm::MachO::load_command, class Header>
+const CommandType *findCommand(const Header *hdr, uint32_t type) {
+  const uint8_t *p = reinterpret_cast<const uint8_t *>(hdr) + sizeof(Header);
 
   for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
     auto *cmd = reinterpret_cast<const CommandType *>(p);
diff --git a/MachO/InputSection.cpp b/MachO/InputSection.cpp
index a925449..70a3a85 100644
--- a/MachO/InputSection.cpp
+++ b/MachO/InputSection.cpp
@@ -41,10 +41,10 @@
       return in.stubs->addr + sym.stubsIndex * target->stubSize;
   } else if (relocAttrs.hasAttr(RelocAttrBits::GOT)) {
     if (sym.isInGot())
-      return in.got->addr + sym.gotIndex * WordSize;
+      return in.got->addr + sym.gotIndex * target->wordSize;
   } else if (relocAttrs.hasAttr(RelocAttrBits::TLV)) {
     if (sym.isInGot())
-      return in.tlvPointers->addr + sym.gotIndex * WordSize;
+      return in.tlvPointers->addr + sym.gotIndex * target->wordSize;
     assert(isa<Defined>(&sym));
   }
   return sym.getVA();
diff --git a/MachO/LTO.cpp b/MachO/LTO.cpp
index bb42189..7b55835 100644
--- a/MachO/LTO.cpp
+++ b/MachO/LTO.cpp
@@ -11,6 +11,7 @@
 #include "Driver.h"
 #include "InputFiles.h"
 #include "Symbols.h"
+#include "Target.h"
 
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Strings.h"
diff --git a/MachO/MachOStructs.h b/MachO/MachOStructs.h
index 69b50ec..6d9cc49 100644
--- a/MachO/MachOStructs.h
+++ b/MachO/MachOStructs.h
@@ -29,6 +29,14 @@
   llvm::support::ulittle64_t n_value;
 };
 
+struct nlist {
+  llvm::support::ulittle32_t n_strx;
+  uint8_t n_type;
+  uint8_t n_sect;
+  llvm::support::ulittle16_t n_desc;
+  llvm::support::ulittle32_t n_value;
+};
+
 } // namespace structs
 
 } // namespace lld
diff --git a/MachO/ObjC.cpp b/MachO/ObjC.cpp
index 21691ef..6a341b2 100644
--- a/MachO/ObjC.cpp
+++ b/MachO/ObjC.cpp
@@ -9,20 +9,25 @@
 #include "ObjC.h"
 #include "InputFiles.h"
 #include "OutputSegment.h"
+#include "Target.h"
 
 #include "llvm/BinaryFormat/MachO.h"
 
 using namespace llvm;
 using namespace llvm::MachO;
 using namespace lld;
+using namespace lld::macho;
 
-bool macho::hasObjCSection(MemoryBufferRef mb) {
-  auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
-  if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) {
-    auto *c = reinterpret_cast<const segment_command_64 *>(cmd);
-    auto sectionHeaders = ArrayRef<section_64>{
-        reinterpret_cast<const section_64 *>(c + 1), c->nsects};
-    for (const section_64 &sec : sectionHeaders) {
+template <class LP> static bool hasObjCSection(MemoryBufferRef mb) {
+  using Section = typename LP::section;
+
+  auto *hdr =
+      reinterpret_cast<const typename LP::mach_header *>(mb.getBufferStart());
+  if (const load_command *cmd = findCommand(hdr, LP::segmentLCType)) {
+    auto *c = reinterpret_cast<const typename LP::segment_command *>(cmd);
+    auto sectionHeaders =
+        ArrayRef<Section>{reinterpret_cast<const Section *>(c + 1), c->nsects};
+    for (const Section &sec : sectionHeaders) {
       StringRef sectname(sec.sectname,
                          strnlen(sec.sectname, sizeof(sec.sectname)));
       StringRef segname(sec.segname, strnlen(sec.segname, sizeof(sec.segname)));
@@ -34,3 +39,10 @@
   }
   return false;
 }
+
+bool macho::hasObjCSection(MemoryBufferRef mb) {
+  if (target->wordSize == 8)
+    return ::hasObjCSection<LP64>(mb);
+  else
+    return ::hasObjCSection<ILP32>(mb);
+}
diff --git a/MachO/SyntheticSections.cpp b/MachO/SyntheticSections.cpp
index 341802a..63ea14d 100644
--- a/MachO/SyntheticSections.cpp
+++ b/MachO/SyntheticSections.cpp
@@ -65,8 +65,21 @@
   sizeOfCmds += lc->getSize();
 }
 
-uint64_t MachHeaderSection::getSize() const {
-  return sizeof(mach_header_64) + sizeOfCmds + config->headerPad;
+// This serves to hide (type-erase) the template parameter from
+// MachHeaderSection.
+template <class LP> class MachHeaderSectionImpl : public MachHeaderSection {
+public:
+  MachHeaderSectionImpl() = default;
+  uint64_t getSize() const override;
+  void writeTo(uint8_t *buf) const override;
+};
+
+template <class LP> MachHeaderSection *macho::makeMachHeaderSection() {
+  return make<MachHeaderSectionImpl<LP>>();
+}
+
+template <class LP> uint64_t MachHeaderSectionImpl<LP>::getSize() const {
+  return sizeof(typename LP::mach_header) + sizeOfCmds + config->headerPad;
 }
 
 static uint32_t cpuSubtype() {
@@ -81,9 +94,10 @@
   return subtype;
 }
 
-void MachHeaderSection::writeTo(uint8_t *buf) const {
-  auto *hdr = reinterpret_cast<mach_header_64 *>(buf);
-  hdr->magic = MH_MAGIC_64;
+template <class LP>
+void MachHeaderSectionImpl<LP>::writeTo(uint8_t *buf) const {
+  auto *hdr = reinterpret_cast<typename LP::mach_header *>(buf);
+  hdr->magic = LP::magic;
   hdr->cputype = target->cpuType;
   hdr->cpusubtype = cpuSubtype();
   hdr->filetype = config->outputType;
@@ -177,7 +191,7 @@
   }
   ++lastRebase.consecutiveCount;
   // DO_REBASE causes dyld to both perform the binding and increment the offset
-  lastRebase.offset += WordSize;
+  lastRebase.offset += target->wordSize;
 }
 
 void RebaseSection::finalizeContents() {
@@ -208,7 +222,7 @@
 NonLazyPointerSectionBase::NonLazyPointerSectionBase(const char *segname,
                                                      const char *name)
     : SyntheticSection(segname, name) {
-  align = WordSize;
+  align = target->wordSize;
   flags = S_NON_LAZY_SYMBOL_POINTERS;
 }
 
@@ -235,14 +249,14 @@
     assert(!sym->isInGot());
     sym->gotIndex = entries.size() - 1;
 
-    addNonLazyBindingEntries(sym, isec, sym->gotIndex * WordSize);
+    addNonLazyBindingEntries(sym, isec, sym->gotIndex * target->wordSize);
   }
 }
 
 void NonLazyPointerSectionBase::writeTo(uint8_t *buf) const {
   for (size_t i = 0, n = entries.size(); i < n; ++i)
     if (auto *defined = dyn_cast<Defined>(entries[i]))
-      write64le(&buf[i * WordSize], defined->getVA());
+      write64le(&buf[i * target->wordSize], defined->getVA());
 }
 
 BindingSection::BindingSection()
@@ -295,7 +309,7 @@
      << static_cast<uint8_t>(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER)
      << static_cast<uint8_t>(BIND_OPCODE_DO_BIND);
   // DO_BIND causes dyld to both perform the binding and increment the offset
-  lastBinding.offset += WordSize;
+  lastBinding.offset += target->wordSize;
 }
 
 // Non-weak bindings need to have their dylib ordinal encoded as well.
@@ -463,20 +477,20 @@
 ImageLoaderCacheSection::ImageLoaderCacheSection() {
   segname = segment_names::data;
   name = "__data";
-  uint8_t *arr = bAlloc.Allocate<uint8_t>(WordSize);
-  memset(arr, 0, WordSize);
-  data = {arr, WordSize};
-  align = WordSize;
+  uint8_t *arr = bAlloc.Allocate<uint8_t>(target->wordSize);
+  memset(arr, 0, target->wordSize);
+  data = {arr, target->wordSize};
+  align = target->wordSize;
 }
 
 LazyPointerSection::LazyPointerSection()
     : SyntheticSection(segment_names::data, "__la_symbol_ptr") {
-  align = WordSize;
+  align = target->wordSize;
   flags = S_LAZY_SYMBOL_POINTERS;
 }
 
 uint64_t LazyPointerSection::getSize() const {
-  return in.stubs->getEntries().size() * WordSize;
+  return in.stubs->getEntries().size() * target->wordSize;
 }
 
 bool LazyPointerSection::isNeeded() const {
@@ -496,7 +510,7 @@
     } else {
       write64le(buf + off, sym->getVA());
     }
-    off += WordSize;
+    off += target->wordSize;
   }
 }
 
@@ -517,7 +531,8 @@
 void LazyBindingSection::addEntry(DylibSymbol *dysym) {
   if (entries.insert(dysym)) {
     dysym->stubsHelperIndex = entries.size() - 1;
-    in.rebase->addEntry(in.lazyPointers->isec, dysym->stubsIndex * WordSize);
+    in.rebase->addEntry(in.lazyPointers->isec,
+                        dysym->stubsIndex * target->wordSize);
   }
 }
 
@@ -533,7 +548,7 @@
   os << static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
                              dataSeg->index);
   uint64_t offset = in.lazyPointers->addr - dataSeg->firstSection()->addr +
-                    sym.stubsIndex * WordSize;
+                    sym.stubsIndex * target->wordSize;
   encodeULEB128(offset, os);
   encodeDylibOrdinal(ordinalForDylibSymbol(sym), os);
 
@@ -621,10 +636,6 @@
     : LinkEditSection(segment_names::linkEdit, section_names::symbolTable),
       stringTableSection(stringTableSection) {}
 
-uint64_t SymtabSection::getRawSize() const {
-  return getNumSymbols() * sizeof(structs::nlist_64);
-}
-
 void SymtabSection::emitBeginSourceStab(DWARFUnit *compileUnit) {
   StabsEntry stab(N_SO);
   SmallString<261> dir(compileUnit->getCompilationDir());
@@ -781,8 +792,21 @@
          undefinedSymbols.size();
 }
 
-void SymtabSection::writeTo(uint8_t *buf) const {
-  auto *nList = reinterpret_cast<structs::nlist_64 *>(buf);
+// This serves to hide (type-erase) the template parameter from SymtabSection.
+template <class LP> class SymtabSectionImpl : public SymtabSection {
+public:
+  SymtabSectionImpl(StringTableSection &stringTableSection)
+      : SymtabSection(stringTableSection) {}
+  uint64_t getRawSize() const override;
+  void writeTo(uint8_t *buf) const override;
+};
+
+template <class LP> uint64_t SymtabSectionImpl<LP>::getRawSize() const {
+  return getNumSymbols() * sizeof(typename LP::nlist);
+}
+
+template <class LP> void SymtabSectionImpl<LP>::writeTo(uint8_t *buf) const {
+  auto *nList = reinterpret_cast<typename LP::nlist *>(buf);
   // Emit the stabs entries before the "real" symbols. We cannot emit them
   // after as that would render Symbol::symtabIndex inaccurate.
   for (const StabsEntry &entry : stabs) {
@@ -845,6 +869,12 @@
   }
 }
 
+template <class LP>
+SymtabSection *
+macho::makeSymtabSection(StringTableSection &stringTableSection) {
+  return make<SymtabSectionImpl<LP>>(stringTableSection);
+}
+
 IndirectSymtabSection::IndirectSymtabSection()
     : LinkEditSection(segment_names::linkEdit,
                       section_names::indirectSymbolTable) {}
@@ -1050,3 +1080,8 @@
   // so that's what's implemented here.
   addHeaderSymbol("___dso_handle");
 }
+
+template MachHeaderSection *macho::makeMachHeaderSection<LP64>();
+template MachHeaderSection *macho::makeMachHeaderSection<ILP32>();
+template SymtabSection *macho::makeSymtabSection<LP64>(StringTableSection &);
+template SymtabSection *macho::makeSymtabSection<ILP32>(StringTableSection &);
diff --git a/MachO/SyntheticSections.h b/MachO/SyntheticSections.h
index 8674e54..632a8ba 100644
--- a/MachO/SyntheticSections.h
+++ b/MachO/SyntheticSections.h
@@ -53,7 +53,7 @@
 public:
   LinkEditSection(const char *segname, const char *name)
       : SyntheticSection(segname, name) {
-    align = WordSize; // mimic ld64
+    align = target->wordSize;
   }
 
   // Sections in __LINKEDIT are special: their offsets are recorded in the
@@ -77,24 +77,24 @@
 // The header of the Mach-O file, which must have a file offset of zero.
 class MachHeaderSection : public SyntheticSection {
 public:
-  MachHeaderSection();
   void addLoadCommand(LoadCommand *);
   bool isHidden() const override { return true; }
-  uint64_t getSize() const override;
-  void writeTo(uint8_t *buf) const override;
 
-private:
+protected:
+  MachHeaderSection();
   std::vector<LoadCommand *> loadCommands;
   uint32_t sizeOfCmds = 0;
 };
 
+template <class LP> MachHeaderSection *makeMachHeaderSection();
+
 // A hidden section that exists solely for the purpose of creating the
 // __PAGEZERO segment, which is used to catch null pointer dereferences.
 class PageZeroSection : public SyntheticSection {
 public:
   PageZeroSection();
   bool isHidden() const override { return true; }
-  uint64_t getSize() const override { return PageZeroSize; }
+  uint64_t getSize() const override { return target->pageZeroSize; }
   uint64_t getFileSize() const override { return 0; }
   void writeTo(uint8_t *buf) const override {}
 };
@@ -111,7 +111,9 @@
 
   bool isNeeded() const override { return !entries.empty(); }
 
-  uint64_t getSize() const override { return entries.size() * WordSize; }
+  uint64_t getSize() const override {
+    return entries.size() * target->wordSize;
+  }
 
   void writeTo(uint8_t *buf) const override;
 
@@ -309,7 +311,7 @@
 class ImageLoaderCacheSection : public InputSection {
 public:
   ImageLoaderCacheSection();
-  uint64_t getSize() const override { return WordSize; }
+  uint64_t getSize() const override { return target->wordSize; }
 };
 
 // Note that this section may also be targeted by non-lazy bindings. In
@@ -406,7 +408,6 @@
 // range (start index and total number) of those symbols in the symbol table.
 class SymtabSection : public LinkEditSection {
 public:
-  SymtabSection(StringTableSection &);
   void finalizeContents();
   uint32_t getNumSymbols() const;
   uint32_t getNumLocalSymbols() const {
@@ -414,8 +415,6 @@
   }
   uint32_t getNumExternalSymbols() const { return externalSymbols.size(); }
   uint32_t getNumUndefinedSymbols() const { return undefinedSymbols.size(); }
-  uint64_t getRawSize() const override;
-  void writeTo(uint8_t *buf) const override;
 
 private:
   void emitBeginSourceStab(llvm::DWARFUnit *compileUnit);
@@ -424,6 +423,9 @@
   void emitEndFunStab(Defined *);
   void emitStabs();
 
+protected:
+  SymtabSection(StringTableSection &);
+
   StringTableSection &stringTableSection;
   // STABS symbols are always local symbols, but we represent them with special
   // entries because they may use fields like n_sect and n_desc differently.
@@ -433,6 +435,8 @@
   std::vector<SymtabEntry> undefinedSymbols;
 };
 
+template <class LP> SymtabSection *makeSymtabSection(StringTableSection &);
+
 // The indirect symbol table is a list of 32-bit integers that serve as indices
 // into the (actual) symbol table. The indirect symbol table is a
 // concatenation of several sub-arrays of indices, each sub-array belonging to
diff --git a/MachO/Target.h b/MachO/Target.h
index 2e4329b..9eed70b 100644
--- a/MachO/Target.h
+++ b/MachO/Target.h
@@ -9,6 +9,7 @@
 #ifndef LLD_MACHO_TARGET_H
 #define LLD_MACHO_TARGET_H
 
+#include "MachOStructs.h"
 #include "Relocations.h"
 
 #include "llvm/ADT/BitmaskEnum.h"
@@ -26,21 +27,20 @@
 class DylibSymbol;
 class InputSection;
 
-enum : uint64_t {
-  // We are currently only supporting 64-bit targets since macOS and iOS are
-  // deprecating 32-bit apps.
-  WordSize = 8,
-  PageZeroSize = 1ull << 32, // XXX should be 4096 for 32-bit targets
-  MaxAlignmentPowerOf2 = 32,
-};
-
 class TargetInfo {
 public:
+  template <class LP> TargetInfo(LP) {
+    // Having these values available in TargetInfo allows us to access them
+    // without having to resort to templates.
+    pageZeroSize = LP::pageZeroSize;
+    wordSize = LP::wordSize;
+  }
+
   virtual ~TargetInfo() = default;
 
   // Validate the relocation structure and get its addend.
   virtual int64_t
-  getEmbeddedAddend(llvm::MemoryBufferRef, const llvm::MachO::section_64 &,
+  getEmbeddedAddend(llvm::MemoryBufferRef, uint64_t offset,
                     const llvm::MachO::relocation_info) const = 0;
   virtual void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
                            uint64_t relocVA) const = 0;
@@ -70,14 +70,42 @@
   uint32_t cpuType;
   uint32_t cpuSubtype;
 
+  size_t pageZeroSize;
   size_t stubSize;
   size_t stubHelperHeaderSize;
   size_t stubHelperEntrySize;
+  size_t wordSize;
 };
 
 TargetInfo *createX86_64TargetInfo();
 TargetInfo *createARM64TargetInfo();
 
+struct LP64 {
+  using mach_header = llvm::MachO::mach_header_64;
+  using nlist = structs::nlist_64;
+  using segment_command = llvm::MachO::segment_command_64;
+  using section = llvm::MachO::section_64;
+
+  static constexpr uint32_t magic = llvm::MachO::MH_MAGIC_64;
+  static constexpr uint32_t segmentLCType = llvm::MachO::LC_SEGMENT_64;
+
+  static constexpr size_t pageZeroSize = 1ull << 32;
+  static constexpr size_t wordSize = 8;
+};
+
+struct ILP32 {
+  using mach_header = llvm::MachO::mach_header;
+  using nlist = structs::nlist;
+  using segment_command = llvm::MachO::segment_command;
+  using section = llvm::MachO::section;
+
+  static constexpr uint32_t magic = llvm::MachO::MH_MAGIC;
+  static constexpr uint32_t segmentLCType = llvm::MachO::LC_SEGMENT;
+
+  static constexpr size_t pageZeroSize = 1ull << 12;
+  static constexpr size_t wordSize = 4;
+};
+
 extern TargetInfo *target;
 
 } // namespace macho
diff --git a/MachO/UnwindInfoSection.cpp b/MachO/UnwindInfoSection.cpp
index 949d16f..52abdc6 100644
--- a/MachO/UnwindInfoSection.cpp
+++ b/MachO/UnwindInfoSection.cpp
@@ -403,7 +403,7 @@
 
   // Personalities
   for (const uint32_t &personality : personalities)
-    *i32p++ = in.got->addr + (personality - 1) * WordSize;
+    *i32p++ = in.got->addr + (personality - 1) * target->wordSize;
 
   // Level-1 index
   uint32_t lsdaOffset =
diff --git a/MachO/Writer.cpp b/MachO/Writer.cpp
index a3b307a..01f6d4f 100644
--- a/MachO/Writer.cpp
+++ b/MachO/Writer.cpp
@@ -49,8 +49,8 @@
 
   void scanRelocations();
   void scanSymbols();
-  void createOutputSections();
-  void createLoadCommands();
+  template <class LP> void createOutputSections();
+  template <class LP> void createLoadCommands();
   void finalizeAddressses();
   void finalizeLinkEditSegment();
   void assignAddresses(OutputSegment *);
@@ -61,7 +61,7 @@
   void writeCodeSignature();
   void writeOutputFile();
 
-  void run();
+  template <class LP> void run();
 
   std::unique_ptr<FileOutputBuffer> &buffer;
   uint64_t addr = 0;
@@ -171,20 +171,23 @@
   IndirectSymtabSection *indirectSymtabSection;
 };
 
-class LCSegment : public LoadCommand {
+template <class LP> class LCSegment : public LoadCommand {
 public:
   LCSegment(StringRef name, OutputSegment *seg) : name(name), seg(seg) {}
 
-  uint32_t getSize() const override {
-    return sizeof(segment_command_64) +
-           seg->numNonHiddenSections() * sizeof(section_64);
+  uint32_t getSize() const {
+    return sizeof(typename LP::segment_command) +
+           seg->numNonHiddenSections() * sizeof(typename LP::section);
   }
 
-  void writeTo(uint8_t *buf) const override {
-    auto *c = reinterpret_cast<segment_command_64 *>(buf);
-    buf += sizeof(segment_command_64);
+  void writeTo(uint8_t *buf) const {
+    using SegmentCommand = typename LP::segment_command;
+    using Section = typename LP::section;
 
-    c->cmd = LC_SEGMENT_64;
+    auto *c = reinterpret_cast<SegmentCommand *>(buf);
+    buf += sizeof(SegmentCommand);
+
+    c->cmd = LP::segmentLCType;
     c->cmdsize = getSize();
     memcpy(c->segname, name.data(), name.size());
     c->fileoff = seg->fileOff;
@@ -202,15 +205,15 @@
     for (const OutputSection *osec : seg->getSections()) {
       if (!isZeroFill(osec->flags)) {
         assert(osec->fileOff >= seg->fileOff);
-        c->filesize = std::max(
+        c->filesize = std::max<uint64_t>(
             c->filesize, osec->fileOff + osec->getFileSize() - seg->fileOff);
       }
 
       if (osec->isHidden())
         continue;
 
-      auto *sectHdr = reinterpret_cast<section_64 *>(buf);
-      buf += sizeof(section_64);
+      auto *sectHdr = reinterpret_cast<Section *>(buf);
+      buf += sizeof(Section);
 
       memcpy(sectHdr->sectname, osec->name.data(), osec->name.size());
       memcpy(sectHdr->segname, name.data(), name.size());
@@ -342,7 +345,7 @@
   LCRPath(StringRef path) : path(path) {}
 
   uint32_t getSize() const override {
-    return alignTo(sizeof(rpath_command) + path.size() + 1, WordSize);
+    return alignTo(sizeof(rpath_command) + path.size() + 1, target->wordSize);
   }
 
   void writeTo(uint8_t *buf) const override {
@@ -459,9 +462,9 @@
     if (in.stubs->addEntry(dysym)) {
       if (sym->isWeakDef()) {
         in.binding->addEntry(dysym, in.lazyPointers->isec,
-                             sym->stubsIndex * WordSize);
+                             sym->stubsIndex * target->wordSize);
         in.weakBinding->addEntry(sym, in.lazyPointers->isec,
-                                 sym->stubsIndex * WordSize);
+                                 sym->stubsIndex * target->wordSize);
       } else {
         in.lazyBinding->addEntry(dysym);
       }
@@ -469,9 +472,10 @@
   } else if (auto *defined = dyn_cast<Defined>(sym)) {
     if (defined->isExternalWeakDef()) {
       if (in.stubs->addEntry(sym)) {
-        in.rebase->addEntry(in.lazyPointers->isec, sym->stubsIndex * WordSize);
+        in.rebase->addEntry(in.lazyPointers->isec,
+                            sym->stubsIndex * target->wordSize);
         in.weakBinding->addEntry(sym, in.lazyPointers->isec,
-                                 sym->stubsIndex * WordSize);
+                                 sym->stubsIndex * target->wordSize);
       }
     }
   }
@@ -555,10 +559,10 @@
   }
 }
 
-void Writer::createLoadCommands() {
+template <class LP> void Writer::createLoadCommands() {
   uint8_t segIndex = 0;
   for (OutputSegment *seg : outputSegments) {
-    in.header->addLoadCommand(make<LCSegment>(seg->name, seg));
+    in.header->addLoadCommand(make<LCSegment<LP>>(seg->name, seg));
     seg->index = segIndex++;
   }
 
@@ -788,12 +792,12 @@
   return key;
 }
 
-void Writer::createOutputSections() {
+template <class LP> void Writer::createOutputSections() {
   TimeTraceScope timeScope("Create output sections");
   // First, create hidden sections
   stringTableSection = make<StringTableSection>();
   unwindInfoSection = make<UnwindInfoSection>(); // TODO(gkm): only when no -r
-  symtabSection = make<SymtabSection>(*stringTableSection);
+  symtabSection = makeSymtabSection<LP>(*stringTableSection);
   indirectSymtabSection = make<IndirectSymtabSection>();
   if (config->adhocCodesign)
     codeSignatureSection = make<CodeSignatureSection>();
@@ -958,26 +962,26 @@
     error("failed to write to the output file: " + toString(std::move(e)));
 }
 
-void Writer::run() {
+template <class LP> void Writer::run() {
   prepareBranchTarget(config->entry);
   scanRelocations();
   if (in.stubHelper->isNeeded())
     in.stubHelper->setup();
   scanSymbols();
-  createOutputSections();
+  createOutputSections<LP>();
   // No more sections nor segments are created beyond this point.
   sortSegmentsAndSections();
-  createLoadCommands();
+  createLoadCommands<LP>();
   finalizeAddressses();
   finalizeLinkEditSegment();
   writeMapFile();
   writeOutputFile();
 }
 
-void macho::writeResult() { Writer().run(); }
+template <class LP> void macho::writeResult() { Writer().run<LP>(); }
 
-void macho::createSyntheticSections() {
-  in.header = make<MachHeaderSection>();
+template <class LP> void macho::createSyntheticSections() {
+  in.header = makeMachHeaderSection<LP>();
   in.rebase = make<RebaseSection>();
   in.binding = make<BindingSection>();
   in.weakBinding = make<WeakBindingSection>();
@@ -992,3 +996,8 @@
 }
 
 OutputSection *macho::firstTLVDataSection = nullptr;
+
+template void macho::writeResult<LP64>();
+template void macho::writeResult<ILP32>();
+template void macho::createSyntheticSections<LP64>();
+template void macho::createSyntheticSections<ILP32>();
diff --git a/MachO/Writer.h b/MachO/Writer.h
index a13b5ce..7369a6d 100644
--- a/MachO/Writer.h
+++ b/MachO/Writer.h
@@ -25,9 +25,9 @@
   virtual void writeTo(uint8_t *buf) const = 0;
 };
 
-void writeResult();
+template <class LP> void writeResult();
 
-void createSyntheticSections();
+template <class LP> void createSyntheticSections();
 
 // Add bindings for symbols that need weak or non-lazy bindings.
 void addNonLazyBindingEntries(const Symbol *, const InputSection *,