Exemple #1
0
static bool isScatteredFixupFullyResolved(const MCAssembler &Asm,
                                          const MCAsmLayout &Layout,
                                          const MCFixup &Fixup,
                                          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(Layout, &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(Layout, &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;
}
void X86MachObjectWriter::RecordX86_64Relocation(
    MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout,
    const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
    uint64_t &FixedValue) {
  unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
  unsigned IsRIPRel = isFixupKindRIPRel(Fixup.getKind());
  unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());

  // See <reloc.h>.
  uint32_t FixupOffset =
    Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
  uint32_t FixupAddress =
    Writer->getFragmentAddress(Fragment, Layout) + Fixup.getOffset();
  int64_t Value = 0;
  unsigned Index = 0;
  unsigned IsExtern = 0;
  unsigned Type = 0;
  const MCSymbol *RelSymbol = nullptr;

  Value = Target.getConstant();

  if (IsPCRel) {
    // Compensate for the relocation offset, Darwin x86_64 relocations only have
    // the addend and appear to have attempted to define it to be the actual
    // expression addend without the PCrel bias. However, instructions with data
    // following the relocation are not accommodated for (see comment below
    // regarding SIGNED{1,2,4}), so it isn't exactly that either.
    Value += 1LL << Log2Size;
  }

  if (Target.isAbsolute()) { // constant
    // SymbolNum of 0 indicates the absolute section.
    Type = MachO::X86_64_RELOC_UNSIGNED;

    // FIXME: I believe this is broken, I don't think the linker can understand
    // it. I think it would require a local relocation, but I'm not sure if that
    // would work either. The official way to get an absolute PCrel relocation
    // is to use an absolute symbol (which we don't support yet).
    if (IsPCRel) {
      IsExtern = 1;
      Type = MachO::X86_64_RELOC_BRANCH;
    }
  } else if (Target.getSymB()) { // A - B + constant
    const MCSymbol *A = &Target.getSymA()->getSymbol();
    if (A->isTemporary())
      A = &Writer->findAliasedSymbol(*A);
    const MCSymbolData &A_SD = Asm.getSymbolData(*A);
    const MCSymbol *A_Base = Asm.getAtom(&A_SD);

    const MCSymbol *B = &Target.getSymB()->getSymbol();
    if (B->isTemporary())
      B = &Writer->findAliasedSymbol(*B);
    const MCSymbolData &B_SD = Asm.getSymbolData(*B);
    const MCSymbol *B_Base = Asm.getAtom(&B_SD);

    // Neither symbol can be modified.
    if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
        Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None)
      report_fatal_error("unsupported relocation of modified symbol", false);

    // We don't support PCrel relocations of differences. Darwin 'as' doesn't
    // implement most of these correctly.
    if (IsPCRel)
      report_fatal_error("unsupported pc-relative relocation of difference",
                         false);

    // The support for the situation where one or both of the symbols would
    // require a local relocation is handled just like if the symbols were
    // external.  This is certainly used in the case of debug sections where the
    // section has only temporary symbols and thus the symbols don't have base
    // symbols.  This is encoded using the section ordinal and non-extern
    // relocation entries.

    // Darwin 'as' doesn't emit correct relocations for this (it ends up with a
    // single SIGNED relocation); reject it for now.  Except the case where both
    // symbols don't have a base, equal but both NULL.
    if (A_Base == B_Base && A_Base)
      report_fatal_error("unsupported relocation with identical base", false);

    // A subtraction expression where either symbol is undefined is a
    // non-relocatable expression.
    if (A->isUndefined() || B->isUndefined()) {
      StringRef Name = A->isUndefined() ? A->getName() : B->getName();
      Asm.getContext().FatalError(Fixup.getLoc(),
        "unsupported relocation with subtraction expression, symbol '" +
        Name + "' can not be undefined in a subtraction expression");
    }

    Value +=
        Writer->getSymbolAddress(&A_SD, Layout) -
        (!A_Base ? 0 : Writer->getSymbolAddress(&A_Base->getData(), Layout));
    Value -=
        Writer->getSymbolAddress(&B_SD, Layout) -
        (!B_Base ? 0 : Writer->getSymbolAddress(&B_Base->getData(), Layout));

    if (!A_Base)
      Index = A_SD.getFragment()->getParent()->getOrdinal() + 1;
    Type = MachO::X86_64_RELOC_UNSIGNED;

    MachO::any_relocation_info MRE;
    MRE.r_word0 = FixupOffset;
    MRE.r_word1 =
        (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
    Writer->addRelocation(A_Base, Fragment->getParent(), MRE);

    if (B_Base)
      RelSymbol = B_Base;
    else
      Index = B_SD.getFragment()->getParent()->getOrdinal() + 1;
    Type = MachO::X86_64_RELOC_SUBTRACTOR;
  } else {
    const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
    if (Symbol->isTemporary() && Value) {
      const MCSection &Sec = Symbol->getSection();
      if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
        Asm.addLocalUsedInReloc(*Symbol);
    }
    const MCSymbolData &SD = Asm.getSymbolData(*Symbol);
    RelSymbol = Asm.getAtom(&SD);

    // Relocations inside debug sections always use local relocations when
    // possible. This seems to be done because the debugger doesn't fully
    // understand x86_64 relocation entries, and expects to find values that
    // have already been fixed up.
    if (Symbol->isInSection()) {
      const MCSectionMachO &Section = static_cast<const MCSectionMachO&>(
        Fragment->getParent()->getSection());
      if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
        RelSymbol = nullptr;
    }

    // x86_64 almost always uses external relocations, except when there is no
    // symbol to use as a base address (a local symbol with no preceding
    // non-local symbol).
    if (RelSymbol) {
      // Add the local offset, if needed.
      if (&RelSymbol->getData() != &SD)
        Value += Layout.getSymbolOffset(&SD) -
                 Layout.getSymbolOffset(&RelSymbol->getData());
    } else if (Symbol->isInSection() && !Symbol->isVariable()) {
      // The index is the section ordinal (1-based).
      Index = SD.getFragment()->getParent()->getOrdinal() + 1;
      Value += Writer->getSymbolAddress(&SD, Layout);

      if (IsPCRel)
        Value -= FixupAddress + (1 << Log2Size);
    } else if (Symbol->isVariable()) {
      const MCExpr *Value = Symbol->getVariableValue();
      int64_t Res;
      bool isAbs = Value->EvaluateAsAbsolute(Res, Layout,
                                             Writer->getSectionAddressMap());
      if (isAbs) {
        FixedValue = Res;
        return;
      } else {
        report_fatal_error("unsupported relocation of variable '" +
                           Symbol->getName() + "'", false);
      }
    } else {
      report_fatal_error("unsupported relocation of undefined symbol '" +
                         Symbol->getName() + "'", false);
    }

    MCSymbolRefExpr::VariantKind Modifier = Target.getSymA()->getKind();
    if (IsPCRel) {
      if (IsRIPRel) {
        if (Modifier == MCSymbolRefExpr::VK_GOTPCREL) {
          // x86_64 distinguishes movq foo@GOTPCREL so that the linker can
          // rewrite the movq to an leaq at link time if the symbol ends up in
          // the same linkage unit.
          if (unsigned(Fixup.getKind()) == X86::reloc_riprel_4byte_movq_load)
            Type = MachO::X86_64_RELOC_GOT_LOAD;
          else
            Type = MachO::X86_64_RELOC_GOT;
        }  else if (Modifier == MCSymbolRefExpr::VK_TLVP) {
          Type = MachO::X86_64_RELOC_TLV;
        }  else if (Modifier != MCSymbolRefExpr::VK_None) {
          report_fatal_error("unsupported symbol modifier in relocation",
                             false);
        } else {
          Type = MachO::X86_64_RELOC_SIGNED;

          // The Darwin x86_64 relocation format has a problem where it cannot
          // encode an address (L<foo> + <constant>) which is outside the atom
          // containing L<foo>. Generally, this shouldn't occur but it does
          // happen when we have a RIPrel instruction with data following the
          // relocation entry (e.g., movb $012, L0(%rip)). Even with the PCrel
          // adjustment Darwin x86_64 uses, the offset is still negative and the
          // linker has no way to recognize this.
          //
          // To work around this, Darwin uses several special relocation types
          // to indicate the offsets. However, the specification or
          // implementation of these seems to also be incomplete; they should
          // adjust the addend as well based on the actual encoded instruction
          // (the additional bias), but instead appear to just look at the final
          // offset.
          switch (-(Target.getConstant() + (1LL << Log2Size))) {
          case 1: Type = MachO::X86_64_RELOC_SIGNED_1; break;
          case 2: Type = MachO::X86_64_RELOC_SIGNED_2; break;
          case 4: Type = MachO::X86_64_RELOC_SIGNED_4; break;
          }
        }
      } else {
        if (Modifier != MCSymbolRefExpr::VK_None)
          report_fatal_error("unsupported symbol modifier in branch "
                             "relocation", false);

        Type = MachO::X86_64_RELOC_BRANCH;
      }
    } else {
      if (Modifier == MCSymbolRefExpr::VK_GOT) {
        Type = MachO::X86_64_RELOC_GOT;
      } else if (Modifier == MCSymbolRefExpr::VK_GOTPCREL) {
        // GOTPCREL is allowed as a modifier on non-PCrel instructions, in which
        // case all we do is set the PCrel bit in the relocation entry; this is
        // used with exception handling, for example. The source is required to
        // include any necessary offset directly.
        Type = MachO::X86_64_RELOC_GOT;
        IsPCRel = 1;
      } else if (Modifier == MCSymbolRefExpr::VK_TLVP) {
        report_fatal_error("TLVP symbol modifier should have been rip-rel",
                           false);
      } else if (Modifier != MCSymbolRefExpr::VK_None)
        report_fatal_error("unsupported symbol modifier in relocation", false);
      else {
        Type = MachO::X86_64_RELOC_UNSIGNED;
        unsigned Kind = Fixup.getKind();
        if (Kind == X86::reloc_signed_4byte)
          report_fatal_error("32-bit absolute addressing is not supported in "
                             "64-bit mode", false);
      }
    }
  }

  // x86_64 always writes custom values into the fixups.
  FixedValue = Value;

  // struct relocation_info (8 bytes)
  MachO::any_relocation_info MRE;
  MRE.r_word0 = FixupOffset;
  MRE.r_word1 = (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |
                (IsExtern << 27) | (Type << 28);
  Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
}
void AArch64MachObjectWriter::RecordRelocation(
    MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout,
    const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target,
    uint64_t &FixedValue) {
    unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());

    // See <reloc.h>.
    uint32_t FixupOffset = Layout.getFragmentOffset(Fragment);
    unsigned Log2Size = 0;
    int64_t Value = 0;
    unsigned Index = 0;
    unsigned Type = 0;
    unsigned Kind = Fixup.getKind();
    const MCSymbolData *RelSymbol = nullptr;

    FixupOffset += Fixup.getOffset();

    // AArch64 pcrel relocation addends do not include the section offset.
    if (IsPCRel)
        FixedValue += FixupOffset;

    // ADRP fixups use relocations for the whole symbol value and only
    // put the addend in the instruction itself. Clear out any value the
    // generic code figured out from the sybmol definition.
    if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21)
        FixedValue = 0;

    // imm19 relocations are for conditional branches, which require
    // assembler local symbols. If we got here, that's not what we have,
    // so complain loudly.
    if (Kind == AArch64::fixup_aarch64_pcrel_branch19) {
        Asm.getContext().FatalError(Fixup.getLoc(),
                                    "conditional branch requires assembler-local"
                                    " label. '" +
                                    Target.getSymA()->getSymbol().getName() +
                                    "' is external.");
        return;
    }

    // 14-bit branch relocations should only target internal labels, and so
    // should never get here.
    if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {
        Asm.getContext().FatalError(Fixup.getLoc(),
                                    "Invalid relocation on conditional branch!");
        return;
    }

    if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size,
                                      Asm)) {
        Asm.getContext().FatalError(Fixup.getLoc(), "unknown AArch64 fixup kind!");
        return;
    }

    Value = Target.getConstant();

    if (Target.isAbsolute()) { // constant
        // FIXME: Should this always be extern?
        // SymbolNum of 0 indicates the absolute section.
        Type = MachO::ARM64_RELOC_UNSIGNED;

        if (IsPCRel) {
            Asm.getContext().FatalError(Fixup.getLoc(),
                                        "PC relative absolute relocation!");

            // FIXME: x86_64 sets the type to a branch reloc here. Should we do
            // something similar?
        }
    } else if (Target.getSymB()) { // A - B + constant
        const MCSymbol *A = &Target.getSymA()->getSymbol();
        const MCSymbolData &A_SD = Asm.getSymbolData(*A);
        const MCSymbolData *A_Base = Asm.getAtom(&A_SD);

        const MCSymbol *B = &Target.getSymB()->getSymbol();
        const MCSymbolData &B_SD = Asm.getSymbolData(*B);
        const MCSymbolData *B_Base = Asm.getAtom(&B_SD);

        // Check for "_foo@got - .", which comes through here as:
        // Ltmp0:
        //    ... _foo@got - Ltmp0
        if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT &&
                Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None &&
                Layout.getSymbolOffset(&B_SD) ==
                Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) {
            // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.
            Type = MachO::ARM64_RELOC_POINTER_TO_GOT;
            IsPCRel = 1;
            MachO::any_relocation_info MRE;
            MRE.r_word0 = FixupOffset;
            MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
            Writer->addRelocation(A_Base, Fragment->getParent(), MRE);
            return;
        } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None ||
                   Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None)
            // Otherwise, neither symbol can be modified.
            Asm.getContext().FatalError(Fixup.getLoc(),
                                        "unsupported relocation of modified symbol");

        // We don't support PCrel relocations of differences.
        if (IsPCRel)
            Asm.getContext().FatalError(Fixup.getLoc(),
                                        "unsupported pc-relative relocation of "
                                        "difference");

        // AArch64 always uses external relocations. If there is no symbol to use as
        // a base address (a local symbol with no preceding non-local symbol),
        // error out.
        //
        // FIXME: We should probably just synthesize an external symbol and use
        // that.
        if (!A_Base)
            Asm.getContext().FatalError(
                Fixup.getLoc(),
                "unsupported relocation of local symbol '" + A->getName() +
                "'. Must have non-local symbol earlier in section.");
        if (!B_Base)
            Asm.getContext().FatalError(
                Fixup.getLoc(),
                "unsupported relocation of local symbol '" + B->getName() +
                "'. Must have non-local symbol earlier in section.");

        if (A_Base == B_Base && A_Base)
            Asm.getContext().FatalError(Fixup.getLoc(),
                                        "unsupported relocation with identical base");

        Value += (!A_SD.getFragment() ? 0
                  : Writer->getSymbolAddress(&A_SD, Layout)) -
                 (!A_Base || !A_Base->getFragment()
                  ? 0
                  : Writer->getSymbolAddress(A_Base, Layout));
        Value -= (!B_SD.getFragment() ? 0
                  : Writer->getSymbolAddress(&B_SD, Layout)) -
                 (!B_Base || !B_Base->getFragment()
                  ? 0
                  : Writer->getSymbolAddress(B_Base, Layout));

        Type = MachO::ARM64_RELOC_UNSIGNED;

        MachO::any_relocation_info MRE;
        MRE.r_word0 = FixupOffset;
        MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
        Writer->addRelocation(A_Base, Fragment->getParent(), MRE);

        RelSymbol = B_Base;
        Type = MachO::ARM64_RELOC_SUBTRACTOR;
    } else { // A + constant
        const MCSymbol *Symbol = &Target.getSymA()->getSymbol();
        const MCSectionMachO &Section = static_cast<const MCSectionMachO &>(
                                            Fragment->getParent()->getSection());

        bool CanUseLocalRelocation =
            canUseLocalRelocation(Section, *Symbol, Log2Size);
        if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {
            const MCSection &Sec = Symbol->getSection();
            if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec))
                Asm.addLocalUsedInReloc(*Symbol);
        }

        const MCSymbolData &SD = Asm.getSymbolData(*Symbol);
        const MCSymbolData *Base = Asm.getAtom(&SD);

        // If the symbol is a variable and we weren't able to get a Base for it
        // (i.e., it's not in the symbol table associated with a section) resolve
        // the relocation based its expansion instead.
        if (Symbol->isVariable() && !Base) {
            // If the evaluation is an absolute value, just use that directly
            // to keep things easy.
            int64_t Res;
            if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
                        Res, Layout, Writer->getSectionAddressMap())) {
                FixedValue = Res;
                return;
            }

            // FIXME: Will the Target we already have ever have any data in it
            // we need to preserve and merge with the new Target? How about
            // the FixedValue?
            if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout,
                    &Fixup))
                Asm.getContext().FatalError(Fixup.getLoc(),
                                            "unable to resolve variable '" +
                                            Symbol->getName() + "'");
            return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
                                    FixedValue);
        }

        // Relocations inside debug sections always use local relocations when
        // possible. This seems to be done because the debugger doesn't fully
        // understand relocation entries and expects to find values that
        // have already been fixed up.
        if (Symbol->isInSection()) {
            if (Section.hasAttribute(MachO::S_ATTR_DEBUG))
                Base = nullptr;
        }

        // AArch64 uses external relocations as much as possible. For debug
        // sections, and for pointer-sized relocations (.quad), we allow section
        // relocations.  It's code sections that run into trouble.
        if (Base) {
            RelSymbol = Base;

            // Add the local offset, if needed.
            if (Base != &SD)
                Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base);
        } else if (Symbol->isInSection()) {
            if (!CanUseLocalRelocation)
                Asm.getContext().FatalError(
                    Fixup.getLoc(),
                    "unsupported relocation of local symbol '" + Symbol->getName() +
                    "'. Must have non-local symbol earlier in section.");
            // Adjust the relocation to be section-relative.
            // The index is the section ordinal (1-based).
            const MCSectionData &SymSD =
                Asm.getSectionData(SD.getSymbol().getSection());
            Index = SymSD.getOrdinal() + 1;
            Value += Writer->getSymbolAddress(&SD, Layout);

            if (IsPCRel)
                Value -= Writer->getFragmentAddress(Fragment, Layout) +
                         Fixup.getOffset() + (1ULL << Log2Size);
        } else {
            // Resolve constant variables.
            if (SD.getSymbol().isVariable()) {
                int64_t Res;
                if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute(
                            Res, Layout, Writer->getSectionAddressMap())) {
                    FixedValue = Res;
                    return;
                }
            }
            Asm.getContext().FatalError(Fixup.getLoc(),
                                        "unsupported relocation of variable '" +
                                        Symbol->getName() + "'");
        }
    }

    // If the relocation kind is Branch26, Page21, or Pageoff12, any addend
    // is represented via an Addend relocation, not encoded directly into
    // the instruction.
    if ((Type == MachO::ARM64_RELOC_BRANCH26 ||
            Type == MachO::ARM64_RELOC_PAGE21 ||
            Type == MachO::ARM64_RELOC_PAGEOFF12) &&
            Value) {
        assert((Value & 0xff000000) == 0 && "Added relocation out of range!");

        MachO::any_relocation_info MRE;
        MRE.r_word0 = FixupOffset;
        MRE.r_word1 =
            (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
        Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);

        // Now set up the Addend relocation.
        Type = MachO::ARM64_RELOC_ADDEND;
        Index = Value;
        RelSymbol = nullptr;
        IsPCRel = 0;
        Log2Size = 2;

        // Put zero into the instruction itself. The addend is in the relocation.
        Value = 0;
    }

    // If there's any addend left to handle, encode it in the instruction.
    FixedValue = Value;

    // struct relocation_info (8 bytes)
    MachO::any_relocation_info MRE;
    MRE.r_word0 = FixupOffset;
    MRE.r_word1 =
        (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);
    Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);
}