forked from OSchip/llvm-project
900 lines
34 KiB
C++
900 lines
34 KiB
C++
//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ArchHandler.h"
|
|
#include "Atoms.h"
|
|
#include "MachONormalizedFileBinaryUtils.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
using namespace llvm::MachO;
|
|
using namespace lld::mach_o::normalized;
|
|
|
|
namespace lld {
|
|
namespace mach_o {
|
|
|
|
using llvm::support::ulittle32_t;
|
|
using llvm::support::ulittle64_t;
|
|
|
|
using llvm::support::little32_t;
|
|
using llvm::support::little64_t;
|
|
|
|
class ArchHandler_x86_64 : public ArchHandler {
|
|
public:
|
|
ArchHandler_x86_64() = default;
|
|
~ArchHandler_x86_64() override = default;
|
|
|
|
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
|
|
|
|
Reference::KindArch kindArch() override {
|
|
return Reference::KindArch::x86_64;
|
|
}
|
|
|
|
/// Used by GOTPass to locate GOT References
|
|
bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
|
|
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
|
return false;
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
switch (ref.kindValue()) {
|
|
case ripRel32GotLoad:
|
|
canBypassGOT = true;
|
|
return true;
|
|
case ripRel32Got:
|
|
canBypassGOT = false;
|
|
return true;
|
|
case imageOffsetGot:
|
|
canBypassGOT = false;
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool isTLVAccess(const Reference &ref) const override {
|
|
assert(ref.kindNamespace() == Reference::KindNamespace::mach_o);
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
return ref.kindValue() == ripRel32Tlv;
|
|
}
|
|
|
|
void updateReferenceToTLV(const Reference *ref) override {
|
|
assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
|
|
assert(ref->kindArch() == Reference::KindArch::x86_64);
|
|
assert(ref->kindValue() == ripRel32Tlv);
|
|
const_cast<Reference*>(ref)->setKindValue(ripRel32);
|
|
}
|
|
|
|
/// Used by GOTPass to update GOT References
|
|
void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
|
|
assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
|
|
assert(ref->kindArch() == Reference::KindArch::x86_64);
|
|
|
|
switch (ref->kindValue()) {
|
|
case ripRel32Got:
|
|
assert(targetNowGOT && "target must be GOT");
|
|
LLVM_FALLTHROUGH;
|
|
case ripRel32GotLoad:
|
|
const_cast<Reference *>(ref)
|
|
->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea);
|
|
break;
|
|
case imageOffsetGot:
|
|
const_cast<Reference *>(ref)->setKindValue(imageOffset);
|
|
break;
|
|
default:
|
|
llvm_unreachable("unknown GOT reference kind");
|
|
}
|
|
}
|
|
|
|
bool needsCompactUnwind() override {
|
|
return true;
|
|
}
|
|
|
|
Reference::KindValue imageOffsetKind() override {
|
|
return imageOffset;
|
|
}
|
|
|
|
Reference::KindValue imageOffsetKindIndirect() override {
|
|
return imageOffsetGot;
|
|
}
|
|
|
|
Reference::KindValue unwindRefToPersonalityFunctionKind() override {
|
|
return ripRel32Got;
|
|
}
|
|
|
|
Reference::KindValue unwindRefToCIEKind() override {
|
|
return negDelta32;
|
|
}
|
|
|
|
Reference::KindValue unwindRefToFunctionKind() override{
|
|
return unwindFDEToFunction;
|
|
}
|
|
|
|
Reference::KindValue lazyImmediateLocationKind() override {
|
|
return lazyImmediateLocation;
|
|
}
|
|
|
|
Reference::KindValue unwindRefToEhFrameKind() override {
|
|
return unwindInfoToEhFrame;
|
|
}
|
|
|
|
Reference::KindValue pointerKind() override {
|
|
return pointer64;
|
|
}
|
|
|
|
uint32_t dwarfCompactUnwindType() override {
|
|
return 0x04000000U;
|
|
}
|
|
|
|
const StubInfo &stubInfo() override { return _sStubInfo; }
|
|
|
|
bool isNonCallBranch(const Reference &) override {
|
|
return false;
|
|
}
|
|
|
|
bool isCallSite(const Reference &) override;
|
|
bool isPointer(const Reference &) override;
|
|
bool isPairedReloc(const normalized::Relocation &) override;
|
|
|
|
llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
|
|
const DefinedAtom *inAtom,
|
|
uint32_t offsetInAtom,
|
|
uint64_t fixupAddress, bool swap,
|
|
FindAtomBySectionAndAddress atomFromAddress,
|
|
FindAtomBySymbolIndex atomFromSymbolIndex,
|
|
Reference::KindValue *kind,
|
|
const lld::Atom **target,
|
|
Reference::Addend *addend) override;
|
|
llvm::Error
|
|
getPairReferenceInfo(const normalized::Relocation &reloc1,
|
|
const normalized::Relocation &reloc2,
|
|
const DefinedAtom *inAtom,
|
|
uint32_t offsetInAtom,
|
|
uint64_t fixupAddress, bool swap, bool scatterable,
|
|
FindAtomBySectionAndAddress atomFromAddress,
|
|
FindAtomBySymbolIndex atomFromSymbolIndex,
|
|
Reference::KindValue *kind,
|
|
const lld::Atom **target,
|
|
Reference::Addend *addend) override;
|
|
|
|
bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
|
|
return (atom->contentType() == DefinedAtom::typeCString);
|
|
}
|
|
|
|
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
|
|
FindAddressForAtom findAddress,
|
|
FindAddressForAtom findSectionAddress,
|
|
uint64_t imageBase,
|
|
llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
|
|
|
|
void appendSectionRelocations(const DefinedAtom &atom,
|
|
uint64_t atomSectionOffset,
|
|
const Reference &ref,
|
|
FindSymbolIndexForAtom symbolIndexForAtom,
|
|
FindSectionIndexForAtom sectionIndexForAtom,
|
|
FindAddressForAtom addressForAtom,
|
|
normalized::Relocations &relocs) override;
|
|
|
|
bool isDataInCodeTransition(Reference::KindValue refKind) override {
|
|
return refKind == modeCode || refKind == modeData;
|
|
}
|
|
|
|
Reference::KindValue dataInCodeTransitionStart(
|
|
const MachODefinedAtom &atom) override {
|
|
return modeData;
|
|
}
|
|
|
|
Reference::KindValue dataInCodeTransitionEnd(
|
|
const MachODefinedAtom &atom) override {
|
|
return modeCode;
|
|
}
|
|
|
|
private:
|
|
static const Registry::KindStrings _sKindStrings[];
|
|
static const StubInfo _sStubInfo;
|
|
|
|
enum X86_64Kind: Reference::KindValue {
|
|
invalid, /// for error condition
|
|
|
|
modeCode, /// Content starting at this offset is code.
|
|
modeData, /// Content starting at this offset is data.
|
|
|
|
// Kinds found in mach-o .o files:
|
|
branch32, /// ex: call _foo
|
|
ripRel32, /// ex: movq _foo(%rip), %rax
|
|
ripRel32Minus1, /// ex: movb $0x12, _foo(%rip)
|
|
ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip)
|
|
ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip)
|
|
ripRel32Anon, /// ex: movq L1(%rip), %rax
|
|
ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip)
|
|
ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip)
|
|
ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip)
|
|
ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax
|
|
ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip)
|
|
ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi
|
|
pointer64, /// ex: .quad _foo
|
|
pointer64Anon, /// ex: .quad L1
|
|
delta64, /// ex: .quad _foo - .
|
|
delta32, /// ex: .long _foo - .
|
|
delta64Anon, /// ex: .quad L1 - .
|
|
delta32Anon, /// ex: .long L1 - .
|
|
negDelta64, /// ex: .quad . - _foo
|
|
negDelta32, /// ex: .long . - _foo
|
|
|
|
// Kinds introduced by Passes:
|
|
ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so
|
|
/// "movq _foo@GOTPCREL(%rip), %rax" can be changed
|
|
/// to "leaq _foo(%rip), %rax
|
|
lazyPointer, /// Location contains a lazy pointer.
|
|
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
|
|
|
imageOffset, /// Location contains offset of atom in final image
|
|
imageOffsetGot, /// Location contains offset of GOT entry for atom in
|
|
/// final image (typically personality function).
|
|
unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
|
|
/// relocatable object (yay for implicit contracts!).
|
|
unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
|
|
/// refer to __eh_frame entry.
|
|
tlvInitSectionOffset /// Location contains offset tlv init-value atom
|
|
/// within the __thread_data section.
|
|
};
|
|
|
|
Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
|
|
|
|
void applyFixupFinal(const Reference &ref, uint8_t *location,
|
|
uint64_t fixupAddress, uint64_t targetAddress,
|
|
uint64_t inAtomAddress, uint64_t imageBaseAddress,
|
|
FindAddressForAtom findSectionAddress);
|
|
|
|
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
|
|
uint64_t fixupAddress,
|
|
uint64_t targetAddress,
|
|
uint64_t inAtomAddress);
|
|
};
|
|
|
|
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
|
|
LLD_KIND_STRING_ENTRY(invalid),
|
|
LLD_KIND_STRING_ENTRY(modeCode),
|
|
LLD_KIND_STRING_ENTRY(modeData),
|
|
LLD_KIND_STRING_ENTRY(branch32),
|
|
LLD_KIND_STRING_ENTRY(ripRel32),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Minus1),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Minus2),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Minus4),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Anon),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
|
|
LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
|
|
LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Got),
|
|
LLD_KIND_STRING_ENTRY(ripRel32Tlv),
|
|
LLD_KIND_STRING_ENTRY(lazyPointer),
|
|
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
|
LLD_KIND_STRING_ENTRY(pointer64),
|
|
LLD_KIND_STRING_ENTRY(pointer64Anon),
|
|
LLD_KIND_STRING_ENTRY(delta32),
|
|
LLD_KIND_STRING_ENTRY(delta64),
|
|
LLD_KIND_STRING_ENTRY(delta32Anon),
|
|
LLD_KIND_STRING_ENTRY(delta64Anon),
|
|
LLD_KIND_STRING_ENTRY(negDelta64),
|
|
LLD_KIND_STRING_ENTRY(negDelta32),
|
|
LLD_KIND_STRING_ENTRY(imageOffset),
|
|
LLD_KIND_STRING_ENTRY(imageOffsetGot),
|
|
LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
|
|
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
|
|
LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
|
|
LLD_KIND_STRING_END
|
|
};
|
|
|
|
const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = {
|
|
"dyld_stub_binder",
|
|
|
|
// Lazy pointer references
|
|
{ Reference::KindArch::x86_64, pointer64, 0, 0 },
|
|
{ Reference::KindArch::x86_64, lazyPointer, 0, 0 },
|
|
|
|
// GOT pointer to dyld_stub_binder
|
|
{ Reference::KindArch::x86_64, pointer64, 0, 0 },
|
|
|
|
// x86_64 code alignment 2^1
|
|
1,
|
|
|
|
// Stub size and code
|
|
6,
|
|
{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
|
|
{ Reference::KindArch::x86_64, ripRel32, 2, 0 },
|
|
{ false, 0, 0, 0 },
|
|
|
|
// Stub Helper size and code
|
|
10,
|
|
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset
|
|
0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
|
|
{ Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 },
|
|
{ Reference::KindArch::x86_64, branch32, 6, 0 },
|
|
|
|
// Stub helper image cache content type
|
|
DefinedAtom::typeNonLazyPointer,
|
|
|
|
// Stub Helper-Common size and code
|
|
16,
|
|
// Stub helper alignment
|
|
2,
|
|
{ 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11
|
|
0x41, 0x53, // push %r11
|
|
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip)
|
|
0x90 }, // nop
|
|
{ Reference::KindArch::x86_64, ripRel32, 3, 0 },
|
|
{ false, 0, 0, 0 },
|
|
{ Reference::KindArch::x86_64, ripRel32, 11, 0 },
|
|
{ false, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
bool ArchHandler_x86_64::isCallSite(const Reference &ref) {
|
|
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
|
return false;
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
return (ref.kindValue() == branch32);
|
|
}
|
|
|
|
bool ArchHandler_x86_64::isPointer(const Reference &ref) {
|
|
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
|
return false;
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
Reference::KindValue kind = ref.kindValue();
|
|
return (kind == pointer64 || kind == pointer64Anon);
|
|
}
|
|
|
|
bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) {
|
|
return (reloc.type == X86_64_RELOC_SUBTRACTOR);
|
|
}
|
|
|
|
Reference::KindValue
|
|
ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) {
|
|
switch(relocPattern(reloc)) {
|
|
case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4:
|
|
return branch32;
|
|
case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4:
|
|
return ripRel32;
|
|
case X86_64_RELOC_SIGNED | rPcRel | rLength4:
|
|
return ripRel32Anon;
|
|
case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4:
|
|
return ripRel32Minus1;
|
|
case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4:
|
|
return ripRel32Minus1Anon;
|
|
case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4:
|
|
return ripRel32Minus2;
|
|
case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4:
|
|
return ripRel32Minus2Anon;
|
|
case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4:
|
|
return ripRel32Minus4;
|
|
case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4:
|
|
return ripRel32Minus4Anon;
|
|
case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4:
|
|
return ripRel32GotLoad;
|
|
case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4:
|
|
return ripRel32Got;
|
|
case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4:
|
|
return ripRel32Tlv;
|
|
case X86_64_RELOC_UNSIGNED | rExtern | rLength8:
|
|
return pointer64;
|
|
case X86_64_RELOC_UNSIGNED | rLength8:
|
|
return pointer64Anon;
|
|
default:
|
|
return invalid;
|
|
}
|
|
}
|
|
|
|
llvm::Error
|
|
ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
|
|
const DefinedAtom *inAtom,
|
|
uint32_t offsetInAtom,
|
|
uint64_t fixupAddress, bool swap,
|
|
FindAtomBySectionAndAddress atomFromAddress,
|
|
FindAtomBySymbolIndex atomFromSymbolIndex,
|
|
Reference::KindValue *kind,
|
|
const lld::Atom **target,
|
|
Reference::Addend *addend) {
|
|
*kind = kindFromReloc(reloc);
|
|
if (*kind == invalid)
|
|
return llvm::make_error<GenericError>("unknown type");
|
|
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
|
uint64_t targetAddress;
|
|
switch (*kind) {
|
|
case branch32:
|
|
case ripRel32:
|
|
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
|
return ec;
|
|
*addend = *(const little32_t *)fixupContent;
|
|
return llvm::Error::success();
|
|
case ripRel32Minus1:
|
|
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
|
return ec;
|
|
*addend = (int32_t)*(const little32_t *)fixupContent + 1;
|
|
return llvm::Error::success();
|
|
case ripRel32Minus2:
|
|
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
|
return ec;
|
|
*addend = (int32_t)*(const little32_t *)fixupContent + 2;
|
|
return llvm::Error::success();
|
|
case ripRel32Minus4:
|
|
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
|
return ec;
|
|
*addend = (int32_t)*(const little32_t *)fixupContent + 4;
|
|
return llvm::Error::success();
|
|
case ripRel32Anon:
|
|
targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent;
|
|
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
|
case ripRel32Minus1Anon:
|
|
targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent;
|
|
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
|
case ripRel32Minus2Anon:
|
|
targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent;
|
|
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
|
case ripRel32Minus4Anon:
|
|
targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent;
|
|
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
|
case ripRel32GotLoad:
|
|
case ripRel32Got:
|
|
case ripRel32Tlv:
|
|
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
|
return ec;
|
|
*addend = *(const little32_t *)fixupContent;
|
|
return llvm::Error::success();
|
|
case tlvInitSectionOffset:
|
|
case pointer64:
|
|
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
|
|
return ec;
|
|
// If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's
|
|
// initial value) we need to handle it specially.
|
|
if (inAtom->contentType() == DefinedAtom::typeThunkTLV &&
|
|
offsetInAtom == 16) {
|
|
*kind = tlvInitSectionOffset;
|
|
assert(*addend == 0 && "TLV-init has non-zero addend?");
|
|
} else
|
|
*addend = *(const little64_t *)fixupContent;
|
|
return llvm::Error::success();
|
|
case pointer64Anon:
|
|
targetAddress = *(const little64_t *)fixupContent;
|
|
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
|
default:
|
|
llvm_unreachable("bad reloc kind");
|
|
}
|
|
}
|
|
|
|
llvm::Error
|
|
ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
|
|
const normalized::Relocation &reloc2,
|
|
const DefinedAtom *inAtom,
|
|
uint32_t offsetInAtom,
|
|
uint64_t fixupAddress, bool swap,
|
|
bool scatterable,
|
|
FindAtomBySectionAndAddress atomFromAddress,
|
|
FindAtomBySymbolIndex atomFromSymbolIndex,
|
|
Reference::KindValue *kind,
|
|
const lld::Atom **target,
|
|
Reference::Addend *addend) {
|
|
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
|
uint64_t targetAddress;
|
|
const lld::Atom *fromTarget;
|
|
if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget))
|
|
return ec;
|
|
|
|
switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
|
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength8): {
|
|
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
|
return ec;
|
|
uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent;
|
|
if (inAtom == fromTarget) {
|
|
if (inAtom->contentType() == DefinedAtom::typeCFI)
|
|
*kind = unwindFDEToFunction;
|
|
else
|
|
*kind = delta64;
|
|
*addend = encodedAddend + offsetInAtom;
|
|
} else if (inAtom == *target) {
|
|
*kind = negDelta64;
|
|
*addend = encodedAddend - offsetInAtom;
|
|
*target = fromTarget;
|
|
} else
|
|
return llvm::make_error<GenericError>("Invalid pointer diff");
|
|
return llvm::Error::success();
|
|
}
|
|
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength4): {
|
|
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
|
|
return ec;
|
|
uint32_t encodedAddend = (int32_t)*(const little32_t *)fixupContent;
|
|
if (inAtom == fromTarget) {
|
|
*kind = delta32;
|
|
*addend = encodedAddend + offsetInAtom;
|
|
} else if (inAtom == *target) {
|
|
*kind = negDelta32;
|
|
*addend = encodedAddend - offsetInAtom;
|
|
*target = fromTarget;
|
|
} else
|
|
return llvm::make_error<GenericError>("Invalid pointer diff");
|
|
return llvm::Error::success();
|
|
}
|
|
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
|
X86_64_RELOC_UNSIGNED | rLength8):
|
|
if (fromTarget != inAtom)
|
|
return llvm::make_error<GenericError>("pointer diff not in base atom");
|
|
*kind = delta64Anon;
|
|
targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent;
|
|
return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
|
|
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
|
X86_64_RELOC_UNSIGNED | rLength4):
|
|
if (fromTarget != inAtom)
|
|
return llvm::make_error<GenericError>("pointer diff not in base atom");
|
|
*kind = delta32Anon;
|
|
targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent;
|
|
return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
|
|
default:
|
|
return llvm::make_error<GenericError>("unknown pair");
|
|
}
|
|
}
|
|
|
|
void ArchHandler_x86_64::generateAtomContent(
|
|
const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
|
|
FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
|
|
llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
|
|
// Copy raw bytes.
|
|
std::copy(atom.rawContent().begin(), atom.rawContent().end(),
|
|
atomContentBuffer.begin());
|
|
// Apply fix-ups.
|
|
for (const Reference *ref : atom) {
|
|
uint32_t offset = ref->offsetInAtom();
|
|
const Atom *target = ref->target();
|
|
uint64_t targetAddress = 0;
|
|
if (isa<DefinedAtom>(target))
|
|
targetAddress = findAddress(*target);
|
|
uint64_t atomAddress = findAddress(atom);
|
|
uint64_t fixupAddress = atomAddress + offset;
|
|
if (relocatable) {
|
|
applyFixupRelocatable(*ref, &atomContentBuffer[offset],
|
|
fixupAddress, targetAddress,
|
|
atomAddress);
|
|
} else {
|
|
applyFixupFinal(*ref, &atomContentBuffer[offset],
|
|
fixupAddress, targetAddress,
|
|
atomAddress, imageBaseAddress, findSectionAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArchHandler_x86_64::applyFixupFinal(
|
|
const Reference &ref, uint8_t *loc, uint64_t fixupAddress,
|
|
uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress,
|
|
FindAddressForAtom findSectionAddress) {
|
|
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
|
return;
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
|
ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
|
|
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
|
case branch32:
|
|
case ripRel32:
|
|
case ripRel32Anon:
|
|
case ripRel32Got:
|
|
case ripRel32GotLoad:
|
|
case ripRel32Tlv:
|
|
*loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
|
|
return;
|
|
case pointer64:
|
|
case pointer64Anon:
|
|
*loc64 = targetAddress + ref.addend();
|
|
return;
|
|
case tlvInitSectionOffset:
|
|
*loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
|
|
return;
|
|
case ripRel32Minus1:
|
|
case ripRel32Minus1Anon:
|
|
*loc32 = targetAddress - (fixupAddress + 5) + ref.addend();
|
|
return;
|
|
case ripRel32Minus2:
|
|
case ripRel32Minus2Anon:
|
|
*loc32 = targetAddress - (fixupAddress + 6) + ref.addend();
|
|
return;
|
|
case ripRel32Minus4:
|
|
case ripRel32Minus4Anon:
|
|
*loc32 = targetAddress - (fixupAddress + 8) + ref.addend();
|
|
return;
|
|
case delta32:
|
|
case delta32Anon:
|
|
*loc32 = targetAddress - fixupAddress + ref.addend();
|
|
return;
|
|
case delta64:
|
|
case delta64Anon:
|
|
case unwindFDEToFunction:
|
|
*loc64 = targetAddress - fixupAddress + ref.addend();
|
|
return;
|
|
case ripRel32GotLoadNowLea:
|
|
// Change MOVQ to LEA
|
|
assert(loc[-2] == 0x8B);
|
|
loc[-2] = 0x8D;
|
|
*loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
|
|
return;
|
|
case negDelta64:
|
|
*loc64 = fixupAddress - targetAddress + ref.addend();
|
|
return;
|
|
case negDelta32:
|
|
*loc32 = fixupAddress - targetAddress + ref.addend();
|
|
return;
|
|
case modeCode:
|
|
case modeData:
|
|
case lazyPointer:
|
|
// Do nothing
|
|
return;
|
|
case lazyImmediateLocation:
|
|
*loc32 = ref.addend();
|
|
return;
|
|
case imageOffset:
|
|
case imageOffsetGot:
|
|
*loc32 = (targetAddress - imageBaseAddress) + ref.addend();
|
|
return;
|
|
case unwindInfoToEhFrame: {
|
|
uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
|
|
assert(val < 0xffffffU && "offset in __eh_frame too large");
|
|
*loc32 = (*loc32 & 0xff000000U) | val;
|
|
return;
|
|
}
|
|
case invalid:
|
|
// Fall into llvm_unreachable().
|
|
break;
|
|
}
|
|
llvm_unreachable("invalid x86_64 Reference Kind");
|
|
}
|
|
|
|
void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
|
|
uint8_t *loc,
|
|
uint64_t fixupAddress,
|
|
uint64_t targetAddress,
|
|
uint64_t inAtomAddress) {
|
|
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
|
return;
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
|
|
ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
|
|
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
|
case branch32:
|
|
case ripRel32:
|
|
case ripRel32Got:
|
|
case ripRel32GotLoad:
|
|
case ripRel32Tlv:
|
|
*loc32 = ref.addend();
|
|
return;
|
|
case ripRel32Anon:
|
|
*loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
|
|
return;
|
|
case tlvInitSectionOffset:
|
|
case pointer64:
|
|
*loc64 = ref.addend();
|
|
return;
|
|
case pointer64Anon:
|
|
*loc64 = targetAddress + ref.addend();
|
|
return;
|
|
case ripRel32Minus1:
|
|
*loc32 = ref.addend() - 1;
|
|
return;
|
|
case ripRel32Minus1Anon:
|
|
*loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend();
|
|
return;
|
|
case ripRel32Minus2:
|
|
*loc32 = ref.addend() - 2;
|
|
return;
|
|
case ripRel32Minus2Anon:
|
|
*loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend();
|
|
return;
|
|
case ripRel32Minus4:
|
|
*loc32 = ref.addend() - 4;
|
|
return;
|
|
case ripRel32Minus4Anon:
|
|
*loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend();
|
|
return;
|
|
case delta32:
|
|
*loc32 = ref.addend() + inAtomAddress - fixupAddress;
|
|
return;
|
|
case delta32Anon:
|
|
// The value we write here should be the delta to the target
|
|
// after taking in to account the difference from the fixup back to the
|
|
// last defined label
|
|
// ie, if we have:
|
|
// _base: ...
|
|
// Lfixup: .quad Ltarget - .
|
|
// ...
|
|
// Ltarget:
|
|
//
|
|
// Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
|
|
*loc32 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
|
|
return;
|
|
case delta64:
|
|
*loc64 = ref.addend() + inAtomAddress - fixupAddress;
|
|
return;
|
|
case delta64Anon:
|
|
// The value we write here should be the delta to the target
|
|
// after taking in to account the difference from the fixup back to the
|
|
// last defined label
|
|
// ie, if we have:
|
|
// _base: ...
|
|
// Lfixup: .quad Ltarget - .
|
|
// ...
|
|
// Ltarget:
|
|
//
|
|
// Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
|
|
*loc64 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
|
|
return;
|
|
case negDelta64:
|
|
*loc64 = ref.addend() + fixupAddress - inAtomAddress;
|
|
return;
|
|
case negDelta32:
|
|
*loc32 = ref.addend() + fixupAddress - inAtomAddress;
|
|
return;
|
|
case ripRel32GotLoadNowLea:
|
|
llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
|
|
return;
|
|
case lazyPointer:
|
|
case lazyImmediateLocation:
|
|
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
|
return;
|
|
case imageOffset:
|
|
case imageOffsetGot:
|
|
case unwindInfoToEhFrame:
|
|
llvm_unreachable("fixup implies __unwind_info");
|
|
return;
|
|
case modeCode:
|
|
case modeData:
|
|
case unwindFDEToFunction:
|
|
// Do nothing for now
|
|
return;
|
|
case invalid:
|
|
// Fall into llvm_unreachable().
|
|
break;
|
|
}
|
|
llvm_unreachable("unknown x86_64 Reference Kind");
|
|
}
|
|
|
|
void ArchHandler_x86_64::appendSectionRelocations(
|
|
const DefinedAtom &atom,
|
|
uint64_t atomSectionOffset,
|
|
const Reference &ref,
|
|
FindSymbolIndexForAtom symbolIndexForAtom,
|
|
FindSectionIndexForAtom sectionIndexForAtom,
|
|
FindAddressForAtom addressForAtom,
|
|
normalized::Relocations &relocs) {
|
|
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
|
return;
|
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
|
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
|
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
|
case modeCode:
|
|
case modeData:
|
|
return;
|
|
case branch32:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
|
|
return;
|
|
case ripRel32:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case ripRel32Anon:
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED | rPcRel | rLength4 );
|
|
return;
|
|
case ripRel32Got:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case ripRel32GotLoad:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case ripRel32Tlv:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case tlvInitSectionOffset:
|
|
case pointer64:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength8);
|
|
return;
|
|
case pointer64Anon:
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_UNSIGNED | rLength8);
|
|
return;
|
|
case ripRel32Minus1:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case ripRel32Minus1Anon:
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 );
|
|
return;
|
|
case ripRel32Minus2:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case ripRel32Minus2Anon:
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 );
|
|
return;
|
|
case ripRel32Minus4:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 );
|
|
return;
|
|
case ripRel32Minus4Anon:
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 );
|
|
return;
|
|
case delta32:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
|
X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
|
|
return;
|
|
case delta32Anon:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
|
X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_UNSIGNED | rLength4 );
|
|
return;
|
|
case delta64:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
|
X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
|
|
return;
|
|
case delta64Anon:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
|
X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
|
|
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_UNSIGNED | rLength8 );
|
|
return;
|
|
case unwindFDEToFunction:
|
|
case unwindInfoToEhFrame:
|
|
return;
|
|
case negDelta32:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
|
|
return;
|
|
case negDelta64:
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
|
X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
|
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
|
|
X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
|
|
return;
|
|
case ripRel32GotLoadNowLea:
|
|
llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
|
|
return;
|
|
case lazyPointer:
|
|
case lazyImmediateLocation:
|
|
llvm_unreachable("lazy reference kind implies Stubs pass was run");
|
|
return;
|
|
case imageOffset:
|
|
case imageOffsetGot:
|
|
llvm_unreachable("__unwind_info references should have been resolved");
|
|
return;
|
|
case invalid:
|
|
// Fall into llvm_unreachable().
|
|
break;
|
|
}
|
|
llvm_unreachable("unknown x86_64 Reference Kind");
|
|
}
|
|
|
|
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86_64() {
|
|
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86_64());
|
|
}
|
|
|
|
} // namespace mach_o
|
|
} // namespace lld
|