[mach-o] Add support for arm64 (AAarch64)

Most of the changes are in the new file ArchHandler_arm64.cpp.  But a few
things had to be fixed to support 16KB pages (instead of 4KB) which iOS arm64
requires.  In addition the StubInfo struct had to be expanded because
arm64 uses two instruction (ADRP/LDR) to load a global which requires two
relocations.  The other mach-o arches just needed one relocation.

llvm-svn: 217469
This commit is contained in:
Nick Kledzik 2014-09-09 23:52:59 +00:00
parent d0f103775a
commit 1bebb2832e
18 changed files with 1391 additions and 26 deletions

View File

@ -43,6 +43,7 @@ public:
arch_armv6,
arch_armv7,
arch_armv7s,
arch_arm64,
};
enum class OS {

View File

@ -42,6 +42,8 @@ std::unique_ptr<mach_o::ArchHandler> ArchHandler::create(
case MachOLinkingContext::arch_armv7:
case MachOLinkingContext::arch_armv7s:
return create_arm();
case MachOLinkingContext::arch_arm64:
return create_arm64();
default:
llvm_unreachable("Unknown arch");
}

View File

@ -174,6 +174,13 @@ public:
int32_t addend;
};
struct OptionalRefInfo {
bool used;
uint16_t kind;
uint32_t offset;
int32_t addend;
};
/// Table of architecture specific information for creating stubs.
struct StubInfo {
const char* binderSymbolName;
@ -185,16 +192,19 @@ public:
uint32_t stubSize;
uint8_t stubBytes[16];
ReferenceInfo stubReferenceToLP;
OptionalRefInfo optStubReferenceToLP;
uint32_t stubHelperSize;
uint8_t stubHelperBytes[16];
ReferenceInfo stubHelperReferenceToImm;
ReferenceInfo stubHelperReferenceToHelperCommon;
uint32_t stubHelperCommonSize;
uint8_t stubHelperCommonBytes[36];
ReferenceInfo stubHelperCommonReferenceToCache;
OptionalRefInfo optStubHelperCommonReferenceToCache;
ReferenceInfo stubHelperCommonReferenceToBinder;
OptionalRefInfo optStubHelperCommonReferenceToBinder;
};
virtual const StubInfo &stubInfo() = 0;
@ -205,6 +215,7 @@ protected:
static std::unique_ptr<mach_o::ArchHandler> create_x86_64();
static std::unique_ptr<mach_o::ArchHandler> create_x86();
static std::unique_ptr<mach_o::ArchHandler> create_arm();
static std::unique_ptr<mach_o::ArchHandler> create_arm64();
// Handy way to pack mach-o r_type and other bit fields into one 16-bit value.
typedef uint16_t RelocPattern;

View File

@ -208,6 +208,7 @@ const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = {
0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip]
0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8)
{ Reference::KindArch::ARM, delta32, 12, 0 },
{ false, 0, 0, 0 },
// Stub Helper size and code
12,
@ -232,7 +233,9 @@ const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = {
0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16)
0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28)
{ Reference::KindArch::ARM, delta32, 28, 0xC },
{ Reference::KindArch::ARM, delta32, 32, 0x04 }
{ false, 0, 0, 0 },
{ Reference::KindArch::ARM, delta32, 32, 0x04 },
{ false, 0, 0, 0 }
};
const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() {

View File

@ -0,0 +1,727 @@
//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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/ErrorHandling.h"
#include "llvm/Support/Format.h"
using namespace llvm::MachO;
using namespace lld::mach_o::normalized;
namespace lld {
namespace mach_o {
class ArchHandler_arm64 : public ArchHandler {
public:
ArchHandler_arm64();
virtual ~ArchHandler_arm64();
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
Reference::KindArch kindArch() override {
return Reference::KindArch::AArch64;
}
/// 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::AArch64);
switch (ref.kindValue()) {
case gotPage21:
case gotOffset12:
canBypassGOT = true;
return true;
default:
return false;
}
}
/// Used by GOTPass to update GOT References.
void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
// If GOT slot was instanciated, transform:
// gotPage21/gotOffset12 -> page21/offset12scale8
// If GOT slot optimized away, transform:
// gotPage21/gotOffset12 -> page21/addOffset12
assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
assert(ref->kindArch() == Reference::KindArch::AArch64);
switch (ref->kindValue()) {
case gotPage21:
const_cast<Reference *>(ref)->setKindValue(page21);
break;
case gotOffset12:
const_cast<Reference *>(ref)->setKindValue(targetNowGOT ?
offset12scale8 : addOffset12);
break;
default:
llvm_unreachable("Not a GOT reference");
}
}
const StubInfo &stubInfo() override { return _sStubInfo; }
bool isCallSite(const Reference &) override;
bool isPointer(const Reference &) override;
bool isPairedReloc(const normalized::Relocation &) override;
std::error_code 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;
std::error_code
getPairReferenceInfo(const normalized::Relocation &reloc1,
const normalized::Relocation &reloc2,
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;
virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) {
return (atom->contentType() == DefinedAtom::typeCString);
}
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
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;
private:
static const Registry::KindStrings _sKindStrings[];
static const StubInfo _sStubInfo;
enum Arm64_Kinds : Reference::KindValue {
invalid, /// for error condition
// Kinds found in mach-o .o files:
branch26, /// ex: bl _foo
page21, /// ex: adrp x1, _foo@PAGE
offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF]
offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF]
offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF]
offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF]
offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF]
gotPage21, /// ex: adrp x1, _foo@GOTPAGE
gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF]
tlvPage21, /// ex: adrp x1, _foo@TLVPAGE
tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF]
pointer64, /// ex: .quad _foo
delta64, /// ex: .quad _foo - .
delta32, /// ex: .long _foo - .
pointer64ToGOT, /// ex: .quad _foo@GOT
delta32ToGOT, /// ex: .long _foo@GOT - .
// Kinds introduced by Passes:
addOffset12, /// Location contains LDR to change into ADD.
lazyPointer, /// Location contains a lazy pointer.
lazyImmediateLocation, /// Location contains immediate value used in stub.
};
void applyFixupFinal(const Reference &ref, uint8_t *location,
uint64_t fixupAddress, uint64_t targetAddress,
uint64_t inAtomAddress);
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
uint64_t fixupAddress, uint64_t targetAddress,
uint64_t inAtomAddress);
// Utility functions for inspecting/updating instructions.
static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp);
static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp);
static Arm64_Kinds offset12KindFromInstruction(uint32_t instr);
static uint32_t setImm12(uint32_t instr, uint32_t offset);
const bool _swap;
};
ArchHandler_arm64::ArchHandler_arm64()
: _swap(!MachOLinkingContext::isHostEndian(MachOLinkingContext::arch_arm64)) {
}
ArchHandler_arm64::~ArchHandler_arm64() {}
const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = {
LLD_KIND_STRING_ENTRY(invalid),
LLD_KIND_STRING_ENTRY(branch26),
LLD_KIND_STRING_ENTRY(page21),
LLD_KIND_STRING_ENTRY(offset12),
LLD_KIND_STRING_ENTRY(offset12scale2),
LLD_KIND_STRING_ENTRY(offset12scale4),
LLD_KIND_STRING_ENTRY(offset12scale8),
LLD_KIND_STRING_ENTRY(offset12scale16),
LLD_KIND_STRING_ENTRY(gotPage21),
LLD_KIND_STRING_ENTRY(gotOffset12),
LLD_KIND_STRING_ENTRY(tlvPage21),
LLD_KIND_STRING_ENTRY(tlvOffset12),
LLD_KIND_STRING_ENTRY(pointer64),
LLD_KIND_STRING_ENTRY(delta64),
LLD_KIND_STRING_ENTRY(delta32),
LLD_KIND_STRING_ENTRY(pointer64ToGOT),
LLD_KIND_STRING_ENTRY(delta32ToGOT),
LLD_KIND_STRING_ENTRY(addOffset12),
LLD_KIND_STRING_ENTRY(lazyPointer),
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
LLD_KIND_STRING_ENTRY(pointer64),
LLD_KIND_STRING_END
};
const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = {
"dyld_stub_binder",
// Lazy pointer references
{ Reference::KindArch::AArch64, pointer64, 0, 0 },
{ Reference::KindArch::AArch64, lazyPointer, 0, 0 },
// GOT pointer to dyld_stub_binder
{ Reference::KindArch::AArch64, pointer64, 0, 0 },
// arm64 code alignment 2^2
2,
// Stub size and code
12,
{ 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page
0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff]
0x00, 0x02, 0x1F, 0xD6 }, // BR X16
{ Reference::KindArch::AArch64, page21, 0, 0 },
{ true, offset12scale8, 4, 0 },
// Stub Helper size and code
12,
{ 0x50, 0x00, 0x00, 0x18, // LDR W16, L0
0x00, 0x00, 0x00, 0x14, // LDR B helperhelper
0x00, 0x00, 0x00, 0x00 }, // L0: .long 0
{ Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 },
{ Reference::KindArch::AArch64, branch26, 4, 0 },
// Stub Helper-Common size and code
24,
{ 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page
0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff
0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]!
0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page
0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff]
0x00, 0x02, 0x1F, 0xD6 }, // BR X16
{ Reference::KindArch::AArch64, page21, 0, 0 },
{ true, offset12, 4, 0 },
{ Reference::KindArch::AArch64, page21, 12, 0 },
{ true, offset12scale8, 16, 0 }
};
bool ArchHandler_arm64::isCallSite(const Reference &ref) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return false;
assert(ref.kindArch() == Reference::KindArch::AArch64);
return (ref.kindValue() == branch26);
}
bool ArchHandler_arm64::isPointer(const Reference &ref) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return false;
assert(ref.kindArch() == Reference::KindArch::AArch64);
Reference::KindValue kind = ref.kindValue();
return (kind == pointer64);
}
bool ArchHandler_arm64::isPairedReloc(const Relocation &r) {
return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR));
}
uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr,
int32_t displacement) {
assert((displacement <= 134217727) && (displacement > (-134217728)) &&
"arm64 branch out of range");
return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF);
}
uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction,
int64_t displacement) {
assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) &&
"arm64 ADRP out of range");
assert(((instruction & 0x9F000000) == 0x90000000) &&
"reloc not on ADRP instruction");
uint32_t immhi = (displacement >> 9) & (0x00FFFFE0);
uint32_t immlo = (displacement << 17) & (0x60000000);
return (instruction & 0x9F00001F) | immlo | immhi;
}
ArchHandler_arm64::Arm64_Kinds
ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) {
if (instruction & 0x08000000) {
switch ((instruction >> 30) & 0x3) {
case 0:
if ((instruction & 0x04800000) == 0x04800000)
return offset12scale16;
return offset12;
case 1:
return offset12scale2;
case 2:
return offset12scale4;
case 3:
return offset12scale8;
}
}
return offset12;
}
uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) {
assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range");
uint32_t imm12 = offset << 10;
return (instruction & 0xFFC003FF) | imm12;
}
std::error_code ArchHandler_arm64::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) {
typedef std::error_code E;
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
switch (relocPattern(reloc)) {
case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4:
// ex: bl _foo
*kind = branch26;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4:
// ex: adrp x1, _foo@PAGE
*kind = page21;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4:
// ex: ldr x0, [x1, _foo@PAGEOFF]
*kind = offset12KindFromInstruction(readS32(swap, fixupContent));
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
// ex: adrp x1, _foo@GOTPAGE
*kind = gotPage21;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4:
// ex: ldr x0, [x1, _foo@GOTPAGEOFF]
*kind = gotOffset12;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
// ex: adrp x1, _foo@TLVPAGE
*kind = tlvPage21;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4:
// ex: ldr x0, [x1, _foo@TLVPAGEOFF]
*kind = tlvOffset12;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case X86_64_RELOC_UNSIGNED | rExtern | rLength8:
// ex: .quad _foo + N
*kind = pointer64;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = readS64(swap, fixupContent);
return std::error_code();
case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8:
// ex: .quad _foo@GOT
*kind = pointer64ToGOT;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4:
// ex: .long _foo@GOT - .
*kind = delta32ToGOT;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
return std::error_code();
default:
return make_dynamic_error_code(Twine("unsupported arm relocation type"));
}
}
std::error_code ArchHandler_arm64::getPairReferenceInfo(
const normalized::Relocation &reloc1, const normalized::Relocation &reloc2,
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) {
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
const uint32_t *cont32 = reinterpret_cast<const uint32_t *>(fixupContent);
switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4):
// ex: bl _foo+8
*kind = branch26;
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = reloc1.symbol;
return std::error_code();
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4):
// ex: adrp x1, _foo@PAGE
*kind = page21;
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = reloc1.symbol;
return std::error_code();
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4):
// ex: ldr w0, [x1, _foo@PAGEOFF]
*kind = offset12KindFromInstruction(*cont32);
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = reloc1.symbol;
return std::error_code();
case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
ARM64_RELOC_UNSIGNED | rExtern | rLength8):
// ex: .quad _foo - .
*kind = delta64;
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = readS64(swap, fixupContent) + offsetInAtom;
return std::error_code();
case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
ARM64_RELOC_UNSIGNED | rExtern | rLength4):
// ex: .quad _foo - .
*kind = delta32;
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = readS32(swap, fixupContent) + offsetInAtom;
return std::error_code();
default:
return make_dynamic_error_code(Twine("unsupported arm64 relocation pair"));
}
}
void ArchHandler_arm64::generateAtomContent(const DefinedAtom &atom,
bool relocatable,
FindAddressForAtom findAddress,
uint8_t *atomContentBuffer) {
// Copy raw bytes.
memcpy(atomContentBuffer, atom.rawContent().data(), atom.size());
// 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);
}
}
}
void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *location,
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::AArch64);
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
uint64_t *loc64 = reinterpret_cast<uint64_t *>(location);
int32_t displacement;
uint32_t instruction;
uint32_t value32;
switch (static_cast<Arm64_Kinds>(ref.kindValue())) {
case branch26:
displacement = (targetAddress - fixupAddress) + ref.addend();
value32 = setDisplacementInBranch26(*loc32, displacement);
write32(*loc32, _swap, value32);
return;
case page21:
case gotPage21:
case tlvPage21:
displacement =
((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096));
value32 = setDisplacementInADRP(*loc32, displacement);
write32(*loc32, _swap, value32);
return;
case offset12:
case gotOffset12:
case tlvOffset12:
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
value32 = setImm12(*loc32, displacement);
write32(*loc32, _swap, value32);
return;
case offset12scale2:
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
assert(((displacement & 0x1) == 0) &&
"scaled imm12 not accessing 2-byte aligneds");
value32 = setImm12(*loc32, displacement >> 1);
write32(*loc32, _swap, value32);
return;
case offset12scale4:
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
assert(((displacement & 0x3) == 0) &&
"scaled imm12 not accessing 4-byte aligned");
value32 = setImm12(*loc32, displacement >> 2);
write32(*loc32, _swap, value32);
return;
case offset12scale8:
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
assert(((displacement & 0x7) == 0) &&
"scaled imm12 not accessing 8-byte aligned");
value32 = setImm12(*loc32, displacement >> 3);
write32(*loc32, _swap, value32);
return;
case offset12scale16:
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
assert(((displacement & 0xF) == 0) &&
"scaled imm12 not accessing 16-byte aligned");
value32 = setImm12(*loc32, displacement >> 4);
write32(*loc32, _swap, value32);
return;
case addOffset12:
instruction = read32(_swap, *loc32);
assert(((instruction & 0xFFC00000) == 0xF9400000) &&
"GOT reloc is not an LDR instruction");
displacement = (targetAddress + ref.addend()) & 0x00000FFF;
value32 = 0x91000000 | (instruction & 0x000003FF);
instruction = setImm12(value32, displacement);
write32(*loc32, _swap, instruction);
return;
case pointer64:
case pointer64ToGOT:
write64(*loc64, _swap, targetAddress + ref.addend());
return;
case delta64:
write64(*loc64, _swap, (targetAddress - fixupAddress) + ref.addend());
return;
case delta32:
case delta32ToGOT:
write32(*loc32, _swap, (targetAddress - fixupAddress) + ref.addend());
return;
case lazyPointer:
case lazyImmediateLocation:
// Do nothing
return;
case invalid:
// Fall into llvm_unreachable().
break;
}
llvm_unreachable("invalid arm64 Reference Kind");
}
void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref,
uint8_t *location,
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::AArch64);
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
uint64_t *loc64 = reinterpret_cast<uint64_t *>(location);
uint32_t value32;
switch (static_cast<Arm64_Kinds>(ref.kindValue())) {
case branch26:
value32 = setDisplacementInBranch26(*loc32, 0);
write32(*loc32, _swap, value32);
return;
case page21:
case gotPage21:
case tlvPage21:
value32 = setDisplacementInADRP(*loc32, 0);
write32(*loc32, _swap, value32);
return;
case offset12:
case offset12scale2:
case offset12scale4:
case offset12scale8:
case offset12scale16:
case gotOffset12:
case tlvOffset12:
value32 = setImm12(*loc32, 0);
write32(*loc32, _swap, value32);
return;
case pointer64:
write64(*loc64, _swap, ref.addend());
return;
case delta64:
write64(*loc64, _swap, ref.addend() + inAtomAddress - fixupAddress);
return;
case delta32:
write32(*loc32, _swap, ref.addend() + inAtomAddress - fixupAddress);
return;
case pointer64ToGOT:
write64(*loc64, _swap, 0);
return;
case delta32ToGOT:
write32(*loc32, _swap, -fixupAddress);
return;
case addOffset12:
llvm_unreachable("lazy reference kind implies GOT pass was run");
case lazyPointer:
case lazyImmediateLocation:
llvm_unreachable("lazy reference kind implies Stubs pass was run");
case invalid:
// Fall into llvm_unreachable().
break;
}
llvm_unreachable("unknown arm64 Reference Kind");
}
void ArchHandler_arm64::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::AArch64);
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
switch (static_cast<Arm64_Kinds>(ref.kindValue())) {
case branch26:
if (ref.addend()) {
appendReloc(relocs, sectionOffset, ref.addend(), 0,
ARM64_RELOC_ADDEND | rLength4);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
} else {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
}
return;
case page21:
if (ref.addend()) {
appendReloc(relocs, sectionOffset, ref.addend(), 0,
ARM64_RELOC_ADDEND | rLength4);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
} else {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
}
return;
case offset12:
case offset12scale2:
case offset12scale4:
case offset12scale8:
case offset12scale16:
if (ref.addend()) {
appendReloc(relocs, sectionOffset, ref.addend(), 0,
ARM64_RELOC_ADDEND | rLength4);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
} else {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
}
return;
case gotPage21:
assert(ref.addend() == 0);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
return;
case gotOffset12:
assert(ref.addend() == 0);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4);
return;
case tlvPage21:
assert(ref.addend() == 0);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
return;
case tlvOffset12:
assert(ref.addend() == 0);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4);
return;
case pointer64:
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
X86_64_RELOC_UNSIGNED | rExtern | rLength8);
return;
case delta64:
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
ARM64_RELOC_SUBTRACTOR | rExtern | rLength8);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_UNSIGNED | rExtern | rLength8);
return;
case delta32:
appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 );
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_UNSIGNED | rExtern | rLength4 );
return;
case pointer64ToGOT:
assert(ref.addend() == 0);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8);
return;
case delta32ToGOT:
assert(ref.addend() == 0);
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4);
return;
case addOffset12:
llvm_unreachable("lazy reference kind implies GOT pass was run");
case lazyPointer:
case lazyImmediateLocation:
llvm_unreachable("lazy reference kind implies Stubs pass was run");
case invalid:
// Fall into llvm_unreachable().
break;
}
llvm_unreachable("unknown arm64 Reference Kind");
}
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm64() {
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm64());
}
} // namespace mach_o
} // namespace lld

View File

@ -170,6 +170,7 @@ const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
6,
{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
{ Reference::KindArch::x86, abs32, 2, 0 },
{ false, 0, 0, 0 },
// Stub Helper size and code
10,
@ -184,7 +185,9 @@ const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
0x90 }, // nop
{ Reference::KindArch::x86, abs32, 1, 0 },
{ Reference::KindArch::x86, abs32, 7, 0 }
{ false, 0, 0, 0 },
{ Reference::KindArch::x86, abs32, 7, 0 },
{ false, 0, 0, 0 }
};
bool ArchHandler_x86::isCallSite(const Reference &ref) {

View File

@ -185,6 +185,7 @@ const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = {
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,
@ -200,7 +201,9 @@ const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = {
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip)
0x90 }, // nop
{ Reference::KindArch::x86_64, ripRel32, 3, 0 },
{ Reference::KindArch::x86_64, ripRel32, 11, 0 }
{ false, 0, 0, 0 },
{ Reference::KindArch::x86_64, ripRel32, 11, 0 },
{ false, 0, 0, 0 }
};

View File

@ -1,6 +1,7 @@
add_lld_library(lldMachO
ArchHandler.cpp
ArchHandler_arm.cpp
ArchHandler_arm64.cpp
ArchHandler_x86.cpp
ArchHandler_x86_64.cpp
GOTPass.cpp

View File

@ -79,6 +79,7 @@ MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = {
{ "armv6", arch_armv6, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 },
{ "armv7", arch_armv7, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 },
{ "armv7s", arch_armv7s, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
{ "arm64", arch_arm64, true, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
{ "", arch_unknown,false, 0, 0 }
};
@ -194,6 +195,10 @@ void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os,
default:
break;
}
// Set default segment page sizes based on arch.
if (arch == arch_arm64)
_pageSize = 4*4096;
}
uint32_t MachOLinkingContext::getCPUType() const {
@ -264,10 +269,17 @@ bool MachOLinkingContext::needsStubsPass() const {
}
bool MachOLinkingContext::needsGOTPass() const {
// Only x86_64 uses GOT pass but not in -r mode.
if (_arch != arch_x86_64)
// GOT pass not used in -r mode.
if (_outputMachOType == MH_OBJECT)
return false;
return (_outputMachOType != MH_OBJECT);
// Only some arches use GOT pass.
switch (_arch) {
case arch_x86_64:
case arch_arm64:
return true;
default:
return false;
}
}

View File

@ -236,6 +236,7 @@ struct NormalizedFile {
Hex32 sdkVersion;
// Maps to load commands with LINKEDIT content (final linked images only).
Hex32 pageSize;
std::vector<RebaseLocation> rebasingInfo;
std::vector<BindLocation> bindingInfo;
std::vector<BindLocation> weakBindingInfo;

View File

@ -470,16 +470,21 @@ void MachOFileLayout::buildFileOffsets() {
// Assign sections to segments.
for (const Section &s : _file.sections) {
_sectInfo[&s] = t2;
bool foundSegment = false;
for (const Segment &sg : _file.segments) {
if ((s.address >= sg.address)
if (sg.name.equals(s.segmentName)) {
if ((s.address >= sg.address)
&& (s.address+s.content.size() <= sg.address+sg.size)) {
if (!sg.name.equals(s.segmentName)) {
_ec = make_error_code(llvm::errc::executable_format_error);
return;
_segInfo[&sg].sections.push_back(&s);
foundSegment = true;
break;
}
_segInfo[&sg].sections.push_back(&s);
}
}
if (!foundSegment) {
_ec = make_error_code(llvm::errc::executable_format_error);
return;
}
}
// Assign file offsets.
@ -507,9 +512,10 @@ void MachOFileLayout::buildFileOffsets() {
<< ", fileOffset=" << fileOffset << "\n");
}
// FIXME: 4096 should be inferred from segments in normalized file.
_segInfo[&sg].fileSize = llvm::RoundUpToAlignment(segFileSize, 4096);
fileOffset = llvm::RoundUpToAlignment(fileOffset + segFileSize, 4096);
_segInfo[&sg].fileSize = llvm::RoundUpToAlignment(segFileSize,
_file.pageSize);
fileOffset = llvm::RoundUpToAlignment(fileOffset + segFileSize,
_file.pageSize);
_addressOfLinkEdit = sg.address + sg.size;
}
_startOfLinkEdit = fileOffset;
@ -638,15 +644,16 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
lc = reinterpret_cast<uint8_t*>(next);
}
// Add implicit __LINKEDIT segment
size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit;
typename T::command* cmd = reinterpret_cast<typename T::command*>(lc);
cmd->cmd = T::LC;
cmd->cmdsize = sizeof(typename T::command);
uint8_t *next = lc + cmd->cmdsize;
setString16("__LINKEDIT", cmd->segname);
cmd->vmaddr = _addressOfLinkEdit;
cmd->vmsize = _endOfLinkEdit - _startOfLinkEdit;
cmd->vmsize = llvm::RoundUpToAlignment(linkeditSize, _file.pageSize);
cmd->fileoff = _startOfLinkEdit;
cmd->filesize = _endOfLinkEdit - _startOfLinkEdit;
cmd->filesize = linkeditSize;
cmd->maxprot = VM_PROT_READ;
cmd->initprot = VM_PROT_READ;
cmd->nsects = 0;
@ -823,6 +830,8 @@ void MachOFileLayout::writeSectionContent() {
// Copy all section content to output buffer.
if (s.type == llvm::MachO::S_ZEROFILL)
continue;
if (s.content.empty())
continue;
uint32_t offset = _sectInfo[&s].fileOffset;
uint8_t *p = &_buffer[offset];
memcpy(p, &s.content[0], s.content.size());
@ -1262,7 +1271,6 @@ std::error_code MachOFileLayout::writeBinary(StringRef path) {
}
/// Takes in-memory normalized view and writes a mach-o object file.
std::error_code writeBinary(const NormalizedFile &file, StringRef path) {
MachOFileLayout layout(file);

View File

@ -1149,6 +1149,7 @@ normalizedFromAtoms(const lld::File &atomFile,
normFile.fileType = context.outputMachOType();
normFile.flags = util.fileFlags();
normFile.installName = context.installName();
normFile.pageSize = context.pageSize();
util.addDependentDylibs(atomFile, normFile);
util.copySegmentInfo(normFile);
util.copySectionInfo(normFile);

View File

@ -285,6 +285,12 @@ std::error_code processSymboledSection(DefinedAtom::ContentType atomType,
Atom::Scope rScope = atomScope(rhs->scope);
if (lScope != rScope)
return lScope < rScope;
// If same address and scope, see if one might be better as
// the alias.
bool lPrivate = (lhs->name.front() == 'l');
bool rPrivate = (rhs->name.front() == 'l');
if (lPrivate != rPrivate)
return lPrivate;
// If same address and scope, sort by name.
return lhs->name < rhs->name;
});
@ -315,8 +321,15 @@ std::error_code processSymboledSection(DefinedAtom::ContentType atomType,
const Symbol *lastSym = nullptr;
for (const Symbol *sym : symbols) {
if (lastSym != nullptr) {
atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name,
lastSym->desc, atomScope(lastSym->scope), sym->value, copyRefs);
// Ignore any assembler added "ltmpNNN" symbol at start of section
// if there is another symbol at the start.
if ((lastSym->value != sym->value)
|| lastSym->value != section.address
|| !lastSym->name.startswith("ltmp")) {
atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name,
lastSym->desc, atomScope(lastSym->scope), sym->value,
copyRefs);
}
}
lastSym = sym;
}

View File

@ -129,6 +129,7 @@ struct ScalarEnumerationTraits<lld::MachOLinkingContext::Arch> {
io.enumCase(value, "armv6", lld::MachOLinkingContext::arch_armv6);
io.enumCase(value, "armv7", lld::MachOLinkingContext::arch_armv7);
io.enumCase(value, "armv7s", lld::MachOLinkingContext::arch_armv7s);
io.enumCase(value, "arm64", lld::MachOLinkingContext::arch_arm64);
}
};
@ -400,6 +401,30 @@ struct ScalarEnumerationTraits<RelocationInfoType> {
io.enumCase(value, "ARM_RELOC_HALF_SECTDIFF",
llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
break;
case lld::MachOLinkingContext::arch_arm64:
io.enumCase(value, "ARM64_RELOC_UNSIGNED",
llvm::MachO::ARM64_RELOC_UNSIGNED);
io.enumCase(value, "ARM64_RELOC_SUBTRACTOR",
llvm::MachO::ARM64_RELOC_SUBTRACTOR);
io.enumCase(value, "ARM64_RELOC_BRANCH26",
llvm::MachO::ARM64_RELOC_BRANCH26);
io.enumCase(value, "ARM64_RELOC_PAGE21",
llvm::MachO::ARM64_RELOC_PAGE21);
io.enumCase(value, "ARM64_RELOC_PAGEOFF12",
llvm::MachO::ARM64_RELOC_PAGEOFF12);
io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGE21",
llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
io.enumCase(value, "ARM64_RELOC_POINTER_TO_GOT",
llvm::MachO::ARM64_RELOC_POINTER_TO_GOT);
io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGE21",
llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
io.enumCase(value, "ARM64_RELOC_ADDEND",
llvm::MachO::ARM64_RELOC_ADDEND);
break;
default:
llvm_unreachable("unknown architecture");
}
@ -646,6 +671,7 @@ struct MappingTraits<NormalizedFile> {
io.mapOptional("local-symbols", file.localSymbols);
io.mapOptional("global-symbols", file.globalSymbols);
io.mapOptional("undefined-symbols",file.undefinedSymbols);
io.mapOptional("page-size", file.pageSize, Hex32(4096));
io.mapOptional("rebasings", file.rebasingInfo);
io.mapOptional("bindings", file.bindingInfo);
io.mapOptional("weak-bindings", file.weakBindingInfo);

View File

@ -321,6 +321,8 @@ private:
StubHelperAtom(_file, _stubInfo);
addReference(stub, _stubInfo.stubReferenceToLP, lp);
addOptReference(stub, _stubInfo.stubReferenceToLP,
_stubInfo.optStubReferenceToLP, lp);
addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper);
addReference(lp, _stubInfo.lazyPointerReferenceToFinal, &target);
addReference(helper, _stubInfo.stubHelperReferenceToImm, helper);
@ -334,14 +336,25 @@ private:
return stub;
}
void addReference(SimpleDefinedAtom* atom,
void addReference(SimpleDefinedAtom* atom,
const ArchHandler::ReferenceInfo &refInfo,
const lld::Atom* target) {
atom->addReference(Reference::KindNamespace::mach_o,
refInfo.arch, refInfo.kind, refInfo.offset,
target, refInfo.addend);
}
void addOptReference(SimpleDefinedAtom* atom,
const ArchHandler::ReferenceInfo &refInfo,
const ArchHandler::OptionalRefInfo &optRef,
const lld::Atom* target) {
if (!optRef.used)
return;
atom->addReference(Reference::KindNamespace::mach_o,
refInfo.arch, optRef.kind, optRef.offset,
target, optRef.addend);
}
const DefinedAtom* helperCommon() {
if ( !_helperCommonAtom ) {
// Lazily create common helper code and data.
@ -352,11 +365,19 @@ private:
_helperBinderNLPAtom = new (_file.allocator())
NonLazyPointerAtom(_file, _context.is64Bit());
addReference(_helperCommonAtom,
_stubInfo.stubHelperCommonReferenceToCache,
_stubInfo.stubHelperCommonReferenceToCache,
_helperCacheNLPAtom);
addReference(_helperCommonAtom,
_stubInfo.stubHelperCommonReferenceToBinder,
addOptReference(_helperCommonAtom,
_stubInfo.stubHelperCommonReferenceToCache,
_stubInfo.optStubHelperCommonReferenceToCache,
_helperCacheNLPAtom);
addReference(_helperCommonAtom,
_stubInfo.stubHelperCommonReferenceToBinder,
_helperBinderNLPAtom);
addOptReference(_helperCommonAtom,
_stubInfo.stubHelperCommonReferenceToBinder,
_stubInfo.optStubHelperCommonReferenceToBinder,
_helperBinderNLPAtom);
}
return _helperCommonAtom;
}

View File

@ -0,0 +1,104 @@
# RUN: lld -flavor darwin -arch arm64 -ios_version_min 7.0 %s -o %t && \
# RUN: llvm-nm -m -n %t | FileCheck %s
#
# Test that arm64 hello-world can be linked into a mach-o executable
#
--- !mach-o
arch: arm64
file-type: MH_OBJECT
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
alignment: 2
address: 0x0000000000000000
content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
0x08, 0x00, 0x00, 0x90, 0x08, 0x01, 0x40, 0xF9,
0x00, 0x01, 0x40, 0xF9, 0x01, 0x00, 0x00, 0x90,
0x21, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x94,
0x00, 0x00, 0x80, 0x52, 0xFD, 0x7B, 0xC1, 0xA8,
0xC0, 0x03, 0x5F, 0xD6 ]
relocations:
- offset: 0x0000001C
type: ARM64_RELOC_BRANCH26
length: 2
pc-rel: true
extern: true
symbol: 5
- offset: 0x00000018
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 1
- offset: 0x00000014
type: ARM64_RELOC_PAGE21
length: 2
pc-rel: true
extern: true
symbol: 1
- offset: 0x0000000C
type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 4
- offset: 0x00000008
type: ARM64_RELOC_GOT_LOAD_PAGE21
length: 2
pc-rel: true
extern: true
symbol: 4
- segment: __TEXT
section: __cstring
type: S_CSTRING_LITERALS
attributes: [ ]
address: 0x000000000000002C
content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
local-symbols:
- name: ltmp0
type: N_SECT
sect: 1
value: 0x0000000000000000
- name: l_.str
type: N_SECT
sect: 2
value: 0x000000000000002C
- name: ltmp1
type: N_SECT
sect: 2
value: 0x000000000000002C
global-symbols:
- name: _main
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000000
undefined-symbols:
- name: ___stdoutp
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
- name: _fprintf
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
...
--- !mach-o
arch: arm64
file-type: MH_DYLIB
install-name: /usr/lib/libSystem.B.dylib
exports:
- name: _fprintf
- name: ___stdoutp
- name: dyld_stub_binder
...
# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
# CHECK: (undefined) external ___stdoutp (from libSystem)
# CHECK: (undefined) external _fprintf (from libSystem)
# CHECK: (undefined) external dyld_stub_binder (from libSystem)

View File

@ -0,0 +1,191 @@
# RUN: lld -flavor darwin -arch arm64 -r -print_atoms %s -o %t | FileCheck %s \
# RUN: && lld -flavor darwin -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s
#
# Test parsing and writing of arm64 data relocations.
#
# The first step tests if the supplied mach-o file is parsed into the correct
# set of references. The second step verifies relocations can be round-tripped
# by writing to a new .o file, then parsing that file which should result in
# the same references.
#
#_test:
--- !mach-o
arch: arm64
file-type: MH_OBJECT
flags: [ ]
has-UUID: false
OS: unknown
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
address: 0x0000000000000000
- segment: __DATA
section: __data
type: S_REGULAR
attributes: [ ]
alignment: 3
address: 0x0000000000000000
content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xD0, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0xFF, 0xFF,
0xC8, 0xFF, 0xFF, 0xFF ]
relocations:
- offset: 0x00000038
type: ARM64_RELOC_POINTER_TO_GOT
length: 2
pc-rel: true
extern: true
symbol: 3
- offset: 0x00000034
type: ARM64_RELOC_SUBTRACTOR
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000034
type: ARM64_RELOC_UNSIGNED
length: 2
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000030
type: ARM64_RELOC_SUBTRACTOR
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000030
type: ARM64_RELOC_UNSIGNED
length: 2
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000028
type: ARM64_RELOC_SUBTRACTOR
length: 3
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000028
type: ARM64_RELOC_UNSIGNED
length: 3
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000020
type: ARM64_RELOC_SUBTRACTOR
length: 3
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000020
type: ARM64_RELOC_UNSIGNED
length: 3
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000018
type: ARM64_RELOC_SUBTRACTOR
length: 3
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000018
type: ARM64_RELOC_UNSIGNED
length: 3
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000010
type: ARM64_RELOC_POINTER_TO_GOT
length: 3
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000008
type: ARM64_RELOC_UNSIGNED
length: 3
pc-rel: false
extern: true
symbol: 3
- offset: 0x00000000
type: ARM64_RELOC_UNSIGNED
length: 3
pc-rel: false
extern: true
symbol: 3
local-symbols:
- name: ltmp0
type: N_SECT
sect: 1
value: 0x0000000000000000
- name: ltmp1
type: N_SECT
sect: 2
value: 0x0000000000000000
- name: _v1
type: N_SECT
sect: 2
value: 0x0000000000000000
undefined-symbols:
- name: _foo
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
...
# CHECK: defined-atoms:
# CHECK: - name: _v1
# CHECK: type: data
# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 08, 00, 00, 00,
# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, E0, FF, FF, FF,
# CHECK: FF, FF, FF, FF, DC, FF, FF, FF, FF, FF, FF, FF,
# CHECK: D0, FF, FF, FF, CE, FF, FF, FF, C8, FF, FF, FF ]
# CHECK: references:
# CHECK: - kind: pointer64
# CHECK: offset: 0
# CHECK: target: _foo
# CHECK-NOT: addend:
# CHECK: - kind: pointer64
# CHECK: offset: 8
# CHECK: target: _foo
# CHECK: addend: 8
# CHECK: - kind: pointer64ToGOT
# CHECK: offset: 16
# CHECK: target: _foo
# CHECK-NOT: addend:
# CHECK: - kind: delta64
# CHECK: offset: 24
# CHECK: target: _foo
# CHECK: addend: 24
# CHECK: - kind: delta64
# CHECK: offset: 32
# CHECK: target: _foo
# CHECK-NOT: addend:
# CHECK: - kind: delta64
# CHECK: offset: 40
# CHECK: target: _foo
# CHECK: addend: 4
# CHECK: - kind: delta32
# CHECK: offset: 48
# CHECK: target: _foo
# CHECK-NOT: addend:
# CHECK: - kind: delta32
# CHECK: offset: 52
# CHECK: target: _foo
# CHECK: addend: 2
# CHECK: - kind: delta32ToGOT
# CHECK: offset: 56
# CHECK: target: _foo
# CHECK-NOT: addend:
# CHECK: undefined-atoms:
# CHECK: - name: _foo

View File

@ -0,0 +1,237 @@
# RUN: lld -flavor darwin -arch arm64 -r -print_atoms %s -o %t | FileCheck %s \
# RUN: && lld -flavor darwin -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s
#
# Test parsing and writing of arm64 text relocations.
#
# The first step tests if the supplied mach-o file is parsed into the correct
# set of references. The second step verifies relocations can be round-tripped
# by writing to a new .o file, then parsing that file which should result in
# the same references.
#
#_test:
--- !mach-o
arch: arm64
file-type: MH_OBJECT
flags: [ ]
has-UUID: false
OS: unknown
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
address: 0x0000000000000000
content: [ 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x94,
0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0x39,
0x20, 0x00, 0x40, 0x79, 0x20, 0x00, 0x40, 0xB9,
0x20, 0x00, 0x40, 0xF9, 0x20, 0x00, 0xC0, 0x3D,
0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0xB9,
0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0xF9,
0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x40, 0xF9 ]
relocations:
- offset: 0x00000034
type: ARM64_RELOC_TLVP_LOAD_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 5
- offset: 0x00000030
type: ARM64_RELOC_TLVP_LOAD_PAGE21
length: 2
pc-rel: true
extern: true
symbol: 5
- offset: 0x0000002C
type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 6
- offset: 0x00000028
type: ARM64_RELOC_GOT_LOAD_PAGE21
length: 2
pc-rel: true
extern: true
symbol: 6
- offset: 0x00000024
type: ARM64_RELOC_ADDEND
length: 2
pc-rel: false
extern: false
symbol: 16
- offset: 0x00000024
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000020
type: ARM64_RELOC_ADDEND
length: 2
pc-rel: false
extern: false
symbol: 16
- offset: 0x00000020
type: ARM64_RELOC_PAGE21
length: 2
pc-rel: true
extern: true
symbol: 2
- offset: 0x0000001C
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000018
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000014
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000010
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x0000000C
type: ARM64_RELOC_PAGEOFF12
length: 2
pc-rel: false
extern: true
symbol: 2
- offset: 0x00000008
type: ARM64_RELOC_PAGE21
length: 2
pc-rel: true
extern: true
symbol: 2
- offset: 0x00000004
type: ARM64_RELOC_ADDEND
length: 2
pc-rel: false
extern: false
symbol: 8
- offset: 0x00000004
type: ARM64_RELOC_BRANCH26
length: 2
pc-rel: true
extern: true
symbol: 4
- offset: 0x00000000
type: ARM64_RELOC_BRANCH26
length: 2
pc-rel: true
extern: true
symbol: 4
- segment: __DATA
section: __data
type: S_REGULAR
attributes: [ ]
alignment: 2
address: 0x0000000000000038
content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
local-symbols:
- name: ltmp0
type: N_SECT
sect: 1
value: 0x0000000000000000
- name: _func
type: N_SECT
sect: 1
value: 0x0000000000000000
- name: _v1
type: N_SECT
sect: 2
value: 0x0000000000000038
- name: ltmp1
type: N_SECT
sect: 2
value: 0x0000000000000038
undefined-symbols:
- name: _foo
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
- name: _tlv
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
- name: _v2
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
...
# CHECK: defined-atoms:
# CHECK: - name: _v1
# CHECK: type: data
# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
# CHECK: 00, 00, 00, 00 ]
# CHECK: - name: _func
# CHECK: content: [ 00, 00, 00, 94, 00, 00, 00, 94, 01, 00, 00, 90,
# CHECK: 20, 00, 40, 39, 20, 00, 40, 79, 20, 00, 40, B9,
# CHECK: 20, 00, 40, F9, 20, 00, C0, 3D, 01, 00, 00, 90,
# CHECK: 20, 00, 40, B9, 01, 00, 00, 90, 20, 00, 40, F9,
# CHECK: 00, 00, 00, 90, 00, 00, 40, F9 ]
# CHECK: references:
# CHECK: - kind: branch26
# CHECK: offset: 0
# CHECK: target: _foo
# CHECK: - kind: branch26
# CHECK: offset: 4
# CHECK: target: _foo
# CHECK: addend: 8
# CHECK: - kind: page21
# CHECK: offset: 8
# CHECK: target: _v1
# CHECK: - kind: offset12
# CHECK: offset: 12
# CHECK: target: _v1
# CHECK: - kind: offset12scale2
# CHECK: offset: 16
# CHECK: target: _v1
# CHECK: - kind: offset12scale4
# CHECK: offset: 20
# CHECK: target: _v1
# CHECK: - kind: offset12scale8
# CHECK: offset: 24
# CHECK: target: _v1
# CHECK: - kind: offset12scale16
# CHECK: offset: 28
# CHECK: target: _v1
# CHECK: - kind: page21
# CHECK: offset: 32
# CHECK: target: _v1
# CHECK: addend: 16
# CHECK: - kind: offset12scale4
# CHECK: offset: 36
# CHECK: target: _v1
# CHECK: addend: 16
# CHECK: - kind: gotPage21
# CHECK: offset: 40
# CHECK: target: _v2
# CHECK: - kind: gotOffset12
# CHECK: offset: 44
# CHECK: target: _v2
# CHECK: - kind: tlvPage21
# CHECK: offset: 48
# CHECK: target: _tlv
# CHECK: - kind: tlvOffset12
# CHECK: offset: 52
# CHECK: target: _tlv
# CHECK: undefined-atoms:
# CHECK: - name: _foo
# CHECK: - name: _tlv
# CHECK: - name: _v2