MC/Mach-O: Lift relocation emission logic a bit higher to separate evaluation / reloc...
[oota-llvm.git] / lib / MC / MCAssembler.cpp
index 29ac8684bde3a0a3197b6d1c92f611661af45b1b..7b4512e7d4e71b1249590d3901b3c45eb4b26f1f 100644 (file)
@@ -24,6 +24,7 @@
 #include "llvm/Support/MachO.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Target/TargetRegistry.h"
 #include "llvm/Target/TargetAsmBackend.h"
 
 // FIXME: Gross.
@@ -51,8 +52,7 @@ static uint64_t WriteNopData(uint64_t Count, MachObjectWriter &MOW);
 static bool isVirtualSection(const MCSection &Section) {
   // FIXME: Lame.
   const MCSectionMachO &SMO = static_cast<const MCSectionMachO&>(Section);
-  unsigned Type = SMO.getTypeAndAttributes() & MCSectionMachO::SECTION_TYPE;
-  return (Type == MCSectionMachO::S_ZEROFILL);
+  return (SMO.getType() == MCSectionMachO::S_ZEROFILL);
 }
 
 static unsigned getFixupKindLog2Size(unsigned Kind) {
@@ -86,14 +86,19 @@ class MachObjectWriter {
     Header_Magic64 = 0xFEEDFACF
   };
 
-  static const unsigned Header32Size = 28;
-  static const unsigned Header64Size = 32;
-  static const unsigned SegmentLoadCommand32Size = 56;
-  static const unsigned Section32Size = 68;
-  static const unsigned SymtabLoadCommandSize = 24;
-  static const unsigned DysymtabLoadCommandSize = 80;
-  static const unsigned Nlist32Size = 12;
-  static const unsigned RelocationInfoSize = 8;
+  enum {
+    Header32Size = 28,
+    Header64Size = 32,
+    SegmentLoadCommand32Size = 56,
+    SegmentLoadCommand64Size = 72,
+    Section32Size = 68,
+    Section64Size = 80,
+    SymtabLoadCommandSize = 24,
+    DysymtabLoadCommandSize = 80,
+    Nlist32Size = 12,
+    Nlist64Size = 16,
+    RelocationInfoSize = 8
+  };
 
   enum HeaderFileType {
     HFT_Object = 0x1
@@ -106,7 +111,8 @@ class MachObjectWriter {
   enum LoadCommandType {
     LCT_Segment = 0x1,
     LCT_Symtab = 0x2,
-    LCT_Dysymtab = 0xb
+    LCT_Dysymtab = 0xb,
+    LCT_Segment64 = 0x19
   };
 
   // See <mach-o/nlist.h>.
@@ -161,11 +167,33 @@ class MachObjectWriter {
   };
 
   raw_ostream &OS;
-  bool IsLSB;
+  unsigned Is64Bit : 1;
+  unsigned IsLSB : 1;
+
+  /// @name Relocation Data
+  /// @{
+
+  struct MachRelocationEntry {
+    uint32_t Word0;
+    uint32_t Word1;
+  };
+
+  llvm::DenseMap<const MCSectionData*,
+                 std::vector<MachRelocationEntry> > Relocations;
+
+  /// @}
+  /// @name Symbol Table Data
+
+  SmallString<256> StringTable;
+  std::vector<MachSymbolData> LocalSymbolData;
+  std::vector<MachSymbolData> ExternalSymbolData;
+  std::vector<MachSymbolData> UndefinedSymbolData;
+
+  /// @}
 
 public:
-  MachObjectWriter(raw_ostream &_OS, bool _IsLSB = true)
-    : OS(_OS), IsLSB(_IsLSB) {
+  MachObjectWriter(raw_ostream &_OS, bool _Is64Bit, bool _IsLSB = true)
+    : OS(_OS), Is64Bit(_Is64Bit), IsLSB(_IsLSB) {
   }
 
   /// @name Helper Methods
@@ -222,22 +250,23 @@ public:
 
   /// @}
 
-  void WriteHeader32(unsigned NumLoadCommands, unsigned LoadCommandsSize,
-                     bool SubsectionsViaSymbols) {
+  void WriteHeader(unsigned NumLoadCommands, unsigned LoadCommandsSize,
+                   bool SubsectionsViaSymbols) {
     uint32_t Flags = 0;
 
     if (SubsectionsViaSymbols)
       Flags |= HF_SubsectionsViaSymbols;
 
-    // struct mach_header (28 bytes)
+    // struct mach_header (28 bytes) or
+    // struct mach_header_64 (32 bytes)
 
     uint64_t Start = OS.tell();
     (void) Start;
 
-    Write32(Header_Magic32);
+    Write32(Is64Bit ? Header_Magic64 : Header_Magic32);
 
     // FIXME: Support cputype.
-    Write32(MachO::CPUTypeI386);
+    Write32(Is64Bit ? MachO::CPUTypeX86_64 : MachO::CPUTypeI386);
     // FIXME: Support cpusubtype.
     Write32(MachO::CPUSubType_I386_ALL);
     Write32(HFT_Object);
@@ -245,48 +274,62 @@ public:
                                  // segment.
     Write32(LoadCommandsSize);
     Write32(Flags);
+    if (Is64Bit)
+      Write32(0); // reserved
 
-    assert(OS.tell() - Start == Header32Size);
+    assert(OS.tell() - Start == Is64Bit ? Header64Size : Header32Size);
   }
 
-  /// WriteSegmentLoadCommand32 - Write a 32-bit segment load command.
+  /// WriteSegmentLoadCommand - Write a segment load command.
   ///
   /// \arg NumSections - The number of sections in this segment.
   /// \arg SectionDataSize - The total size of the sections.
-  void WriteSegmentLoadCommand32(unsigned NumSections,
-                                 uint64_t VMSize,
-                                 uint64_t SectionDataStartOffset,
-                                 uint64_t SectionDataSize) {
-    // struct segment_command (56 bytes)
+  void WriteSegmentLoadCommand(unsigned NumSections,
+                               uint64_t VMSize,
+                               uint64_t SectionDataStartOffset,
+                               uint64_t SectionDataSize) {
+    // struct segment_command (56 bytes) or
+    // struct segment_command_64 (72 bytes)
 
     uint64_t Start = OS.tell();
     (void) Start;
 
-    Write32(LCT_Segment);
-    Write32(SegmentLoadCommand32Size + NumSections * Section32Size);
+    unsigned SegmentLoadCommandSize = Is64Bit ? SegmentLoadCommand64Size :
+      SegmentLoadCommand32Size;
+    Write32(Is64Bit ? LCT_Segment64 : LCT_Segment);
+    Write32(SegmentLoadCommandSize +
+            NumSections * (Is64Bit ? Section64Size : Section32Size));
 
     WriteString("", 16);
-    Write32(0); // vmaddr
-    Write32(VMSize); // vmsize
-    Write32(SectionDataStartOffset); // file offset
-    Write32(SectionDataSize); // file size
+    if (Is64Bit) {
+      Write64(0); // vmaddr
+      Write64(VMSize); // vmsize
+      Write64(SectionDataStartOffset); // file offset
+      Write64(SectionDataSize); // file size
+    } else {
+      Write32(0); // vmaddr
+      Write32(VMSize); // vmsize
+      Write32(SectionDataStartOffset); // file offset
+      Write32(SectionDataSize); // file size
+    }
     Write32(0x7); // maxprot
     Write32(0x7); // initprot
     Write32(NumSections);
     Write32(0); // flags
 
-    assert(OS.tell() - Start == SegmentLoadCommand32Size);
+    assert(OS.tell() - Start == SegmentLoadCommandSize);
   }
 
-  void WriteSection32(const MCSectionData &SD, uint64_t FileOffset,
-                      uint64_t RelocationsStart, unsigned NumRelocations) {
+  void WriteSection(const MCSectionData &SD, uint64_t FileOffset,
+                    uint64_t RelocationsStart, unsigned NumRelocations) {
     // The offset is unused for virtual sections.
     if (isVirtualSection(SD.getSection())) {
       assert(SD.getFileSize() == 0 && "Invalid file size!");
       FileOffset = 0;
     }
 
-    // struct section (68 bytes)
+    // struct section (68 bytes) or
+    // struct section_64 (80 bytes)
 
     uint64_t Start = OS.tell();
     (void) Start;
@@ -296,8 +339,13 @@ public:
       static_cast<const MCSectionMachO&>(SD.getSection());
     WriteString(Section.getSectionName(), 16);
     WriteString(Section.getSegmentName(), 16);
-    Write32(SD.getAddress()); // address
-    Write32(SD.getSize()); // size
+    if (Is64Bit) {
+      Write64(SD.getAddress()); // address
+      Write64(SD.getSize()); // size
+    } else {
+      Write32(SD.getAddress()); // address
+      Write32(SD.getSize()); // size
+    }
     Write32(FileOffset);
 
     unsigned Flags = Section.getTypeAndAttributes();
@@ -311,8 +359,10 @@ public:
     Write32(Flags);
     Write32(0); // reserved1
     Write32(Section.getStubSize()); // reserved2
+    if (Is64Bit)
+      Write32(0); // reserved3
 
-    assert(OS.tell() - Start == Section32Size);
+    assert(OS.tell() - Start == Is64Bit ? Section64Size : Section32Size);
   }
 
   void WriteSymtabLoadCommand(uint32_t SymbolOffset, uint32_t NumSymbols,
@@ -370,7 +420,7 @@ public:
     assert(OS.tell() - Start == DysymtabLoadCommandSize);
   }
 
-  void WriteNlist32(MachSymbolData &MSD) {
+  void WriteNlist(MachSymbolData &MSD) {
     MCSymbolData &Data = *MSD.SymbolData;
     const MCSymbol &Symbol = Data.getSymbol();
     uint8_t Type = 0;
@@ -429,24 +479,22 @@ public:
     // The Mach-O streamer uses the lowest 16-bits of the flags for the 'desc'
     // value.
     Write16(Flags);
-    Write32(Address);
+    if (Is64Bit)
+      Write64(Address);
+    else
+      Write32(Address);
   }
 
-  struct MachRelocationEntry {
-    uint32_t Word0;
-    uint32_t Word1;
-  };
-  void ComputeScatteredRelocationInfo(MCAssembler &Asm, MCFragment &Fragment,
-                                      MCAsmFixup &Fixup,
-                                      const MCValue &Target,
-                                     std::vector<MachRelocationEntry> &Relocs) {
+  void RecordScatteredRelocation(MCAssembler &Asm, MCFragment &Fragment,
+                                 const MCAsmFixup &Fixup, MCValue Target,
+                                 uint64_t &FixedValue) {
     uint32_t Address = Fragment.getOffset() + Fixup.Offset;
     unsigned IsPCRel = isFixupKindPCRel(Fixup.Kind);
     unsigned Log2Size = getFixupKindLog2Size(Fixup.Kind);
     unsigned Type = RIT_Vanilla;
 
     // See <reloc.h>.
-    const MCSymbol *A = Target.getSymA();
+    const MCSymbol *A = &Target.getSymA()->getSymbol();
     MCSymbolData *A_SD = &Asm.getSymbolData(*A);
 
     if (!A_SD->getFragment())
@@ -456,11 +504,11 @@ public:
     uint32_t Value = A_SD->getAddress();
     uint32_t Value2 = 0;
 
-    if (const MCSymbol *B = Target.getSymB()) {
-      MCSymbolData *B_SD = &Asm.getSymbolData(*B);
+    if (const MCSymbolRefExpr *B = Target.getSymB()) {
+      MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
 
       if (!B_SD->getFragment())
-        llvm_report_error("symbol '" + B->getName() +
+        llvm_report_error("symbol '" + B->getSymbol().getName() +
                           "' can not be undefined in a subtraction expression");
 
       // Select the appropriate difference relocation type.
@@ -472,15 +520,7 @@ public:
       Value2 = B_SD->getAddress();
     }
 
-    MachRelocationEntry MRE;
-    MRE.Word0 = ((Address   <<  0) |
-                 (Type      << 24) |
-                 (Log2Size  << 28) |
-                 (IsPCRel   << 30) |
-                 RF_Scattered);
-    MRE.Word1 = Value;
-    Relocs.push_back(MRE);
-
+    // Relocations are written out in reverse order, so the PAIR comes first.
     if (Type == RIT_Difference || Type == RIT_LocalDifference) {
       MachRelocationEntry MRE;
       MRE.Word0 = ((0         <<  0) |
@@ -489,34 +529,36 @@ public:
                    (IsPCRel   << 30) |
                    RF_Scattered);
       MRE.Word1 = Value2;
-      Relocs.push_back(MRE);
+      Relocations[Fragment.getParent()].push_back(MRE);
     }
+
+    MachRelocationEntry MRE;
+    MRE.Word0 = ((Address   <<  0) |
+                 (Type      << 24) |
+                 (Log2Size  << 28) |
+                 (IsPCRel   << 30) |
+                 RF_Scattered);
+    MRE.Word1 = Value;
+    Relocations[Fragment.getParent()].push_back(MRE);
   }
 
-  void ComputeRelocationInfo(MCAssembler &Asm, MCDataFragment &Fragment,
-                             MCAsmFixup &Fixup,
-                             std::vector<MachRelocationEntry> &Relocs) {
+  void RecordRelocation(MCAssembler &Asm, MCDataFragment &Fragment,
+                        const MCAsmFixup &Fixup, MCValue Target,
+                        uint64_t &FixedValue) {
     unsigned IsPCRel = isFixupKindPCRel(Fixup.Kind);
     unsigned Log2Size = getFixupKindLog2Size(Fixup.Kind);
 
-    // FIXME: Share layout object.
-    MCAsmLayout Layout(Asm);
-
-    // Evaluate the fixup; if the value was resolved, no relocation is needed.
-    MCValue Target;
-    if (Asm.EvaluateFixup(Layout, Fixup, &Fragment, Target, Fixup.FixedValue))
-      return;
-
     // If this is a difference or a defined symbol plus an offset, then we need
     // a scattered relocation entry.
     uint32_t Offset = Target.getConstant();
     if (IsPCRel)
       Offset += 1 << Log2Size;
     if (Target.getSymB() ||
-        (Target.getSymA() && !Target.getSymA()->isUndefined() &&
-         Offset))
-      return ComputeScatteredRelocationInfo(Asm, Fragment, Fixup, Target,
-                                            Relocs);
+        (Target.getSymA() && !Target.getSymA()->getSymbol().isUndefined() &&
+         Offset)) {
+      RecordScatteredRelocation(Asm, Fragment, Fixup, Target, FixedValue);
+      return;
+    }
 
     // See <reloc.h>.
     uint32_t Address = Fragment.getOffset() + Fixup.Offset;
@@ -533,7 +575,7 @@ public:
       Type = RIT_Vanilla;
       Value = 0;
     } else {
-      const MCSymbol *Symbol = Target.getSymA();
+      const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
       MCSymbolData *SD = &Asm.getSymbolData(*Symbol);
 
       if (Symbol->isUndefined()) {
@@ -564,7 +606,20 @@ public:
                  (Log2Size  << 25) |
                  (IsExtern  << 27) |
                  (Type      << 28));
-    Relocs.push_back(MRE);
+    Relocations[Fragment.getParent()].push_back(MRE);
+  }
+
+  void ComputeRelocationInfo(MCAssembler &Asm, MCDataFragment &Fragment,
+                             MCAsmFixup &Fixup) {
+    // FIXME: Share layout object.
+    MCAsmLayout Layout(Asm);
+
+    // Evaluate the fixup; if the value was resolved, no relocation is needed.
+    MCValue Target;
+    if (Asm.EvaluateFixup(Layout, Fixup, &Fragment, Target, Fixup.FixedValue))
+      return;
+
+    RecordRelocation(Asm, Fragment, Fixup, Target, Fixup.FixedValue);
   }
 
   void BindIndirectSymbols(MCAssembler &Asm) {
@@ -582,9 +637,7 @@ public:
       const MCSectionMachO &Section =
         static_cast<const MCSectionMachO&>(it->SectionData->getSection());
 
-      unsigned Type =
-        Section.getTypeAndAttributes() & MCSectionMachO::SECTION_TYPE;
-      if (Type != MCSectionMachO::S_NON_LAZY_SYMBOL_POINTERS)
+      if (Section.getType() != MCSectionMachO::S_NON_LAZY_SYMBOL_POINTERS)
         continue;
 
       Asm.getOrCreateSymbolData(*it->Symbol);
@@ -597,10 +650,8 @@ public:
       const MCSectionMachO &Section =
         static_cast<const MCSectionMachO&>(it->SectionData->getSection());
 
-      unsigned Type =
-        Section.getTypeAndAttributes() & MCSectionMachO::SECTION_TYPE;
-      if (Type != MCSectionMachO::S_LAZY_SYMBOL_POINTERS &&
-          Type != MCSectionMachO::S_SYMBOL_STUBS)
+      if (Section.getType() != MCSectionMachO::S_LAZY_SYMBOL_POINTERS &&
+          Section.getType() != MCSectionMachO::S_SYMBOL_STUBS)
         continue;
 
       // Set the symbol type to undefined lazy, but only on construction.
@@ -645,8 +696,8 @@ public:
            ie = Asm.symbol_end(); it != ie; ++it) {
       const MCSymbol &Symbol = it->getSymbol();
 
-      // Ignore assembler temporaries.
-      if (it->getSymbol().isTemporary())
+      // Ignore non-linker visible symbols.
+      if (!Asm.isSymbolLinkerVisible(it))
         continue;
 
       if (!it->isExternal() && !Symbol.isUndefined())
@@ -681,8 +732,8 @@ public:
            ie = Asm.symbol_end(); it != ie; ++it) {
       const MCSymbol &Symbol = it->getSymbol();
 
-      // Ignore assembler temporaries.
-      if (it->getSymbol().isTemporary())
+      // Ignore non-linker visible symbols.
+      if (!Asm.isSymbolLinkerVisible(it))
         continue;
 
       if (it->isExternal() || Symbol.isUndefined())
@@ -748,7 +799,8 @@ public:
     // The section data starts after the header, the segment load command (and
     // section headers) and the symbol table.
     unsigned NumLoadCommands = 1;
-    uint64_t LoadCommandsSize =
+    uint64_t LoadCommandsSize = Is64Bit ?
+      SegmentLoadCommand64Size + NumSections * Section64Size :
       SegmentLoadCommand32Size + NumSections * Section32Size;
 
     // Add the symbol table load command sizes, if used.
@@ -759,7 +811,8 @@ public:
 
     // Compute the total size of the section data, as well as its file size and
     // vm size.
-    uint64_t SectionDataStart = Header32Size + LoadCommandsSize;
+    uint64_t SectionDataStart = (Is64Bit ? Header64Size : Header32Size)
+      + LoadCommandsSize;
     uint64_t SectionDataSize = 0;
     uint64_t SectionDataFileSize = 0;
     uint64_t VMSize = 0;
@@ -785,37 +838,29 @@ public:
     SectionDataFileSize += SectionDataPadding;
 
     // Write the prolog, starting with the header and load command...
-    WriteHeader32(NumLoadCommands, LoadCommandsSize,
-                  Asm.getSubsectionsViaSymbols());
-    WriteSegmentLoadCommand32(NumSections, VMSize,
-                              SectionDataStart, SectionDataSize);
+    WriteHeader(NumLoadCommands, LoadCommandsSize,
+                Asm.getSubsectionsViaSymbols());
+    WriteSegmentLoadCommand(NumSections, VMSize,
+                            SectionDataStart, SectionDataSize);
 
-    // ... and then the section headers.
-    //
-    // We also compute the section relocations while we do this. Note that
-    // computing relocation info will also update the fixup to have the correct
-    // value; this will overwrite the appropriate data in the fragment when it
-    // is written.
-    std::vector<MachRelocationEntry> RelocInfos;
-    uint64_t RelocTableEnd = SectionDataStart + SectionDataFileSize;
     for (MCAssembler::iterator it = Asm.begin(),
            ie = Asm.end(); it != ie; ++it) {
       MCSectionData &SD = *it;
-
-      // The assembler writes relocations in the reverse order they were seen.
-      //
-      // FIXME: It is probably more complicated than this.
-      unsigned NumRelocsStart = RelocInfos.size();
-      for (MCSectionData::reverse_iterator it2 = SD.rbegin(),
-             ie2 = SD.rend(); it2 != ie2; ++it2)
+      for (MCSectionData::iterator it2 = SD.begin(),
+             ie2 = SD.end(); it2 != ie2; ++it2)
         if (MCDataFragment *DF = dyn_cast<MCDataFragment>(&*it2))
           for (unsigned i = 0, e = DF->fixup_size(); i != e; ++i)
-            ComputeRelocationInfo(Asm, *DF, DF->getFixups()[e - i - 1],
-                                  RelocInfos);
+            ComputeRelocationInfo(Asm, *DF, DF->getFixups()[i]);
+    }
 
-      unsigned NumRelocs = RelocInfos.size() - NumRelocsStart;
-      uint64_t SectionStart = SectionDataStart + SD.getAddress();
-      WriteSection32(SD, SectionStart, RelocTableEnd, NumRelocs);
+    // ... and then the section headers.
+    uint64_t RelocTableEnd = SectionDataStart + SectionDataFileSize;
+    for (MCAssembler::iterator it = Asm.begin(),
+           ie = Asm.end(); it != ie; ++it) {
+      std::vector<MachRelocationEntry> &Relocs = Relocations[it];
+      unsigned NumRelocs = Relocs.size();
+      uint64_t SectionStart = SectionDataStart + it->getAddress();
+      WriteSection(*it, SectionStart, RelocTableEnd, NumRelocs);
       RelocTableEnd += NumRelocs * RelocationInfoSize;
     }
 
@@ -842,7 +887,8 @@ public:
 
       // The string table is written after symbol table.
       uint64_t StringTableOffset =
-        SymbolTableOffset + NumSymTabSymbols * Nlist32Size;
+        SymbolTableOffset + NumSymTabSymbols * (Is64Bit ? Nlist64Size :
+                                                Nlist32Size);
       WriteSymtabLoadCommand(SymbolTableOffset, NumSymTabSymbols,
                              StringTableOffset, StringTable.size());
 
@@ -860,9 +906,15 @@ public:
     WriteZeros(SectionDataPadding);
 
     // Write the relocation entries.
-    for (unsigned i = 0, e = RelocInfos.size(); i != e; ++i) {
-      Write32(RelocInfos[i].Word0);
-      Write32(RelocInfos[i].Word1);
+    for (MCAssembler::iterator it = Asm.begin(),
+           ie = Asm.end(); it != ie; ++it) {
+      // Write the section relocation entries, in reverse order to match 'as'
+      // (approximately, the exact algorithm is more complicated than this).
+      std::vector<MachRelocationEntry> &Relocs = Relocations[it];
+      for (unsigned i = 0, e = Relocs.size(); i != e; ++i) {
+        Write32(Relocs[e - i - 1].Word0);
+        Write32(Relocs[e - i - 1].Word1);
+      }
     }
 
     // Write the symbol table data, if used.
@@ -875,9 +927,7 @@ public:
         // special handling.
         const MCSectionMachO &Section =
           static_cast<const MCSectionMachO&>(it->SectionData->getSection());
-        unsigned Type =
-          Section.getTypeAndAttributes() & MCSectionMachO::SECTION_TYPE;
-        if (Type == MCSectionMachO::S_NON_LAZY_SYMBOL_POINTERS) {
+        if (Section.getType() == MCSectionMachO::S_NON_LAZY_SYMBOL_POINTERS) {
           // If this symbol is defined and internal, mark it as such.
           if (it->Symbol->isDefined() &&
               !Asm.getSymbolData(*it->Symbol).isExternal()) {
@@ -896,11 +946,11 @@ public:
 
       // Write the symbol table entries.
       for (unsigned i = 0, e = LocalSymbolData.size(); i != e; ++i)
-        WriteNlist32(LocalSymbolData[i]);
+        WriteNlist(LocalSymbolData[i]);
       for (unsigned i = 0, e = ExternalSymbolData.size(); i != e; ++i)
-        WriteNlist32(ExternalSymbolData[i]);
+        WriteNlist(ExternalSymbolData[i]);
       for (unsigned i = 0, e = UndefinedSymbolData.size(); i != e; ++i)
-        WriteNlist32(UndefinedSymbolData[i]);
+        WriteNlist(UndefinedSymbolData[i]);
 
       // Write the string table.
       OS << StringTable.str();
@@ -981,6 +1031,140 @@ MCAssembler::MCAssembler(MCContext &_Context, TargetAsmBackend &_Backend,
 MCAssembler::~MCAssembler() {
 }
 
+static bool isScatteredFixupFullyResolvedSimple(const MCAssembler &Asm,
+                                                const MCAsmFixup &Fixup,
+                                                const MCDataFragment *DF,
+                                                const MCValue Target,
+                                                const MCSection *BaseSection) {
+  // The effective fixup address is
+  //     addr(atom(A)) + offset(A)
+  //   - addr(atom(B)) - offset(B)
+  //   - addr(<base symbol>) + <fixup offset from base symbol>
+  // and the offsets are not relocatable, so the fixup is fully resolved when
+  //  addr(atom(A)) - addr(atom(B)) - addr(<base symbol>)) == 0.
+  //
+  // The simple (Darwin, except on x86_64) way of dealing with this was to
+  // assume that any reference to a temporary symbol *must* be a temporary
+  // symbol in the same atom, unless the sections differ. Therefore, any PCrel
+  // relocation to a temporary symbol (in the same section) is fully
+  // resolved. This also works in conjunction with absolutized .set, which
+  // requires the compiler to use .set to absolutize the differences between
+  // symbols which the compiler knows to be assembly time constants, so we don't
+  // need to worry about consider symbol differences fully resolved.
+
+  // Non-relative fixups are only resolved if constant.
+  if (!BaseSection)
+    return Target.isAbsolute();
+
+  // Otherwise, relative fixups are only resolved if not a difference and the
+  // target is a temporary in the same section.
+  if (Target.isAbsolute() || Target.getSymB())
+    return false;
+
+  const MCSymbol *A = &Target.getSymA()->getSymbol();
+  if (!A->isTemporary() || !A->isInSection() ||
+      &A->getSection() != BaseSection)
+    return false;
+
+  return true;
+}
+
+static bool isScatteredFixupFullyResolved(const MCAssembler &Asm,
+                                          const MCAsmFixup &Fixup,
+                                          const MCDataFragment *DF,
+                                          const MCValue Target,
+                                          const MCSymbolData *BaseSymbol) {
+  // The effective fixup address is
+  //     addr(atom(A)) + offset(A)
+  //   - addr(atom(B)) - offset(B)
+  //   - addr(BaseSymbol) + <fixup offset from base symbol>
+  // and the offsets are not relocatable, so the fixup is fully resolved when
+  //  addr(atom(A)) - addr(atom(B)) - addr(BaseSymbol) == 0.
+  //
+  // Note that "false" is almost always conservatively correct (it means we emit
+  // a relocation which is unnecessary), except when it would force us to emit a
+  // relocation which the target cannot encode.
+
+  const MCSymbolData *A_Base = 0, *B_Base = 0;
+  if (const MCSymbolRefExpr *A = Target.getSymA()) {
+    // Modified symbol references cannot be resolved.
+    if (A->getKind() != MCSymbolRefExpr::VK_None)
+      return false;
+
+    A_Base = Asm.getAtom(&Asm.getSymbolData(A->getSymbol()));
+    if (!A_Base)
+      return false;
+  }
+
+  if (const MCSymbolRefExpr *B = Target.getSymB()) {
+    // Modified symbol references cannot be resolved.
+    if (B->getKind() != MCSymbolRefExpr::VK_None)
+      return false;
+
+    B_Base = Asm.getAtom(&Asm.getSymbolData(B->getSymbol()));
+    if (!B_Base)
+      return false;
+  }
+
+  // If there is no base, A and B have to be the same atom for this fixup to be
+  // fully resolved.
+  if (!BaseSymbol)
+    return A_Base == B_Base;
+
+  // Otherwise, B must be missing and A must be the base.
+  return !B_Base && BaseSymbol == A_Base;
+}
+
+bool MCAssembler::isSymbolLinkerVisible(const MCSymbolData *SD) const {
+  // Non-temporary labels should always be visible to the linker.
+  if (!SD->getSymbol().isTemporary())
+    return true;
+
+  // Absolute temporary labels are never visible.
+  if (!SD->getFragment())
+    return false;
+
+  // Otherwise, check if the section requires symbols even for temporary labels.
+  return getBackend().doesSectionRequireSymbols(
+    SD->getFragment()->getParent()->getSection());
+}
+
+const MCSymbolData *MCAssembler::getAtomForAddress(const MCSectionData *Section,
+                                                   uint64_t Address) const {
+  const MCSymbolData *Best = 0;
+  for (MCAssembler::const_symbol_iterator it = symbol_begin(),
+         ie = symbol_end(); it != ie; ++it) {
+    // Ignore non-linker visible symbols.
+    if (!isSymbolLinkerVisible(it))
+      continue;
+
+    // Ignore symbols not in the same section.
+    if (!it->getFragment() || it->getFragment()->getParent() != Section)
+      continue;
+
+    // Otherwise, find the closest symbol preceding this address (ties are
+    // resolved in favor of the last defined symbol).
+    if (it->getAddress() <= Address &&
+        (!Best || it->getAddress() >= Best->getAddress()))
+      Best = it;
+  }
+
+  return Best;
+}
+
+const MCSymbolData *MCAssembler::getAtom(const MCSymbolData *SD) const {
+  // Linker visible symbols define atoms.
+  if (isSymbolLinkerVisible(SD))
+    return SD;
+
+  // Absolute and undefined symbols have no defining atom.
+  if (!SD->getFragment())
+    return 0;
+
+  // Otherwise, search by address.
+  return getAtomForAddress(SD->getFragment()->getParent(), SD->getAddress());
+}
+
 bool MCAssembler::EvaluateFixup(const MCAsmLayout &Layout, MCAsmFixup &Fixup,
                                 MCDataFragment *DF,
                                 MCValue &Target, uint64_t &Value) const {
@@ -993,34 +1177,45 @@ bool MCAssembler::EvaluateFixup(const MCAsmLayout &Layout, MCAsmFixup &Fixup,
 
   Value = Target.getConstant();
 
-  // FIXME: This "resolved" check isn't quite right. The assumption is that if
-  // we have a PCrel access to a temporary, then that temporary is in the same
-  // atom, and so the value is resolved. We need explicit atom's to implement
-  // this more precisely.
   bool IsResolved = true, IsPCRel = isFixupKindPCRel(Fixup.Kind);
-  if (const MCSymbol *Symbol = Target.getSymA()) {
-    if (Symbol->isDefined())
-      Value += getSymbolData(*Symbol).getAddress();
+  if (const MCSymbolRefExpr *A = Target.getSymA()) {
+    if (A->getSymbol().isDefined())
+      Value += getSymbolData(A->getSymbol()).getAddress();
     else
       IsResolved = false;
-
-    // With scattered symbols, we assume anything that isn't a PCrel temporary
-    // access can have an arbitrary value.
-    if (getBackend().hasScatteredSymbols() &&
-        (!IsPCRel || !Symbol->isTemporary()))
-      IsResolved = false;
   }
-  if (const MCSymbol *Symbol = Target.getSymB()) {
-    if (Symbol->isDefined())
-      Value -= getSymbolData(*Symbol).getAddress();
+  if (const MCSymbolRefExpr *B = Target.getSymB()) {
+    if (B->getSymbol().isDefined())
+      Value -= getSymbolData(B->getSymbol()).getAddress();
     else
       IsResolved = false;
+  }
 
-    // With scattered symbols, we assume anything that isn't a PCrel temporary
-    // access can have an arbitrary value.
-    if (getBackend().hasScatteredSymbols() &&
-        (!IsPCRel || !Symbol->isTemporary()))
-      IsResolved = false;
+  // If we are using scattered symbols, determine whether this value is actually
+  // resolved; scattering may cause atoms to move.
+  if (IsResolved && getBackend().hasScatteredSymbols()) {
+    if (getBackend().hasReliableSymbolDifference()) {
+      // If this is a PCrel relocation, find the base atom (identified by its
+      // symbol) that the fixup value is relative to.
+      const MCSymbolData *BaseSymbol = 0;
+      if (IsPCRel) {
+        BaseSymbol = getAtomForAddress(
+          DF->getParent(), DF->getAddress() + Fixup.Offset);
+        if (!BaseSymbol)
+          IsResolved = false;
+      }
+
+      if (IsResolved)
+        IsResolved = isScatteredFixupFullyResolved(*this, Fixup, DF, Target,
+                                                   BaseSymbol);
+    } else {
+      const MCSection *BaseSection = 0;
+      if (IsPCRel)
+        BaseSection = &DF->getParent()->getSection();
+
+      IsResolved = isScatteredFixupFullyResolvedSimple(*this, Fixup, DF, Target,
+                                                       BaseSection);
+    }
   }
 
   if (IsPCRel)
@@ -1284,7 +1479,10 @@ void MCAssembler::Finish() {
       dump(); });
 
   // Write the object file.
-  MachObjectWriter MOW(OS);
+  //
+  // FIXME: Factor out MCObjectWriter.
+  bool Is64Bit = StringRef(getBackend().getTarget().getName()) == "x86-64";
+  MachObjectWriter MOW(OS, Is64Bit);
   MOW.WriteObject(*this);
 
   OS.flush();