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

  // If this is a 32-bit TLVP reloc it's handled a bit differently.
  if (Target.getSymA() &&
      Target.getSymA()->getKind() == MCSymbolRefExpr::VK_TLVP) {
    RecordTLVPRelocation(Writer, Asm, Layout, Fragment, Fixup, Target,
                         FixedValue);
    return;
  }

  // If this is a difference or a defined symbol plus an offset, then we need a
  // scattered relocation entry. Differences always require scattered
  // relocations.
  if (Target.getSymB()) {
    RecordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
                              Target, Log2Size, FixedValue);
    return;
  }

  // Get the symbol data, if any.
  const MCSymbolData *SD = nullptr;
  if (Target.getSymA())
    SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());

  // If this is an internal relocation with an offset, it also needs a scattered
  // relocation entry.
  uint32_t Offset = Target.getConstant();
  if (IsPCRel)
    Offset += 1 << Log2Size;
  // Try to record the scattered relocation if needed. Fall back to non
  // scattered if necessary (see comments in RecordScatteredRelocation()
  // for details).
  if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD) &&
      RecordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
                                Target, Log2Size, FixedValue))
    return;

  // See <reloc.h>.
  uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
  unsigned Index = 0;
  unsigned Type = 0;
  const MCSymbol *RelSymbol = nullptr;

  if (Target.isAbsolute()) { // constant
    // SymbolNum of 0 indicates the absolute section.
    //
    // FIXME: Currently, these are never generated (see code below). I cannot
    // find a case where they are actually emitted.
    Type = MachO::GENERIC_RELOC_VANILLA;
  } else {
    // Resolve constant variables.
    if (SD->getSymbol().isVariable()) {
      int64_t Res;
      if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
            Res, Layout, Writer->getSectionAddressMap())) {
        FixedValue = Res;
        return;
      }
    }

    // Check whether we need an external or internal relocation.
    if (Writer->doesSymbolRequireExternRelocation(SD)) {
      RelSymbol = &SD->getSymbol();
      // For external relocations, make sure to offset the fixup value to
      // compensate for the addend of the symbol address, if it was
      // undefined. This occurs with weak definitions, for example.
      if (!SD->getSymbol().isUndefined())
        FixedValue -= Layout.getSymbolOffset(SD);
    } else {
      // The index is the section ordinal (1-based).
      const MCSectionData &SymSD = Asm.getSectionData(
        SD->getSymbol().getSection());
      Index = SymSD.getOrdinal() + 1;
      FixedValue += Writer->getSectionAddress(&SymSD);
    }
    if (IsPCRel)
      FixedValue -= Writer->getSectionAddress(Fragment->getParent());

    Type = MachO::GENERIC_RELOC_VANILLA;
  }

  // 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);
}
SVMSymbolInfo SVMMemoryLayout::getSymbol(const MCAssembler &Asm,
    const MCAsmLayout &Layout, const MCSymbol *S, bool useCodeAddresses) const
{
    SVMSymbolInfo SI;
    SVMDecorations Deco;
    StringRef Name = Deco.Decode(S->getName());
    const MCSymbol *AS = &S->AliasedSymbol();
    const MCSymbolData *SD = &Asm.getSymbolData(*AS);

    if (Deco.isSys) {
        // Numeric syscall

        if (Deco.sysNumber > 0x3FFF)
            report_fatal_error("Syscall number " + Twine(Deco.sysNumber) +
                " is out of range.");

        SI.Kind = SVMSymbolInfo::SYS;
        SI.Value = 0x80000000 | (Deco.sysNumber << 16) | Deco.isTailCall;
        return SI;
    }

    if (!AS->isDefined())
        report_fatal_error("Taking address of undefined symbol '" +
            Twine(Name) + "'");

    // Symbol has a value in our module. Calculate the full virtual address.
    uint32_t Offset = Layout.getSymbolOffset(SD);
    const MCSectionData *SecD = &Asm.getSectionData(AS->getSection());
    uint32_t VA = getSectionMemAddress(SecD) + Offset + Deco.offset;

    if (useCodeAddresses && AS->getSection().getKind().isText()) {
        /*
         * Code address:
         *   - Relative to segment base
         *   - Must be 32-bit aligned
         *   - Includes optional SP adjustment from FNSTACK pseudo-ops
         *   - Includes optional call / tail-call decoration
         */
         
        assert(Deco.offset == 0);
        uint32_t shortVA = VA & 0xfffffc;

        /*
         * We can reach this error if some code wasn't aligned properly,
         * or if someone is calling this function with useCodeAddresses==true
         * when they shouldn't be!
         */
        if ((VA & 0xfffffffc) != VA)
            report_fatal_error("Code symbol '" + Twine(Name) +
                "' has illegal address 0x" + Twine::utohexstr(VA));

        FNStackMap_t::const_iterator I = FNStackMap.find(std::make_pair(SecD, Offset));
        int SPAdj = I == FNStackMap.end() ? 0 : I->second;
        assert(SPAdj >= 0 && !(SPAdj & 3) && SPAdj <= (0x7F * 4));
        SPAdj <<= 22;

        if (Deco.isCall) {
            // A Call, with SP adjustment and tail-call flag
            SI.Value = shortVA | SPAdj | Deco.isTailCall;
            SI.Kind = SVMSymbolInfo::CALL;

        } else if (Deco.isLongBranch) {
            // Encode the Long Branch addrop
            SI.Value = 0xE0000000 | shortVA;
            SI.Kind = SVMSymbolInfo::LB;

        } else {
            // Normal undecorated address. Still includes an SP adjustment
            // if one is present for this symbol's address, though.
            // This is important for function pointers, including the entry
            // point address for main().
            //
            // We also set the LSB, in order to differentiate a valid function
            // address from a possible NULL. We could set either of the two LSBs
            // or the MSB, but this approach makes function addresses clearly
            // distinct from normal VAs even with SPAdj==0.

            SI.Value = shortVA | SPAdj | 1;
            SI.Kind = SVMSymbolInfo::LOCAL;
        }

    } else {
        /*
         * Data address. No decoration at all.
         */

        SI.Value = VA;
        SI.Kind = SVMSymbolInfo::LOCAL;
    }

    return SI;
}