forked from OSchip/llvm-project
[mach-o] refactor KindHandler into ArchHandler and simplify passes.
All architecture specific handling is now done in the appropriate ArchHandler subclass. The StubsPass and GOTPass have been simplified. All architecture specific variations in stubs are now encoded in a table which is vended by the current ArchHandler. llvm-svn: 213187
This commit is contained in:
parent
ac451066f4
commit
2458bec7e7
|
@ -24,7 +24,7 @@ using llvm::MachO::HeaderFileType;
|
|||
namespace lld {
|
||||
|
||||
namespace mach_o {
|
||||
class KindHandler; // defined in lib. this header is in include.
|
||||
class ArchHandler;
|
||||
}
|
||||
|
||||
class MachOLinkingContext : public LinkingContext {
|
||||
|
@ -68,7 +68,7 @@ public:
|
|||
virtual uint64_t pageZeroSize() const { return _pageZeroSize; }
|
||||
virtual uint64_t pageSize() const { return _pageSize; }
|
||||
|
||||
mach_o::KindHandler &kindHandler() const;
|
||||
mach_o::ArchHandler &archHandler() const;
|
||||
|
||||
HeaderFileType outputMachOType() const { return _outputMachOType; }
|
||||
|
||||
|
@ -168,6 +168,15 @@ public:
|
|||
|
||||
StringRef dyldPath() const { return "/usr/lib/dyld"; }
|
||||
|
||||
/// Stub creation Pass should be run.
|
||||
bool needsStubsPass() const;
|
||||
|
||||
// GOT createion Pass should be run.
|
||||
bool needsGOTPass() const;
|
||||
|
||||
/// Magic symbol name stubs will need to help lazy bind.
|
||||
StringRef binderSymbolName() const;
|
||||
|
||||
static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
|
||||
static Arch archFromName(StringRef archName);
|
||||
static StringRef nameFromArch(Arch arch);
|
||||
|
@ -211,7 +220,7 @@ private:
|
|||
bool _printAtoms;
|
||||
bool _testingLibResolution;
|
||||
StringRef _bundleLoader;
|
||||
mutable std::unique_ptr<mach_o::KindHandler> _kindHandler;
|
||||
mutable std::unique_ptr<mach_o::ArchHandler> _archHandler;
|
||||
mutable std::unique_ptr<Writer> _writer;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ class File;
|
|||
class LinkingContext;
|
||||
class PECOFFLinkingContext;
|
||||
class TargetHandlerBase;
|
||||
class MachOLinkingContext;
|
||||
|
||||
/// \brief An abstract class for reading object files, library files, and
|
||||
/// executable files.
|
||||
|
@ -122,7 +123,7 @@ public:
|
|||
void addSupportNativeObjects();
|
||||
void addSupportCOFFObjects(PECOFFLinkingContext &);
|
||||
void addSupportCOFFImportLibraries();
|
||||
void addSupportMachOObjects(StringRef archName);
|
||||
void addSupportMachOObjects(const MachOLinkingContext &);
|
||||
void addSupportELFObjects(bool atomizeStrings, TargetHandlerBase *handler);
|
||||
void addSupportELFDynamicSharedObjects(bool useShlibUndefines,
|
||||
TargetHandlerBase *handler);
|
||||
|
|
|
@ -79,7 +79,7 @@ bool DarwinLdDriver::linkMachO(int argc, const char *argv[],
|
|||
return true;
|
||||
|
||||
// Register possible input file parsers.
|
||||
ctx.registry().addSupportMachOObjects(ctx.archName());
|
||||
ctx.registry().addSupportMachOObjects(ctx);
|
||||
ctx.registry().addSupportArchives(ctx.logInputFiles());
|
||||
ctx.registry().addSupportNativeObjects();
|
||||
ctx.registry().addSupportYamlFiles();
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler.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"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
|
||||
ArchHandler::ArchHandler() {
|
||||
}
|
||||
|
||||
ArchHandler::~ArchHandler() {
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create(
|
||||
MachOLinkingContext::Arch arch) {
|
||||
switch (arch) {
|
||||
case MachOLinkingContext::arch_x86_64:
|
||||
return create_x86_64();
|
||||
case MachOLinkingContext::arch_x86:
|
||||
return create_x86();
|
||||
case MachOLinkingContext::arch_armv6:
|
||||
case MachOLinkingContext::arch_armv7:
|
||||
case MachOLinkingContext::arch_armv7s:
|
||||
return create_arm();
|
||||
default:
|
||||
llvm_unreachable("Unknown arch");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ArchHandler::isLazyPointer(const Reference &ref) {
|
||||
// A lazy bind entry is needed for a lazy pointer.
|
||||
const StubInfo &info = stubInfo();
|
||||
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
|
||||
return false;
|
||||
if (ref.kindArch() != info.lazyPointerReferenceToFinal.arch)
|
||||
return false;
|
||||
return (ref.kindValue() == info.lazyPointerReferenceToFinal.kind);
|
||||
}
|
||||
|
||||
|
||||
ArchHandler::RelocPattern ArchHandler::relocPattern(const Relocation &reloc) {
|
||||
assert((reloc.type & 0xFFF0) == 0);
|
||||
uint16_t result = reloc.type;
|
||||
if (reloc.scattered)
|
||||
result |= rScattered;
|
||||
if (reloc.pcRel)
|
||||
result |= rPcRel;
|
||||
if (reloc.isExtern)
|
||||
result |= rExtern;
|
||||
switch(reloc.length) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
result |= rLength2;
|
||||
break;
|
||||
case 2:
|
||||
result |= rLength4;
|
||||
break;
|
||||
case 3:
|
||||
result |= rLength8;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("bad r_length");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int16_t ArchHandler::readS16(bool swap, const uint8_t *addr) {
|
||||
return read16(swap, *reinterpret_cast<const uint16_t*>(addr));
|
||||
}
|
||||
|
||||
int32_t ArchHandler::readS32(bool swap, const uint8_t *addr) {
|
||||
return read32(swap, *reinterpret_cast<const uint32_t*>(addr));
|
||||
}
|
||||
|
||||
uint32_t ArchHandler::readU32(bool swap, const uint8_t *addr) {
|
||||
return read32(swap, *reinterpret_cast<const uint32_t*>(addr));
|
||||
}
|
||||
|
||||
int64_t ArchHandler::readS64(bool swap, const uint8_t *addr) {
|
||||
return read64(swap, *reinterpret_cast<const uint64_t*>(addr));
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
#include "llvm/ADT/Triple.h"
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
|
||||
#define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
///
|
||||
/// The ArchHandler class handles all architecture specific aspects of
|
||||
/// mach-o linking.
|
||||
///
|
||||
class ArchHandler {
|
||||
public:
|
||||
virtual ~ArchHandler();
|
||||
|
||||
/// There is no public interface to subclasses of ArchHandler, so this
|
||||
/// is the only way to instantiate an ArchHandler.
|
||||
static std::unique_ptr<ArchHandler> create(MachOLinkingContext::Arch arch);
|
||||
|
||||
/// Get (arch specific) kind strings used by Registry.
|
||||
virtual const Registry::KindStrings *kindStrings() = 0;
|
||||
|
||||
/// Convert mach-o Arch to Reference::KindArch.
|
||||
virtual Reference::KindArch kindArch() = 0;
|
||||
|
||||
/// Used by StubPass to update References to shared library functions
|
||||
/// to be references to a stub.
|
||||
virtual bool isCallSite(const Reference &) = 0;
|
||||
|
||||
/// Used by GOTPass to locate GOT References
|
||||
virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Used by GOTPass to update GOT References
|
||||
virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {}
|
||||
|
||||
/// Used by normalizedFromAtoms() to know where to generated rebasing and
|
||||
/// binding info in final executables.
|
||||
virtual bool isPointer(const Reference &) = 0;
|
||||
|
||||
/// Used by normalizedFromAtoms() to know where to generated lazy binding
|
||||
/// info in final executables.
|
||||
virtual bool isLazyPointer(const Reference &);
|
||||
|
||||
/// Returns true if the specified relocation is paired to the next relocation.
|
||||
virtual bool isPairedReloc(const normalized::Relocation &) = 0;
|
||||
|
||||
/// Prototype for a helper function. Given a sectionIndex and address,
|
||||
/// finds the atom and offset with that atom of that address.
|
||||
typedef std::function<std::error_code (uint32_t sectionIndex, uint64_t addr,
|
||||
const lld::Atom **, Reference::Addend *)>
|
||||
FindAtomBySectionAndAddress;
|
||||
|
||||
/// Prototype for a helper function. Given a symbolIndex, finds the atom
|
||||
/// representing that symbol.
|
||||
typedef std::function<std::error_code (uint32_t symbolIndex,
|
||||
const lld::Atom **)> FindAtomBySymbolIndex;
|
||||
|
||||
/// Analyzes a relocation from a .o file and returns the info
|
||||
/// (kind, target, addend) needed to instantiate a Reference.
|
||||
/// Two helper functions are passed as parameters to find the target atom
|
||||
/// given a symbol index or address.
|
||||
virtual 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) = 0;
|
||||
|
||||
/// Analyzes a pair of relocations from a .o file and returns the info
|
||||
/// (kind, target, addend) needed to instantiate a Reference.
|
||||
/// Two helper functions are passed as parameters to find the target atom
|
||||
/// given a symbol index or address.
|
||||
virtual 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) = 0;
|
||||
|
||||
/// Fixup an atom when generating a final linked binary.
|
||||
virtual void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress) = 0;
|
||||
|
||||
struct ReferenceInfo {
|
||||
Reference::KindArch arch;
|
||||
uint16_t kind;
|
||||
uint32_t offset;
|
||||
int32_t addend;
|
||||
};
|
||||
|
||||
/// Table of architecture specific information for creating stubs.
|
||||
struct StubInfo {
|
||||
const char* binderSymbolName;
|
||||
ReferenceInfo lazyPointerReferenceToHelper;
|
||||
ReferenceInfo lazyPointerReferenceToFinal;
|
||||
ReferenceInfo nonLazyPointerReferenceToBinder;
|
||||
uint8_t codeAlignment;
|
||||
|
||||
uint32_t stubSize;
|
||||
uint8_t stubBytes[16];
|
||||
ReferenceInfo stubReferenceToLP;
|
||||
|
||||
uint32_t stubHelperSize;
|
||||
uint8_t stubHelperBytes[16];
|
||||
ReferenceInfo stubHelperReferenceToImm;
|
||||
ReferenceInfo stubHelperReferenceToHelperCommon;
|
||||
|
||||
uint32_t stubHelperCommonSize;
|
||||
uint8_t stubHelperCommonBytes[36];
|
||||
ReferenceInfo stubHelperCommonReferenceToCache;
|
||||
ReferenceInfo stubHelperCommonReferenceToBinder;
|
||||
};
|
||||
|
||||
virtual const StubInfo &stubInfo() = 0;
|
||||
|
||||
protected:
|
||||
ArchHandler();
|
||||
|
||||
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();
|
||||
|
||||
// Handy way to pack mach-o r_type and other bit fields into one 16-bit value.
|
||||
typedef uint16_t RelocPattern;
|
||||
enum {
|
||||
rScattered = 0x8000,
|
||||
rPcRel = 0x4000,
|
||||
rExtern = 0x2000,
|
||||
rLength1 = 0x0000,
|
||||
rLength2 = 0x0100,
|
||||
rLength4 = 0x0200,
|
||||
rLength8 = 0x0300
|
||||
};
|
||||
static RelocPattern relocPattern(const normalized::Relocation &reloc);
|
||||
|
||||
static int16_t readS16(bool swap, const uint8_t *addr);
|
||||
static int32_t readS32(bool swap, const uint8_t *addr);
|
||||
static uint32_t readU32(bool swap, const uint8_t *addr);
|
||||
static int64_t readS64(bool swap, const uint8_t *addr);
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
|
|
@ -0,0 +1,662 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler_arm.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"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class ArchHandler_arm : public ArchHandler {
|
||||
public:
|
||||
ArchHandler_arm();
|
||||
virtual ~ArchHandler_arm();
|
||||
|
||||
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
|
||||
|
||||
Reference::KindArch kindArch() override { return Reference::KindArch::ARM; }
|
||||
|
||||
const ArchHandler::StubInfo &stubInfo() override;
|
||||
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;
|
||||
|
||||
void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress)
|
||||
override;
|
||||
|
||||
private:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfoArmPIC;
|
||||
|
||||
enum : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
thumb_b22, /// ex: bl _foo
|
||||
thumb_movw, /// ex: movw r1, :lower16:_foo
|
||||
thumb_movt, /// ex: movt r1, :lower16:_foo
|
||||
thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
|
||||
thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
|
||||
arm_b24, /// ex: bl _foo
|
||||
arm_movw, /// ex: movw r1, :lower16:_foo
|
||||
arm_movt, /// ex: movt r1, :lower16:_foo
|
||||
arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
|
||||
arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
|
||||
pointer32, /// ex: .long _foo
|
||||
delta32, /// ex: .long _foo - .
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
};
|
||||
|
||||
int32_t getDisplacementFromThumbBranch(uint32_t instruction);
|
||||
int32_t getDisplacementFromArmBranch(uint32_t instruction);
|
||||
uint16_t getWordFromThumbMov(uint32_t instruction);
|
||||
uint16_t getWordFromArmMov(uint32_t instruction);
|
||||
uint32_t clearThumbBit(uint32_t value, const Atom *target);
|
||||
uint32_t setDisplacementInArmBranch(uint32_t instruction, int32_t disp);
|
||||
|
||||
|
||||
const bool _swap;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ArchHandler_arm
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
ArchHandler_arm::ArchHandler_arm() :
|
||||
_swap(!MachOLinkingContext::isHostEndian(MachOLinkingContext::arch_armv7)) {}
|
||||
|
||||
ArchHandler_arm::~ArchHandler_arm() { }
|
||||
|
||||
const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(thumb_b22),
|
||||
LLD_KIND_STRING_ENTRY(thumb_movw),
|
||||
LLD_KIND_STRING_ENTRY(thumb_movt),
|
||||
LLD_KIND_STRING_ENTRY(thumb_movw_funcRel),
|
||||
LLD_KIND_STRING_ENTRY(thumb_movt_funcRel),
|
||||
LLD_KIND_STRING_ENTRY(arm_b24),
|
||||
LLD_KIND_STRING_ENTRY(arm_movw),
|
||||
LLD_KIND_STRING_ENTRY(arm_movt),
|
||||
LLD_KIND_STRING_ENTRY(arm_movw_funcRel),
|
||||
LLD_KIND_STRING_ENTRY(arm_movt_funcRel),
|
||||
LLD_KIND_STRING_ENTRY(pointer32),
|
||||
LLD_KIND_STRING_ENTRY(delta32),
|
||||
LLD_KIND_STRING_ENTRY(lazyPointer),
|
||||
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
||||
LLD_KIND_STRING_END
|
||||
};
|
||||
|
||||
const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = {
|
||||
"dyld_stub_binder",
|
||||
|
||||
// References in lazy pointer
|
||||
{ Reference::KindArch::ARM, pointer32, 0, 0 },
|
||||
{ Reference::KindArch::ARM, lazyPointer, 0, 0 },
|
||||
|
||||
// GOT pointer to dyld_stub_binder
|
||||
{ Reference::KindArch::ARM, pointer32, 0, 0 },
|
||||
|
||||
// arm code alignment 2^2
|
||||
2,
|
||||
|
||||
// Stub size and code
|
||||
16,
|
||||
{ 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12
|
||||
0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
|
||||
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 },
|
||||
|
||||
// Stub Helper size and code
|
||||
12,
|
||||
{ 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0]
|
||||
0x00, 0x00, 0x00, 0xEA, // b _helperhelper
|
||||
0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset
|
||||
{ Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 },
|
||||
{ Reference::KindArch::ARM, arm_b24, 4, 0 },
|
||||
|
||||
// Stub Helper-Common size and code
|
||||
36,
|
||||
{ // push lazy-info-offset
|
||||
0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]!
|
||||
// push address of dyld_mageLoaderCache
|
||||
0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1
|
||||
0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
|
||||
0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]!
|
||||
// jump through dyld_stub_binder
|
||||
0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2
|
||||
0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
|
||||
0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip]
|
||||
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, 0 },
|
||||
{ Reference::KindArch::ARM, delta32, 32, 0 }
|
||||
};
|
||||
|
||||
const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() {
|
||||
// If multiple kinds of stubs are supported, select which StubInfo here.
|
||||
return _sStubInfoArmPIC;
|
||||
}
|
||||
|
||||
bool ArchHandler_arm::isCallSite(const Reference &ref) {
|
||||
return (ref.kindValue() == thumb_b22) || (ref.kindValue() == arm_b24);
|
||||
}
|
||||
|
||||
bool ArchHandler_arm::isPointer(const Reference &ref) {
|
||||
return (ref.kindValue() == pointer32);
|
||||
}
|
||||
|
||||
bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) {
|
||||
switch (reloc.type) {
|
||||
case ARM_RELOC_SECTDIFF:
|
||||
case ARM_RELOC_LOCAL_SECTDIFF:
|
||||
case ARM_RELOC_HALF_SECTDIFF:
|
||||
case ARM_RELOC_HALF:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction) {
|
||||
uint32_t s = (instruction >> 10) & 0x1;
|
||||
uint32_t j1 = (instruction >> 29) & 0x1;
|
||||
uint32_t j2 = (instruction >> 27) & 0x1;
|
||||
uint32_t imm10 = instruction & 0x3FF;
|
||||
uint32_t imm11 = (instruction >> 16) & 0x7FF;
|
||||
uint32_t i1 = (j1 == s);
|
||||
uint32_t i2 = (j2 == s);
|
||||
uint32_t dis =
|
||||
(s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
|
||||
int32_t sdis = dis;
|
||||
if (s)
|
||||
return (sdis | 0xFE000000);
|
||||
else
|
||||
return sdis;
|
||||
}
|
||||
|
||||
int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
|
||||
// Sign-extend imm24
|
||||
int32_t displacement = (instruction & 0x00FFFFFF) << 2;
|
||||
if ((displacement & 0x02000000) != 0)
|
||||
displacement |= 0xFC000000;
|
||||
// If this is BLX and H bit set, add 2.
|
||||
if ((instruction & 0xFF000000) == 0xFB000000)
|
||||
displacement += 2;
|
||||
return displacement;
|
||||
}
|
||||
|
||||
uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction,
|
||||
int32_t displacement) {
|
||||
// FIXME: handle BLX and out-of-range.
|
||||
uint32_t newInstruction = (instruction & 0xFF000000);
|
||||
newInstruction |= ((displacement >> 2) & 0x00FFFFFF);
|
||||
return newInstruction;
|
||||
}
|
||||
|
||||
uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) {
|
||||
uint32_t i = ((instruction & 0x00000400) >> 10);
|
||||
uint32_t imm4 = (instruction & 0x0000000F);
|
||||
uint32_t imm3 = ((instruction & 0x70000000) >> 28);
|
||||
uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
|
||||
return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
|
||||
}
|
||||
|
||||
uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) {
|
||||
uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
|
||||
uint32_t imm12 = (instruction & 0x00000FFF);
|
||||
return (imm4 << 12) | imm12;
|
||||
}
|
||||
|
||||
uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) {
|
||||
// The assembler often adds one to the address of a thumb function.
|
||||
// We need to undo that so it does not look like an addend.
|
||||
if (value & 1) {
|
||||
if (isa<DefinedAtom>(target)) {
|
||||
const MachODefinedAtom *machoTarget =
|
||||
reinterpret_cast<const MachODefinedAtom *>(target);
|
||||
if (machoTarget->isThumb())
|
||||
value &= -2; // mask off thumb-bit
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::error_code ArchHandler_arm::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];
|
||||
uint64_t targetAddress;
|
||||
uint32_t instruction = readU32(swap, fixupContent);
|
||||
int32_t displacement;
|
||||
switch (relocPattern(reloc)) {
|
||||
case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4:
|
||||
// ex: bl _foo (and _foo is undefined)
|
||||
*kind = thumb_b22;
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
// Instruction contains branch to addend.
|
||||
displacement = getDisplacementFromThumbBranch(instruction);
|
||||
*addend = fixupAddress + 4 + displacement;
|
||||
return std::error_code();
|
||||
case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
|
||||
// ex: bl _foo (and _foo is defined)
|
||||
*kind = thumb_b22;
|
||||
displacement = getDisplacementFromThumbBranch(instruction);
|
||||
targetAddress = fixupAddress + 4 + displacement;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4:
|
||||
// ex: bl _foo+4 (and _foo is defined)
|
||||
*kind = thumb_b22;
|
||||
displacement = getDisplacementFromThumbBranch(instruction);
|
||||
targetAddress = fixupAddress + 4 + displacement;
|
||||
if (E ec = atomFromAddress(0, reloc.value, target, addend))
|
||||
return ec;
|
||||
// reloc.value is target atom's address. Instruction contains branch
|
||||
// to atom+addend.
|
||||
*addend += (targetAddress - reloc.value);
|
||||
return std::error_code();
|
||||
case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4:
|
||||
// ex: bl _foo (and _foo is undefined)
|
||||
*kind = arm_b24;
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
// Instruction contains branch to addend.
|
||||
displacement = getDisplacementFromArmBranch(instruction);
|
||||
*addend = fixupAddress + 8 + displacement;
|
||||
return std::error_code();
|
||||
case ARM_RELOC_BR24 | rPcRel | rLength4:
|
||||
// ex: bl _foo (and _foo is defined)
|
||||
*kind = arm_b24;
|
||||
displacement = getDisplacementFromArmBranch(instruction);
|
||||
targetAddress = fixupAddress + 8 + displacement;
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4:
|
||||
// ex: bl _foo+4 (and _foo is defined)
|
||||
*kind = arm_b24;
|
||||
displacement = getDisplacementFromArmBranch(instruction);
|
||||
targetAddress = fixupAddress + 8 + displacement;
|
||||
if (E ec = atomFromAddress(0, reloc.value, target, addend))
|
||||
return ec;
|
||||
// reloc.value is target atom's address. Instruction contains branch
|
||||
// to atom+addend.
|
||||
*addend += (targetAddress - reloc.value);
|
||||
return std::error_code();
|
||||
case ARM_RELOC_VANILLA | rExtern | rLength4:
|
||||
// ex: .long _foo (and _foo is undefined)
|
||||
*kind = pointer32;
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = instruction;
|
||||
return std::error_code();
|
||||
case ARM_RELOC_VANILLA | rLength4:
|
||||
// ex: .long _foo (and _foo is defined)
|
||||
*kind = pointer32;
|
||||
if (E ec = atomFromAddress(reloc.symbol, instruction, target, addend))
|
||||
return ec;
|
||||
*addend = clearThumbBit((uint32_t) * addend, *target);
|
||||
return std::error_code();
|
||||
case ARM_RELOC_VANILLA | rScattered | rLength4:
|
||||
// ex: .long _foo+a (and _foo is defined)
|
||||
*kind = pointer32;
|
||||
if (E ec = atomFromAddress(0, reloc.value, target, addend))
|
||||
return ec;
|
||||
*addend += (clearThumbBit(instruction, *target) - reloc.value);
|
||||
return std::error_code();
|
||||
default:
|
||||
return make_dynamic_error_code(Twine("unsupported arm relocation type"));
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code
|
||||
ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
FindAtomBySectionAndAddress atomFromAddr,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) {
|
||||
bool pointerDiff = false;
|
||||
bool funcRel;
|
||||
bool top;
|
||||
bool thumbReloc;
|
||||
switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
||||
case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
ARM_RELOC_PAIR | rScattered | rLength4):
|
||||
// ex: movw r1, :lower16:(_x-L1) [thumb mode]
|
||||
*kind = thumb_movw_funcRel;
|
||||
funcRel = true;
|
||||
top = false;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLength8) << 16 |
|
||||
ARM_RELOC_PAIR | rScattered | rLength8):
|
||||
// ex: movt r1, :upper16:(_x-L1) [thumb mode]
|
||||
*kind = thumb_movt_funcRel;
|
||||
funcRel = true;
|
||||
top = true;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLength1) << 16 |
|
||||
ARM_RELOC_PAIR | rScattered | rLength1):
|
||||
// ex: movw r1, :lower16:(_x-L1) [arm mode]
|
||||
*kind = arm_movw_funcRel;
|
||||
funcRel = true;
|
||||
top = false;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLength2) << 16 |
|
||||
ARM_RELOC_PAIR | rScattered | rLength2):
|
||||
// ex: movt r1, :upper16:(_x-L1) [arm mode]
|
||||
*kind = arm_movt_funcRel;
|
||||
funcRel = true;
|
||||
top = true;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rLength4) << 16 |
|
||||
ARM_RELOC_PAIR | rLength4):
|
||||
// ex: movw r1, :lower16:_x [thumb mode]
|
||||
*kind = thumb_movw;
|
||||
funcRel = false;
|
||||
top = false;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rLength8) << 16 |
|
||||
ARM_RELOC_PAIR | rLength8):
|
||||
// ex: movt r1, :upper16:_x [thumb mode]
|
||||
*kind = thumb_movt;
|
||||
funcRel = false;
|
||||
top = true;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rLength1) << 16 |
|
||||
ARM_RELOC_PAIR | rLength1):
|
||||
// ex: movw r1, :lower16:_x [arm mode]
|
||||
*kind = arm_movw;
|
||||
funcRel = false;
|
||||
top = false;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rLength2) << 16 |
|
||||
ARM_RELOC_PAIR | rLength2):
|
||||
// ex: movt r1, :upper16:_x [arm mode]
|
||||
*kind = arm_movt;
|
||||
funcRel = false;
|
||||
top = true;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rScattered | rLength4) << 16 |
|
||||
ARM_RELOC_PAIR | rLength4):
|
||||
// ex: movw r1, :lower16:_x+a [thumb mode]
|
||||
*kind = thumb_movw;
|
||||
funcRel = false;
|
||||
top = false;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rScattered | rLength8) << 16 |
|
||||
ARM_RELOC_PAIR | rLength8):
|
||||
// ex: movt r1, :upper16:_x+a [thumb mode]
|
||||
*kind = thumb_movt;
|
||||
funcRel = false;
|
||||
top = true;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rScattered | rLength1) << 16 |
|
||||
ARM_RELOC_PAIR | rLength1):
|
||||
// ex: movw r1, :lower16:_x+a [arm mode]
|
||||
*kind = arm_movw;
|
||||
funcRel = false;
|
||||
top = false;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rScattered | rLength2) << 16 |
|
||||
ARM_RELOC_PAIR | rLength2):
|
||||
// ex: movt r1, :upper16:_x+a [arm mode]
|
||||
*kind = arm_movt;
|
||||
funcRel = false;
|
||||
top = true;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rExtern | rLength4) << 16 |
|
||||
ARM_RELOC_PAIR | rLength4):
|
||||
// ex: movw r1, :lower16:_undef [thumb mode]
|
||||
*kind = thumb_movw;
|
||||
funcRel = false;
|
||||
top = false;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rExtern | rLength8) << 16 |
|
||||
ARM_RELOC_PAIR | rLength8):
|
||||
// ex: movt r1, :upper16:_undef [thumb mode]
|
||||
*kind = thumb_movt;
|
||||
funcRel = false;
|
||||
top = true;
|
||||
thumbReloc = true;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rExtern | rLength1) << 16 |
|
||||
ARM_RELOC_PAIR | rLength1):
|
||||
// ex: movw r1, :lower16:_undef [arm mode]
|
||||
*kind = arm_movw;
|
||||
funcRel = false;
|
||||
top = false;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_HALF | rExtern | rLength2) << 16 |
|
||||
ARM_RELOC_PAIR | rLength2):
|
||||
// ex: movt r1, :upper16:_undef [arm mode]
|
||||
*kind = arm_movt;
|
||||
funcRel = false;
|
||||
top = true;
|
||||
thumbReloc = false;
|
||||
break;
|
||||
case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
ARM_RELOC_PAIR | rScattered | rLength4):
|
||||
case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
ARM_RELOC_PAIR | rScattered | rLength4):
|
||||
// ex: .long _foo - .
|
||||
pointerDiff = true;
|
||||
break;
|
||||
default:
|
||||
return make_dynamic_error_code(Twine("unsupported arm relocation pair"));
|
||||
}
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
std::error_code ec;
|
||||
uint32_t instruction = readU32(swap, fixupContent);
|
||||
uint32_t value;
|
||||
uint32_t fromAddress;
|
||||
uint32_t toAddress;
|
||||
uint16_t instruction16;
|
||||
uint16_t other16;
|
||||
const lld::Atom *fromTarget;
|
||||
Reference::Addend offsetInTo;
|
||||
Reference::Addend offsetInFrom;
|
||||
if (pointerDiff) {
|
||||
toAddress = reloc1.value;
|
||||
fromAddress = reloc2.value;
|
||||
ec = atomFromAddr(0, toAddress, target, &offsetInTo);
|
||||
if (ec)
|
||||
return ec;
|
||||
ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom);
|
||||
if (ec)
|
||||
return ec;
|
||||
if (fromTarget != inAtom)
|
||||
return make_dynamic_error_code(Twine("SECTDIFF relocation where "
|
||||
"subtrahend label is not in atom"));
|
||||
*kind = delta32;
|
||||
value = clearThumbBit(instruction, *target);
|
||||
*addend = value - (toAddress - fromAddress);
|
||||
} else if (funcRel) {
|
||||
toAddress = reloc1.value;
|
||||
fromAddress = reloc2.value;
|
||||
ec = atomFromAddr(0, toAddress, target, &offsetInTo);
|
||||
if (ec)
|
||||
return ec;
|
||||
ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom);
|
||||
if (ec)
|
||||
return ec;
|
||||
if (fromTarget != inAtom)
|
||||
return make_dynamic_error_code(
|
||||
Twine("ARM_RELOC_HALF_SECTDIFF relocation "
|
||||
"where subtrahend label is not in atom"));
|
||||
other16 = (reloc2.offset & 0xFFFF);
|
||||
if (thumbReloc)
|
||||
instruction16 = getWordFromThumbMov(instruction);
|
||||
else
|
||||
instruction16 = getWordFromArmMov(instruction);
|
||||
if (top)
|
||||
value = (instruction16 << 16) | other16;
|
||||
else
|
||||
value = (other16 << 16) | instruction16;
|
||||
value = clearThumbBit(value, *target);
|
||||
int64_t ta = (int64_t) value - (toAddress - fromAddress);
|
||||
*addend = ta - offsetInFrom;
|
||||
return std::error_code();
|
||||
} else {
|
||||
uint32_t sectIndex;
|
||||
if (thumbReloc)
|
||||
instruction16 = getWordFromThumbMov(instruction);
|
||||
else
|
||||
instruction16 = getWordFromArmMov(instruction);
|
||||
other16 = (reloc2.offset & 0xFFFF);
|
||||
if (top)
|
||||
value = (instruction16 << 16) | other16;
|
||||
else
|
||||
value = (other16 << 16) | instruction16;
|
||||
if (reloc1.isExtern) {
|
||||
ec = atomFromSymbolIndex(reloc1.symbol, target);
|
||||
if (ec)
|
||||
return ec;
|
||||
*addend = value;
|
||||
} else {
|
||||
if (reloc1.scattered) {
|
||||
toAddress = reloc1.value;
|
||||
sectIndex = 0;
|
||||
} else {
|
||||
toAddress = value;
|
||||
sectIndex = reloc1.symbol;
|
||||
}
|
||||
ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo);
|
||||
if (ec)
|
||||
return ec;
|
||||
*addend = value - toAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
void ArchHandler_arm::applyFixup(Reference::KindNamespace ns,
|
||||
Reference::KindArch arch,
|
||||
Reference::KindValue kindValue,
|
||||
uint64_t addend, uint8_t *location,
|
||||
uint64_t fixupAddress, uint64_t targetAddress,
|
||||
uint64_t inAtomAddress) {
|
||||
if (ns != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(arch == Reference::KindArch::ARM);
|
||||
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
|
||||
int32_t displacement;
|
||||
// FIXME: these writes may need a swap.
|
||||
switch (kindValue) {
|
||||
case thumb_b22:
|
||||
// FIXME
|
||||
break;
|
||||
case thumb_movw:
|
||||
// FIXME
|
||||
break;
|
||||
case thumb_movt:
|
||||
// FIXME
|
||||
break;
|
||||
case thumb_movw_funcRel:
|
||||
// FIXME
|
||||
break;
|
||||
case thumb_movt_funcRel:
|
||||
// FIXME
|
||||
break;
|
||||
case arm_b24:
|
||||
displacement = (targetAddress - (fixupAddress + 8)) + addend;
|
||||
*loc32 = setDisplacementInArmBranch(*loc32, displacement);
|
||||
break;
|
||||
case arm_movw:
|
||||
// FIXME
|
||||
break;
|
||||
case arm_movt:
|
||||
// FIXME
|
||||
break;
|
||||
case arm_movw_funcRel:
|
||||
// FIXME
|
||||
break;
|
||||
case arm_movt_funcRel:
|
||||
// FIXME
|
||||
break;
|
||||
case pointer32:
|
||||
// FIXME
|
||||
break;
|
||||
case delta32:
|
||||
// FIXME
|
||||
break;
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
// do nothing
|
||||
break;
|
||||
case invalid:
|
||||
llvm_unreachable("invalid ARM Reference Kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() {
|
||||
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm());
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -0,0 +1,331 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler_x86.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"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class ArchHandler_x86 : public ArchHandler {
|
||||
public:
|
||||
ArchHandler_x86();
|
||||
virtual ~ArchHandler_x86();
|
||||
|
||||
const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
|
||||
|
||||
Reference::KindArch kindArch() override { return Reference::KindArch::x86; }
|
||||
|
||||
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;
|
||||
|
||||
void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress)
|
||||
override;
|
||||
|
||||
private:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfo;
|
||||
|
||||
enum : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
branch32, /// ex: call _foo
|
||||
branch16, /// ex: callw _foo
|
||||
abs32, /// ex: movl _foo, %eax
|
||||
funcRel32, /// ex: movl _foo-L1(%eax), %eax
|
||||
pointer32, /// ex: .long _foo
|
||||
delta32, /// ex: .long _foo - .
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
};
|
||||
|
||||
const bool _swap;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ArchHandler_x86
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
ArchHandler_x86::ArchHandler_x86() :
|
||||
_swap(!MachOLinkingContext::isHostEndian(MachOLinkingContext::arch_x86)) {}
|
||||
|
||||
ArchHandler_x86::~ArchHandler_x86() { }
|
||||
|
||||
const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(invalid),
|
||||
LLD_KIND_STRING_ENTRY(branch32),
|
||||
LLD_KIND_STRING_ENTRY(branch16),
|
||||
LLD_KIND_STRING_ENTRY(abs32),
|
||||
LLD_KIND_STRING_ENTRY(funcRel32),
|
||||
LLD_KIND_STRING_ENTRY(pointer32),
|
||||
LLD_KIND_STRING_ENTRY(delta32),
|
||||
LLD_KIND_STRING_ENTRY(lazyPointer),
|
||||
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
||||
LLD_KIND_STRING_END
|
||||
};
|
||||
|
||||
const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
|
||||
"dyld_stub_binder",
|
||||
|
||||
// Lazy pointer references
|
||||
{ Reference::KindArch::x86, pointer32, 0, 0 },
|
||||
{ Reference::KindArch::x86, lazyPointer, 0, 0 },
|
||||
|
||||
// GOT pointer to dyld_stub_binder
|
||||
{ Reference::KindArch::x86, pointer32, 0, 0 },
|
||||
|
||||
// x86 code alignment
|
||||
1,
|
||||
|
||||
// Stub size and code
|
||||
6,
|
||||
{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
|
||||
{ Reference::KindArch::x86, abs32, 2, 0 },
|
||||
|
||||
// Stub Helper size and code
|
||||
10,
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
|
||||
{ Reference::KindArch::x86, lazyImmediateLocation, 1, 0 },
|
||||
{ Reference::KindArch::x86, branch32, 6, 0 },
|
||||
|
||||
// Stub Helper-Common size and code
|
||||
12,
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache
|
||||
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
|
||||
0x90 }, // nop
|
||||
{ Reference::KindArch::x86, abs32, 1, 0 },
|
||||
{ Reference::KindArch::x86, abs32, 7, 0 }
|
||||
};
|
||||
|
||||
bool ArchHandler_x86::isCallSite(const Reference &ref) {
|
||||
return (ref.kindValue() == branch32);
|
||||
}
|
||||
|
||||
bool ArchHandler_x86::isPointer(const Reference &ref) {
|
||||
return (ref.kindValue() == pointer32);
|
||||
}
|
||||
|
||||
bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) {
|
||||
if (!reloc.scattered)
|
||||
return false;
|
||||
return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) ||
|
||||
(reloc.type == GENERIC_RELOC_SECTDIFF);
|
||||
}
|
||||
|
||||
std::error_code
|
||||
ArchHandler_x86::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;
|
||||
DefinedAtom::ContentPermissions perms;
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
uint64_t targetAddress;
|
||||
switch (relocPattern(reloc)) {
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4:
|
||||
// ex: call _foo (and _foo undefined)
|
||||
*kind = branch32;
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = fixupAddress + 4 + readS32(swap, fixupContent);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rLength4:
|
||||
// ex: call _foo (and _foo defined)
|
||||
*kind = branch32;
|
||||
targetAddress = fixupAddress + 4 + readS32(swap, fixupContent);
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2:
|
||||
// ex: callw _foo (and _foo undefined)
|
||||
*kind = branch16;
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = fixupAddress + 2 + readS16(swap, fixupContent);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rPcRel | rLength2:
|
||||
// ex: callw _foo (and _foo defined)
|
||||
*kind = branch16;
|
||||
targetAddress = fixupAddress + 2 + readS16(swap, fixupContent);
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rExtern | rLength4:
|
||||
// ex: movl _foo, %eax (and _foo undefined)
|
||||
// ex: .long _foo (and _foo undefined)
|
||||
perms = inAtom->permissions();
|
||||
*kind =
|
||||
((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
|
||||
: pointer32;
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = readU32(swap, fixupContent);
|
||||
break;
|
||||
case GENERIC_RELOC_VANILLA | rLength4:
|
||||
// ex: movl _foo, %eax (and _foo defined)
|
||||
// ex: .long _foo (and _foo defined)
|
||||
perms = inAtom->permissions();
|
||||
*kind =
|
||||
((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
|
||||
: pointer32;
|
||||
targetAddress = readU32(swap, fixupContent);
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
break;
|
||||
default:
|
||||
return make_dynamic_error_code(Twine("unsupported i386 relocation type"));
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code
|
||||
ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2,
|
||||
const DefinedAtom *inAtom,
|
||||
uint32_t offsetInAtom,
|
||||
uint64_t fixupAddress, bool swap,
|
||||
FindAtomBySectionAndAddress atomFromAddr,
|
||||
FindAtomBySymbolIndex atomFromSymbolIndex,
|
||||
Reference::KindValue *kind,
|
||||
const lld::Atom **target,
|
||||
Reference::Addend *addend) {
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
std::error_code ec;
|
||||
DefinedAtom::ContentPermissions perms = inAtom->permissions();
|
||||
uint32_t fromAddress;
|
||||
uint32_t toAddress;
|
||||
uint32_t value;
|
||||
const lld::Atom *fromTarget;
|
||||
Reference::Addend offsetInTo;
|
||||
Reference::Addend offsetInFrom;
|
||||
switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
||||
case((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4)
|
||||
:
|
||||
case((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
|
||||
GENERIC_RELOC_PAIR | rScattered | rLength4)
|
||||
:
|
||||
toAddress = reloc1.value;
|
||||
fromAddress = reloc2.value;
|
||||
value = readS32(swap, fixupContent);
|
||||
ec = atomFromAddr(0, toAddress, target, &offsetInTo);
|
||||
if (ec)
|
||||
return ec;
|
||||
ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom);
|
||||
if (ec)
|
||||
return ec;
|
||||
if (fromTarget != inAtom)
|
||||
return make_dynamic_error_code(Twine("SECTDIFF relocation where "
|
||||
"subtrahend label is not in atom"));
|
||||
*kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? funcRel32
|
||||
: delta32;
|
||||
if (*kind == funcRel32) {
|
||||
// SECTDIFF relocations are used in i386 codegen where the function
|
||||
// prolog does a CALL to the next instruction which POPs the return
|
||||
// address into EBX which becomes the pic-base register. The POP
|
||||
// instruction is label the used for the subtrahend in expressions.
|
||||
// The funcRel32 kind represents the 32-bit delta to some symbol from
|
||||
// the start of the function (atom) containing the funcRel32.
|
||||
uint32_t ta = fromAddress + value - toAddress;
|
||||
*addend = ta - offsetInFrom;
|
||||
} else {
|
||||
*addend = fromAddress + value - toAddress;
|
||||
}
|
||||
return std::error_code();
|
||||
break;
|
||||
default:
|
||||
return make_dynamic_error_code(Twine("unsupported i386 relocation type"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86::applyFixup(Reference::KindNamespace ns,
|
||||
Reference::KindArch arch,
|
||||
Reference::KindValue kindValue,
|
||||
uint64_t addend, uint8_t *location,
|
||||
uint64_t fixupAddress, uint64_t targetAddress,
|
||||
uint64_t inAtomAddress) {
|
||||
if (ns != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(arch == Reference::KindArch::x86);
|
||||
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
|
||||
int16_t *loc16 = reinterpret_cast<int16_t *>(location);
|
||||
switch (kindValue) {
|
||||
case branch32:
|
||||
write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend);
|
||||
break;
|
||||
case branch16:
|
||||
write16(*loc16, _swap, (targetAddress - (fixupAddress + 4)) + addend);
|
||||
break;
|
||||
case pointer32:
|
||||
case abs32:
|
||||
write32(*loc32, _swap, targetAddress + addend);
|
||||
break;
|
||||
case funcRel32:
|
||||
write32(*loc32, _swap, targetAddress - inAtomAddress + addend); // FIXME
|
||||
break;
|
||||
case delta32:
|
||||
write32(*loc32, _swap, targetAddress - fixupAddress + addend);
|
||||
break;
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("invalid x86 Reference Kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() {
|
||||
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86());
|
||||
}
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
|
@ -0,0 +1,421 @@
|
|||
//===- lib/FileFormat/MachO/ArchHandler_x86_64.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"
|
||||
|
||||
using namespace llvm::MachO;
|
||||
using namespace lld::mach_o::normalized;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
class ArchHandler_x86_64 : public ArchHandler {
|
||||
public:
|
||||
ArchHandler_x86_64();
|
||||
virtual ~ArchHandler_x86_64();
|
||||
|
||||
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;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
const_cast<Reference *>(ref)
|
||||
->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea);
|
||||
}
|
||||
|
||||
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 void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress)
|
||||
override;
|
||||
|
||||
private:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfo;
|
||||
|
||||
enum : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// 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
|
||||
ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax
|
||||
ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip)
|
||||
pointer64, /// ex: .quad _foo
|
||||
pointer64Anon, /// ex: .quad L1
|
||||
delta64, /// ex: .quad _foo - .
|
||||
delta32, /// ex: .long _foo - .
|
||||
delta64Anon, /// ex: .quad L1 - .
|
||||
delta32Anon, /// ex: .long L1 - .
|
||||
|
||||
// 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.
|
||||
};
|
||||
|
||||
Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
|
||||
Reference::KindValue kindFromRelocPair(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2);
|
||||
|
||||
const bool _swap;
|
||||
};
|
||||
|
||||
|
||||
ArchHandler_x86_64::ArchHandler_x86_64() :
|
||||
_swap(!MachOLinkingContext::isHostEndian(MachOLinkingContext::arch_x86_64)) {}
|
||||
|
||||
ArchHandler_x86_64::~ArchHandler_x86_64() { }
|
||||
|
||||
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(invalid), 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(ripRel32GotLoad),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Got), 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_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 },
|
||||
|
||||
// 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-Common size and code
|
||||
16,
|
||||
{ 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 },
|
||||
{ Reference::KindArch::x86_64, ripRel32, 11, 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_2 | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Minus2;
|
||||
case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4:
|
||||
return ripRel32Minus4;
|
||||
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_UNSIGNED | rExtern | rLength8:
|
||||
return pointer64;
|
||||
case X86_64_RELOC_UNSIGNED | rLength8:
|
||||
return pointer64Anon;
|
||||
default:
|
||||
return invalid;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code
|
||||
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) {
|
||||
typedef std::error_code E;
|
||||
*kind = kindFromReloc(reloc);
|
||||
if (*kind == invalid)
|
||||
return make_dynamic_error_code(Twine("unknown type"));
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
uint64_t targetAddress;
|
||||
switch (*kind) {
|
||||
case branch32:
|
||||
case ripRel32:
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = readS32(swap, fixupContent);
|
||||
return std::error_code();
|
||||
case ripRel32Minus1:
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = readS32(swap, fixupContent) + 1;
|
||||
return std::error_code();
|
||||
case ripRel32Minus2:
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = readS32(swap, fixupContent) + 2;
|
||||
return std::error_code();
|
||||
case ripRel32Minus4:
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = readS32(swap, fixupContent) + 4;
|
||||
return std::error_code();
|
||||
case ripRel32Anon:
|
||||
targetAddress = fixupAddress + 4 + readS32(swap, fixupContent);
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
case ripRel32GotLoad:
|
||||
case ripRel32Got:
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = 0;
|
||||
return std::error_code();
|
||||
case pointer64:
|
||||
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
|
||||
return ec;
|
||||
*addend = readS64(swap, fixupContent);
|
||||
return std::error_code();
|
||||
case pointer64Anon:
|
||||
targetAddress = readS64(swap, fixupContent);
|
||||
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
|
||||
default:
|
||||
llvm_unreachable("bad reloc kind");
|
||||
}
|
||||
}
|
||||
|
||||
Reference::KindValue
|
||||
ArchHandler_x86_64::kindFromRelocPair(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2) {
|
||||
switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength8):
|
||||
return delta64;
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rExtern | rLength4):
|
||||
return delta32;
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rLength8):
|
||||
return delta64Anon;
|
||||
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
|
||||
X86_64_RELOC_UNSIGNED | rLength4):
|
||||
return delta32Anon;
|
||||
default:
|
||||
llvm_unreachable("bad reloc pairs");
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code
|
||||
ArchHandler_x86_64::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) {
|
||||
*kind = kindFromRelocPair(reloc1, reloc2);
|
||||
if (*kind == invalid)
|
||||
return make_dynamic_error_code(Twine("unknown pair"));
|
||||
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
|
||||
typedef std::error_code E;
|
||||
uint64_t targetAddress;
|
||||
const lld::Atom *fromTarget;
|
||||
if (E ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget))
|
||||
return ec;
|
||||
if (fromTarget != inAtom)
|
||||
return make_dynamic_error_code(Twine("pointer diff not in base atom"));
|
||||
switch (*kind) {
|
||||
case delta64:
|
||||
if (E ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
*addend = readS64(swap, fixupContent) + offsetInAtom;
|
||||
return std::error_code();
|
||||
case delta32:
|
||||
if (E ec = atomFromSymbolIndex(reloc2.symbol, target))
|
||||
return ec;
|
||||
*addend = readS32(swap, fixupContent) + offsetInAtom;
|
||||
return std::error_code();
|
||||
case delta64Anon:
|
||||
targetAddress = offsetInAtom + readS64(swap, fixupContent);
|
||||
return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
|
||||
case delta32Anon:
|
||||
targetAddress = offsetInAtom + readS32(swap, fixupContent);
|
||||
return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
|
||||
default:
|
||||
llvm_unreachable("bad reloc pair kind");
|
||||
}
|
||||
}
|
||||
|
||||
void ArchHandler_x86_64::applyFixup(Reference::KindNamespace ns,
|
||||
Reference::KindArch arch,
|
||||
Reference::KindValue kindValue,
|
||||
uint64_t addend, uint8_t *location,
|
||||
uint64_t fixupAddress,
|
||||
uint64_t targetAddress,
|
||||
uint64_t inAtomAddress) {
|
||||
if (ns != Reference::KindNamespace::mach_o)
|
||||
return;
|
||||
assert(arch == Reference::KindArch::x86_64);
|
||||
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
|
||||
uint64_t *loc64 = reinterpret_cast<uint64_t *>(location);
|
||||
switch (kindValue) {
|
||||
case branch32:
|
||||
case ripRel32:
|
||||
case ripRel32Got:
|
||||
case ripRel32GotLoad:
|
||||
write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend);
|
||||
break;
|
||||
case pointer64:
|
||||
case pointer64Anon:
|
||||
write64(*loc64, _swap, targetAddress + addend);
|
||||
break;
|
||||
case ripRel32Minus1:
|
||||
write32(*loc32, _swap, (targetAddress - (fixupAddress + 5)) + addend);
|
||||
break;
|
||||
case ripRel32Minus2:
|
||||
write32(*loc32, _swap, (targetAddress - (fixupAddress + 6)) + addend);
|
||||
break;
|
||||
case ripRel32Minus4:
|
||||
write32(*loc32, _swap, (targetAddress - (fixupAddress + 8)) + addend);
|
||||
break;
|
||||
case delta32:
|
||||
case delta32Anon:
|
||||
write32(*loc32, _swap, (targetAddress - fixupAddress) + addend);
|
||||
break;
|
||||
case delta64:
|
||||
case delta64Anon:
|
||||
write64(*loc64, _swap, (targetAddress - fixupAddress) + addend);
|
||||
break;
|
||||
case ripRel32GotLoadNowLea:
|
||||
// Change MOVQ to LEA
|
||||
assert(location[-2] == 0x8B);
|
||||
location[-2] = 0x8D;
|
||||
write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend);
|
||||
break;
|
||||
case lazyPointer:
|
||||
case lazyImmediateLocation:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("invalid x86_64 Reference Kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
|
@ -1,4 +1,8 @@
|
|||
add_lld_library(lldMachO
|
||||
ArchHandler.cpp
|
||||
ArchHandler_arm.cpp
|
||||
ArchHandler_x86.cpp
|
||||
ArchHandler_x86_64.cpp
|
||||
GOTPass.cpp
|
||||
MachOLinkingContext.cpp
|
||||
MachONormalizedFileBinaryReader.cpp
|
||||
|
@ -6,7 +10,6 @@ add_lld_library(lldMachO
|
|||
MachONormalizedFileFromAtoms.cpp
|
||||
MachONormalizedFileToAtoms.cpp
|
||||
MachONormalizedFileYAML.cpp
|
||||
ReferenceKinds.cpp
|
||||
StubsPass.cpp
|
||||
WriterMachO.cpp
|
||||
)
|
||||
|
|
|
@ -31,15 +31,22 @@ namespace mach_o {
|
|||
class CRuntimeFile : public SimpleFile {
|
||||
public:
|
||||
CRuntimeFile(const MachOLinkingContext &context)
|
||||
: SimpleFile("C runtime"), _undefMain(*this, context.entrySymbolName()) {
|
||||
: SimpleFile("C runtime"),
|
||||
_undefMain(*this, context.entrySymbolName()),
|
||||
_undefBinder(*this, context.binderSymbolName()) {
|
||||
// only main executables need _main
|
||||
if (context.outputMachOType() == llvm::MachO::MH_EXECUTE) {
|
||||
this->addAtom(_undefMain);
|
||||
}
|
||||
// only dynamic binaries use stubs
|
||||
if (context.needsStubsPass()) {
|
||||
this->addAtom(_undefBinder);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SimpleUndefinedAtom _undefMain;
|
||||
SimpleUndefinedAtom _undefBinder;
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
|
|
|
@ -10,13 +10,14 @@
|
|||
#ifndef LLD_READER_WRITER_MACHO_FILE_H
|
||||
#define LLD_READER_WRITER_MACHO_FILE_H
|
||||
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
||||
#include "Atoms.h"
|
||||
#include "MachONormalizedFile.h"
|
||||
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace lld {
|
||||
|
@ -138,6 +139,8 @@ public:
|
|||
return pos->second;
|
||||
}
|
||||
|
||||
llvm::BumpPtrAllocator &allocator() { return _allocator; }
|
||||
|
||||
private:
|
||||
struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; };
|
||||
|
||||
|
|
|
@ -32,77 +32,143 @@
|
|||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachOPasses.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
#include "MachOPasses.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
static bool shouldReplaceTargetWithGOTAtom(const Atom *target,
|
||||
bool canBypassGOT) {
|
||||
// Accesses to shared library symbols must go through GOT.
|
||||
if (target->definition() == Atom::definitionSharedLibrary)
|
||||
return true;
|
||||
// Accesses to interposable symbols in same linkage unit must also go
|
||||
// through GOT.
|
||||
const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
|
||||
if (defTarget != nullptr &&
|
||||
defTarget->interposable() != DefinedAtom::interposeNo) {
|
||||
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
||||
return true;
|
||||
|
||||
//
|
||||
// GOT Entry Atom created by the GOT pass.
|
||||
//
|
||||
class GOTEntryAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
GOTEntryAtom(const File &file, bool is64)
|
||||
: SimpleDefinedAtom(file), _is64(is64) { }
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeGOT;
|
||||
}
|
||||
// Target does not require indirection. So, if instruction allows GOT to be
|
||||
// by-passed, do that optimization and don't create GOT entry.
|
||||
return !canBypassGOT;
|
||||
}
|
||||
|
||||
static const DefinedAtom *
|
||||
findGOTAtom(const Atom *target,
|
||||
llvm::DenseMap<const Atom *, const DefinedAtom *> &targetToGOT) {
|
||||
auto pos = targetToGOT.find(target);
|
||||
return (pos == targetToGOT.end()) ? nullptr : pos->second;
|
||||
}
|
||||
Alignment alignment() const override {
|
||||
return Alignment(_is64 ? 3 : 2);
|
||||
}
|
||||
|
||||
void GOTPass::perform(std::unique_ptr<MutableFile> &mergedFile) {
|
||||
// Use map so all pointers to same symbol use same GOT entry.
|
||||
llvm::DenseMap<const Atom*, const DefinedAtom*> targetToGOT;
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile->defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at instructions accessing the GOT.
|
||||
bool canBypassGOT;
|
||||
if (!isGOTAccess(*ref, canBypassGOT))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) {
|
||||
// Update reference kind to reflect that target is a direct accesss.
|
||||
updateReferenceToGOT(ref, false);
|
||||
continue;
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
};
|
||||
|
||||
|
||||
/// Pass for instantiating and optimizing GOT slots.
|
||||
///
|
||||
class GOTPass : public Pass {
|
||||
public:
|
||||
GOTPass(const MachOLinkingContext &context)
|
||||
: _context(context), _archHandler(_context.archHandler()),
|
||||
_file("<mach-o GOT Pass>") { }
|
||||
|
||||
private:
|
||||
|
||||
void perform(std::unique_ptr<MutableFile> &mergedFile) override {
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile->defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at instructions accessing the GOT.
|
||||
bool canBypassGOT;
|
||||
if (!_archHandler.isGOTAccess(*ref, canBypassGOT))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
|
||||
if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) {
|
||||
// Update reference kind to reflect that target is a direct accesss.
|
||||
_archHandler.updateReferenceToGOT(ref, false);
|
||||
} else {
|
||||
// Replace the target with a reference to a GOT entry.
|
||||
const DefinedAtom *gotEntry = makeGOTEntry(target);
|
||||
const_cast<Reference *>(ref)->setTarget(gotEntry);
|
||||
// Update reference kind to reflect that target is now a GOT entry.
|
||||
_archHandler.updateReferenceToGOT(ref, true);
|
||||
}
|
||||
}
|
||||
// Replace the target with a reference to a GOT entry.
|
||||
const DefinedAtom *gotEntry = findGOTAtom(target, targetToGOT);
|
||||
if (!gotEntry) {
|
||||
gotEntry = makeGOTEntry(*target);
|
||||
assert(gotEntry != nullptr);
|
||||
assert(gotEntry->contentType() == DefinedAtom::typeGOT);
|
||||
targetToGOT[target] = gotEntry;
|
||||
}
|
||||
const_cast<Reference *>(ref)->setTarget(gotEntry);
|
||||
// Update reference kind to reflect that target is now a GOT entry.
|
||||
updateReferenceToGOT(ref, true);
|
||||
}
|
||||
|
||||
// add all created GOT Atoms to master file
|
||||
for (auto &it : _targetToGOT)
|
||||
mergedFile->addAtom(*it.second);
|
||||
}
|
||||
|
||||
// add all created GOT Atoms to master file
|
||||
for (auto &it : targetToGOT)
|
||||
mergedFile->addAtom(*it.second);
|
||||
}
|
||||
bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) {
|
||||
// Accesses to shared library symbols must go through GOT.
|
||||
if (target->definition() == Atom::definitionSharedLibrary)
|
||||
return true;
|
||||
// Accesses to interposable symbols in same linkage unit must also go
|
||||
// through GOT.
|
||||
const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
|
||||
if (defTarget != nullptr &&
|
||||
defTarget->interposable() != DefinedAtom::interposeNo) {
|
||||
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
||||
return true;
|
||||
}
|
||||
// Target does not require indirection. So, if instruction allows GOT to be
|
||||
// by-passed, do that optimization and don't create GOT entry.
|
||||
return !canBypassGOT;
|
||||
}
|
||||
|
||||
const DefinedAtom *makeGOTEntry(const Atom *target) {
|
||||
auto pos = _targetToGOT.find(target);
|
||||
if (pos == _targetToGOT.end()) {
|
||||
GOTEntryAtom *gotEntry = new (_file.allocator())
|
||||
GOTEntryAtom(_file, _context.is64Bit());
|
||||
_targetToGOT[target] = gotEntry;
|
||||
const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo().
|
||||
nonLazyPointerReferenceToBinder;
|
||||
gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
|
||||
nlInfo.kind, 0, target, 0);
|
||||
return gotEntry;
|
||||
}
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
|
||||
const MachOLinkingContext &_context;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
MachOFile _file;
|
||||
llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
assert(ctx.needsGOTPass());
|
||||
pm.add(std::unique_ptr<Pass>(new GOTPass(ctx)));
|
||||
}
|
||||
|
||||
|
||||
} // end namesapce mach_o
|
||||
} // end namesapce lld
|
||||
|
|
|
@ -24,27 +24,6 @@ namespace lld {
|
|||
namespace mach_o {
|
||||
|
||||
|
||||
class GOTPass : public lld::GOTPass {
|
||||
public:
|
||||
bool noTextRelocs() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isGOTAccess(const Reference &, bool &canBypassGOT) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateReferenceToGOT(const Reference*, bool targetIsNowGOT) override {
|
||||
|
||||
}
|
||||
|
||||
const DefinedAtom* makeGOTEntry(const Atom&) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
#include "GOTPass.hpp"
|
||||
#include "StubsPass.hpp"
|
||||
#include "ReferenceKinds.h"
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "MachOPasses.h"
|
||||
|
||||
#include "lld/Core/PassManager.h"
|
||||
#include "lld/ReaderWriter/Reader.h"
|
||||
|
@ -25,7 +25,7 @@
|
|||
#include "llvm/Support/MachO.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using lld::mach_o::KindHandler;
|
||||
using lld::mach_o::ArchHandler;
|
||||
using namespace llvm::MachO;
|
||||
|
||||
namespace lld {
|
||||
|
@ -125,7 +125,7 @@ MachOLinkingContext::MachOLinkingContext()
|
|||
_doNothing(false), _arch(arch_unknown), _os(OS::macOSX), _osMinVersion(0),
|
||||
_pageZeroSize(0), _pageSize(4096), _compatibilityVersion(0),
|
||||
_currentVersion(0), _deadStrippableDylib(false), _printAtoms(false),
|
||||
_testingLibResolution(false), _kindHandler(nullptr) {}
|
||||
_testingLibResolution(false), _archHandler(nullptr) {}
|
||||
|
||||
MachOLinkingContext::~MachOLinkingContext() {}
|
||||
|
||||
|
@ -223,6 +223,33 @@ bool MachOLinkingContext::outputTypeHasEntry() const {
|
|||
}
|
||||
}
|
||||
|
||||
bool MachOLinkingContext::needsStubsPass() const {
|
||||
switch (_outputMachOType) {
|
||||
case MH_EXECUTE:
|
||||
return !_outputMachOTypeStatic;
|
||||
case MH_DYLIB:
|
||||
case MH_BUNDLE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MachOLinkingContext::needsGOTPass() const {
|
||||
// Only x86_64 uses GOT pass but not in -r mode.
|
||||
if (_arch != arch_x86_64)
|
||||
return false;
|
||||
return (_outputMachOType != MH_OBJECT);
|
||||
}
|
||||
|
||||
|
||||
StringRef MachOLinkingContext::binderSymbolName() const {
|
||||
return archHandler().stubInfo().binderSymbolName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const {
|
||||
uint32_t parsedVersion;
|
||||
switch (_os) {
|
||||
|
@ -375,11 +402,11 @@ bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) {
|
|||
}
|
||||
|
||||
void MachOLinkingContext::addPasses(PassManager &pm) {
|
||||
if (outputMachOType() != MH_OBJECT) {
|
||||
pm.add(std::unique_ptr<Pass>(new mach_o::GOTPass));
|
||||
pm.add(std::unique_ptr<Pass>(new mach_o::StubsPass(*this)));
|
||||
}
|
||||
pm.add(std::unique_ptr<Pass>(new LayoutPass(registry())));
|
||||
if (needsStubsPass())
|
||||
mach_o::addStubsPass(pm, *this);
|
||||
if (needsGOTPass())
|
||||
mach_o::addGOTPass(pm, *this);
|
||||
}
|
||||
|
||||
Writer &MachOLinkingContext::writer() const {
|
||||
|
@ -388,10 +415,10 @@ Writer &MachOLinkingContext::writer() const {
|
|||
return *_writer;
|
||||
}
|
||||
|
||||
KindHandler &MachOLinkingContext::kindHandler() const {
|
||||
if (!_kindHandler)
|
||||
_kindHandler = KindHandler::create(_arch);
|
||||
return *_kindHandler;
|
||||
ArchHandler &MachOLinkingContext::archHandler() const {
|
||||
if (!_archHandler)
|
||||
_archHandler = ArchHandler::create(_arch);
|
||||
return *_archHandler;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
/// +------------+
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "ReferenceKinds.h"
|
||||
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
@ -442,27 +444,11 @@ private:
|
|||
} // namespace normalized
|
||||
} // namespace mach_o
|
||||
|
||||
void Registry::addSupportMachOObjects(StringRef archName) {
|
||||
MachOLinkingContext::Arch arch = MachOLinkingContext::archFromName(archName);
|
||||
void Registry::addSupportMachOObjects(const MachOLinkingContext &ctx) {
|
||||
MachOLinkingContext::Arch arch = ctx.arch();
|
||||
add(std::unique_ptr<Reader>(new mach_o::normalized::MachOReader(arch)));
|
||||
switch (arch) {
|
||||
case MachOLinkingContext::arch_x86_64:
|
||||
addKindTable(Reference::KindNamespace::mach_o, Reference::KindArch::x86_64,
|
||||
mach_o::KindHandler_x86_64::kindStrings);
|
||||
break;
|
||||
case MachOLinkingContext::arch_x86:
|
||||
addKindTable(Reference::KindNamespace::mach_o, Reference::KindArch::x86,
|
||||
mach_o::KindHandler_x86::kindStrings);
|
||||
break;
|
||||
case MachOLinkingContext::arch_armv6:
|
||||
case MachOLinkingContext::arch_armv7:
|
||||
case MachOLinkingContext::arch_armv7s:
|
||||
addKindTable(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
|
||||
mach_o::KindHandler_arm::kindStrings);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("mach-o arch not supported");
|
||||
}
|
||||
addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
|
||||
ctx.archHandler().kindStrings());
|
||||
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
|
||||
new mach_o::MachOYamlIOTaggedDocumentHandler(arch)));
|
||||
}
|
||||
|
|
|
@ -194,7 +194,17 @@ inline uint64_t read64(bool swap, uint64_t value) {
|
|||
return (swap ? getSwappedBytes(value) : value);
|
||||
}
|
||||
|
||||
inline void write16(int16_t &loc, bool swap, int16_t value) {
|
||||
loc = (swap ? getSwappedBytes(value) : value);
|
||||
}
|
||||
|
||||
inline void write32(int32_t &loc, bool swap, int32_t value) {
|
||||
loc = (swap ? getSwappedBytes(value) : value);
|
||||
}
|
||||
|
||||
inline void write64(uint64_t &loc, bool swap, uint64_t value) {
|
||||
loc = (swap ? getSwappedBytes(value) : value);
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
bitFieldExtract(uint32_t value, bool isBigEndianBigField, uint8_t firstBit,
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
/// +-------+
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "ReferenceKinds.h"
|
||||
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
@ -542,7 +544,7 @@ void Util::appendSection(SectionInfo *si, NormalizedFile &file) {
|
|||
// FIXME: Need a handler method to update content for .o file
|
||||
// output and any needed section relocations.
|
||||
} else {
|
||||
_context.kindHandler().applyFixup(
|
||||
_context.archHandler().applyFixup(
|
||||
ref->kindNamespace(), ref->kindArch(), ref->kindValue(),
|
||||
ref->addend(), &atomContent[offset], fixupAddress, targetAddress,
|
||||
atomAddress);
|
||||
|
@ -713,7 +715,7 @@ void Util::addSymbols(const lld::File &atomFile, NormalizedFile &file) {
|
|||
|
||||
const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) {
|
||||
for (const Reference *ref : *lpAtom) {
|
||||
if (_context.kindHandler().isLazyTarget(*ref)) {
|
||||
if (_context.archHandler().isLazyPointer(*ref)) {
|
||||
return ref->target();
|
||||
}
|
||||
}
|
||||
|
@ -871,7 +873,7 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile,
|
|||
uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom()
|
||||
- segmentStartAddr;
|
||||
const Atom* targ = ref->target();
|
||||
if (_context.kindHandler().isPointer(*ref)) {
|
||||
if (_context.archHandler().isPointer(*ref)) {
|
||||
// A pointer to a DefinedAtom requires rebasing.
|
||||
if (dyn_cast<DefinedAtom>(targ)) {
|
||||
RebaseLocation rebase;
|
||||
|
@ -893,7 +895,7 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile,
|
|||
nFile.bindingInfo.push_back(bind);
|
||||
}
|
||||
}
|
||||
if (_context.kindHandler().isLazyTarget(*ref)) {
|
||||
if (_context.archHandler().isLazyPointer(*ref)) {
|
||||
BindLocation bind;
|
||||
bind.segIndex = segmentIndex;
|
||||
bind.segOffset = segmentOffset;
|
||||
|
|
|
@ -21,10 +21,11 @@
|
|||
/// +-------+
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
#include "File.h"
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "Atoms.h"
|
||||
#include "ReferenceKinds.h"
|
||||
#include "File.h"
|
||||
#include "MachONormalizedFileBinaryUtils.h"
|
||||
|
||||
#include "lld/Core/Error.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
@ -431,8 +432,8 @@ std::error_code processSection(DefinedAtom::ContentType atomType,
|
|||
std::error_code convertRelocs(const Section §ion,
|
||||
const NormalizedFile &normalizedFile,
|
||||
MachOFile &file,
|
||||
KindHandler &handler) {
|
||||
// Utility function for KindHandler to find atom by its address.
|
||||
ArchHandler &handler) {
|
||||
// Utility function for ArchHandler to find atom by its address.
|
||||
auto atomByAddr = [&] (uint32_t sectIndex, uint64_t addr,
|
||||
const lld::Atom **atom, Reference::Addend *addend)
|
||||
-> std::error_code {
|
||||
|
@ -462,7 +463,7 @@ std::error_code convertRelocs(const Section §ion,
|
|||
return std::error_code();
|
||||
};
|
||||
|
||||
// Utility function for KindHandler to find atom by its symbol index.
|
||||
// Utility function for ArchHandler to find atom by its symbol index.
|
||||
auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result)
|
||||
-> std::error_code {
|
||||
// Find symbol from index.
|
||||
|
@ -536,7 +537,7 @@ std::error_code convertRelocs(const Section §ion,
|
|||
&target, &addend);
|
||||
}
|
||||
else {
|
||||
// Use KindHandler to convert relocation record into information
|
||||
// Use ArchHandler to convert relocation record into information
|
||||
// needed to instantiate an lld::Reference object.
|
||||
relocErr = handler.getReferenceInfo(reloc, inAtom, offsetInAtom,
|
||||
fixupAddress,swap, atomByAddr,
|
||||
|
@ -558,27 +559,8 @@ std::error_code convertRelocs(const Section §ion,
|
|||
+ ")" );
|
||||
} else {
|
||||
// Instantiate an lld::Reference object and add to its atom.
|
||||
Reference::KindArch arch = Reference::KindArch::all;
|
||||
switch (normalizedFile.arch ) {
|
||||
case lld::MachOLinkingContext::arch_x86_64:
|
||||
arch = Reference::KindArch::x86_64;
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_x86:
|
||||
arch = Reference::KindArch::x86;
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_ppc:
|
||||
arch = Reference::KindArch::PowerPC;
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_armv6:
|
||||
case lld::MachOLinkingContext::arch_armv7:
|
||||
case lld::MachOLinkingContext::arch_armv7s:
|
||||
arch = Reference::KindArch::ARM;
|
||||
break;
|
||||
case lld::MachOLinkingContext::arch_unknown:
|
||||
return make_dynamic_error_code(Twine("unknown architecture"));
|
||||
}
|
||||
|
||||
inAtom->addReference(offsetInAtom, kind, target, addend, arch);
|
||||
inAtom->addReference(offsetInAtom, kind, target, addend,
|
||||
handler.kindArch());
|
||||
}
|
||||
}
|
||||
return std::error_code();
|
||||
|
@ -616,8 +598,8 @@ normalizedObjectToAtoms(const NormalizedFile &normalizedFile, StringRef path,
|
|||
}
|
||||
|
||||
// Convert mach-o relocations to References
|
||||
std::unique_ptr<mach_o::KindHandler> handler
|
||||
= KindHandler::create(normalizedFile.arch);
|
||||
std::unique_ptr<mach_o::ArchHandler> handler
|
||||
= ArchHandler::create(normalizedFile.arch);
|
||||
for (auto § : normalizedFile.sections) {
|
||||
if (isDebugInfoSection(sect))
|
||||
continue;
|
||||
|
|
|
@ -10,92 +10,16 @@
|
|||
#ifndef LLD_READER_WRITER_MACHO_PASSES_H
|
||||
#define LLD_READER_WRITER_MACHO_PASSES_H
|
||||
|
||||
#include "lld/Core/Atom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Pass.h"
|
||||
#include "lld/Core/range.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
|
||||
#include <vector>
|
||||
#include "lld/Core/PassManager.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
namespace lld {
|
||||
class DefinedAtom;
|
||||
class MutableFile;
|
||||
namespace mach_o {
|
||||
|
||||
void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx);
|
||||
|
||||
/// Pass for adding stubs (PLT entries) for calls to functions
|
||||
/// outside the linkage unit. This class is subclassed by each
|
||||
/// file format Writer which implements the pure virtual methods.
|
||||
class StubsPass : public Pass {
|
||||
public:
|
||||
StubsPass() : Pass() {}
|
||||
|
||||
/// Scans all Atoms looking for call-site uses of SharedLibraryAtoms
|
||||
/// and transfroms the call-site to call a stub instead using the
|
||||
/// helper methods below.
|
||||
void perform(std::unique_ptr<MutableFile> &mergedFile) override;
|
||||
|
||||
/// If true, the pass should use stubs for references
|
||||
/// to shared library symbols. If false, the pass
|
||||
/// will generate relocations on the text segment which the
|
||||
/// runtime loader will use to patch the program at runtime.
|
||||
virtual bool noTextRelocs() = 0;
|
||||
|
||||
/// Returns whether the Reference kind is for a call site. The pass
|
||||
/// uses this to find calls that need to be indirected through a stub.
|
||||
virtual bool isCallSite(const Reference &) = 0;
|
||||
|
||||
/// Returns a file format specific atom for a stub/PLT entry which contains
|
||||
/// instructions which jump to the specified atom. May be called multiple
|
||||
/// times for the same target atom, in which case this method should return
|
||||
/// the same stub atom.
|
||||
virtual const DefinedAtom *getStub(const Atom &target) = 0;
|
||||
|
||||
/// After the default implementation of perform() is done calling getStub(),
|
||||
/// it will call this method to add all the stub (and support) atoms to the
|
||||
/// master file object.
|
||||
virtual void addStubAtoms(MutableFile &masterFile) = 0;
|
||||
|
||||
private:
|
||||
void replaceCalleeWithStub(const Atom *target, const Reference *ref);
|
||||
};
|
||||
|
||||
/// Pass for adding GOT entries for pointers to functions/data
|
||||
/// outside the linkage unit. This class is subclassed by each
|
||||
/// file format Writer which implements the pure virtual methods.
|
||||
class GOTPass : public Pass {
|
||||
public:
|
||||
GOTPass() : Pass() {}
|
||||
|
||||
/// Scans all Atoms looking for pointer to SharedLibraryAtoms
|
||||
/// and transfroms them to a pointer to a GOT entry using the
|
||||
/// helper methods below.
|
||||
void perform(std::unique_ptr<MutableFile> &mergedFile) override;
|
||||
|
||||
/// If true, the pass will use GOT entries for references
|
||||
/// to shared library symbols. If false, the pass
|
||||
/// will generate relocations on the text segment which the
|
||||
/// runtime loader will use to patch the program at runtime.
|
||||
virtual bool noTextRelocs() = 0;
|
||||
|
||||
/// Returns whether the Reference kind is a pre-instantiated GOT access.
|
||||
/// The default implementation of perform() uses this to figure out
|
||||
/// what GOT entries to instantiate.
|
||||
virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) = 0;
|
||||
|
||||
/// The file format Writer needs to alter the reference kind from a
|
||||
/// pre-instantiated GOT access to an actual access. If targetIsNowGOT is
|
||||
/// true, the pass has instantiated a GOT atom and altered the reference's
|
||||
/// target to point to that atom. If targetIsNowGOT is false, the pass
|
||||
/// determined a GOT entry is not needed because the reference site can
|
||||
/// directly access the target.
|
||||
virtual void updateReferenceToGOT(const Reference*, bool targetIsNowGOT) = 0;
|
||||
|
||||
/// Returns a file format specific atom for a GOT entry targeting
|
||||
/// the specified atom.
|
||||
virtual const DefinedAtom *makeGOTEntry(const Atom &target) = 0;
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_PASSES_H
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,334 +0,0 @@
|
|||
//===- lib/FileFormat/MachO/ReferenceKinds.h ------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#include "MachONormalizedFile.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
#include "llvm/ADT/Triple.h"
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_REFERENCE_KINDS_H
|
||||
#define LLD_READER_WRITER_MACHO_REFERENCE_KINDS_H
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
|
||||
///
|
||||
/// The KindHandler class is the abstract interface to Reference::Kind
|
||||
/// values for mach-o files. Particular Kind values (e.g. 3) has a different
|
||||
/// meaning for each architecture.
|
||||
///
|
||||
class KindHandler {
|
||||
public:
|
||||
|
||||
static std::unique_ptr<mach_o::KindHandler> create(MachOLinkingContext::Arch);
|
||||
virtual ~KindHandler();
|
||||
|
||||
virtual bool isCallSite(const Reference &) = 0;
|
||||
virtual bool isPointer(const Reference &) = 0;
|
||||
virtual bool isLazyImmediate(const Reference &) = 0;
|
||||
virtual bool isLazyTarget(const Reference &) = 0;
|
||||
|
||||
/// Returns true if the specified relocation is paired to the next relocation.
|
||||
virtual bool isPairedReloc(const normalized::Relocation &) = 0;
|
||||
|
||||
/// Prototype for a helper function. Given a sectionIndex and address,
|
||||
/// finds the atom and offset with that atom of that address.
|
||||
typedef std::function<std::error_code (uint32_t sectionIndex, uint64_t addr,
|
||||
const lld::Atom **, Reference::Addend *)>
|
||||
FindAtomBySectionAndAddress;
|
||||
|
||||
/// Prototype for a helper function. Given a symbolIndex, finds the atom
|
||||
/// representing that symbol.
|
||||
typedef std::function<std::error_code (uint32_t symbolIndex,
|
||||
const lld::Atom **)> FindAtomBySymbolIndex;
|
||||
|
||||
/// Analyzes a relocation from a .o file and returns the info
|
||||
/// (kind, target, addend) needed to instantiate a Reference.
|
||||
/// Two helper functions are passed as parameters to find the target atom
|
||||
/// given a symbol index or address.
|
||||
virtual 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) = 0;
|
||||
|
||||
/// Analyzes a pair of relocations from a .o file and returns the info
|
||||
/// (kind, target, addend) needed to instantiate a Reference.
|
||||
/// Two helper functions are passed as parameters to find the target atom
|
||||
/// given a symbol index or address.
|
||||
virtual 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) = 0;
|
||||
|
||||
/// Fixup an atom when generating a final linked binary.
|
||||
virtual void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress) = 0;
|
||||
|
||||
protected:
|
||||
KindHandler();
|
||||
|
||||
// Handy way to pack mach-o r_type and other bit fields into one 16-bit value.
|
||||
typedef uint16_t RelocPattern;
|
||||
enum {
|
||||
rScattered = 0x8000,
|
||||
rPcRel = 0x4000,
|
||||
rExtern = 0x2000,
|
||||
rLength1 = 0x0000,
|
||||
rLength2 = 0x0100,
|
||||
rLength4 = 0x0200,
|
||||
rLength8 = 0x0300
|
||||
};
|
||||
static RelocPattern relocPattern(const normalized::Relocation &reloc);
|
||||
|
||||
static int16_t readS16(bool swap, const uint8_t *addr);
|
||||
static int32_t readS32(bool swap, const uint8_t *addr);
|
||||
static uint32_t readU32(bool swap, const uint8_t *addr);
|
||||
static int64_t readS64(bool swap, const uint8_t *addr);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class KindHandler_x86_64 : public KindHandler {
|
||||
public:
|
||||
static const Registry::KindStrings kindStrings[];
|
||||
|
||||
virtual ~KindHandler_x86_64();
|
||||
bool isCallSite(const Reference &) override;
|
||||
bool isPointer(const Reference &) override;
|
||||
bool isLazyImmediate(const Reference &) override;
|
||||
bool isLazyTarget(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 void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress)
|
||||
override;
|
||||
|
||||
private:
|
||||
friend class X86_64LazyPointerAtom;
|
||||
friend class X86_64StubHelperAtom;
|
||||
friend class X86_64StubAtom;
|
||||
friend class X86_64StubHelperCommonAtom;
|
||||
friend class X86_64NonLazyPointerAtom;
|
||||
|
||||
enum : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// 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
|
||||
ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax
|
||||
ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip)
|
||||
pointer64, /// ex: .quad _foo
|
||||
pointer64Anon, /// ex: .quad L1
|
||||
delta64, /// ex: .quad _foo - .
|
||||
delta32, /// ex: .long _foo - .
|
||||
delta64Anon, /// ex: .quad L1 - .
|
||||
delta32Anon, /// ex: .long L1 - .
|
||||
|
||||
// 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.
|
||||
};
|
||||
|
||||
Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
|
||||
Reference::KindValue kindFromRelocPair(const normalized::Relocation &reloc1,
|
||||
const normalized::Relocation &reloc2);
|
||||
|
||||
|
||||
};
|
||||
|
||||
class KindHandler_x86 : public KindHandler {
|
||||
public:
|
||||
static const Registry::KindStrings kindStrings[];
|
||||
|
||||
virtual ~KindHandler_x86();
|
||||
bool isCallSite(const Reference &) override;
|
||||
bool isPointer(const Reference &) override;
|
||||
bool isLazyImmediate(const Reference &) override;
|
||||
bool isLazyTarget(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;
|
||||
|
||||
void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress)
|
||||
override;
|
||||
|
||||
private:
|
||||
friend class X86LazyPointerAtom;
|
||||
friend class X86StubHelperAtom;
|
||||
friend class X86StubAtom;
|
||||
friend class X86StubHelperCommonAtom;
|
||||
friend class X86NonLazyPointerAtom;
|
||||
|
||||
enum : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
branch32, /// ex: call _foo
|
||||
branch16, /// ex: callw _foo
|
||||
abs32, /// ex: movl _foo, %eax
|
||||
funcRel32, /// ex: movl _foo-L1(%eax), %eax
|
||||
pointer32, /// ex: .long _foo
|
||||
delta32, /// ex: .long _foo - .
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class KindHandler_arm : public KindHandler {
|
||||
public:
|
||||
static const Registry::KindStrings kindStrings[];
|
||||
|
||||
virtual ~KindHandler_arm();
|
||||
bool isCallSite(const Reference &) override;
|
||||
bool isPointer(const Reference &) override;
|
||||
bool isLazyImmediate(const Reference &) override;
|
||||
bool isLazyTarget(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;
|
||||
|
||||
void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
|
||||
Reference::KindValue kindValue, uint64_t addend,
|
||||
uint8_t *location, uint64_t fixupAddress,
|
||||
uint64_t targetAddress, uint64_t inAtomAddress)
|
||||
override;
|
||||
|
||||
private:
|
||||
enum : Reference::KindValue {
|
||||
invalid, /// for error condition
|
||||
|
||||
// Kinds found in mach-o .o files:
|
||||
thumb_b22, /// ex: bl _foo
|
||||
thumb_movw, /// ex: movw r1, :lower16:_foo
|
||||
thumb_movt, /// ex: movt r1, :lower16:_foo
|
||||
thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
|
||||
thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
|
||||
arm_b24, /// ex: bl _foo
|
||||
arm_movw, /// ex: movw r1, :lower16:_foo
|
||||
arm_movt, /// ex: movt r1, :lower16:_foo
|
||||
arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
|
||||
arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
|
||||
pointer32, /// ex: .long _foo
|
||||
delta32, /// ex: .long _foo - .
|
||||
|
||||
// Kinds introduced by Passes:
|
||||
lazyPointer, /// Location contains a lazy pointer.
|
||||
lazyImmediateLocation, /// Location contains immediate value used in stub.
|
||||
};
|
||||
|
||||
int32_t getDisplacementFromThumbBranch(uint32_t instruction);
|
||||
int32_t getDisplacementFromArmBranch(uint32_t instruction);
|
||||
uint16_t getWordFromThumbMov(uint32_t instruction);
|
||||
uint16_t getWordFromArmMov(uint32_t instruction);
|
||||
uint32_t clearThumbBit(uint32_t value, const Atom* target);
|
||||
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_REFERENCE_KINDS_H
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/StubAtoms.hpp -------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_STUB_ATOMS_H
|
||||
#define LLD_READER_WRITER_MACHO_STUB_ATOMS_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/SharedLibraryAtom.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
|
||||
#include "ReferenceKinds.h"
|
||||
#include "StubAtoms_x86_64.hpp"
|
||||
#include "StubAtoms_x86.hpp"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
|
||||
//
|
||||
// StubBinderAtom created by the stubs pass.
|
||||
//
|
||||
class StubBinderAtom : public SharedLibraryAtom {
|
||||
public:
|
||||
StubBinderAtom(const File &f) : _file(f) {
|
||||
}
|
||||
|
||||
const File& file() const override {
|
||||
return _file;
|
||||
}
|
||||
|
||||
StringRef name() const override {
|
||||
return StringRef("dyld_stub_binder");
|
||||
}
|
||||
|
||||
StringRef loadName() const override {
|
||||
return StringRef("/usr/lib/libSystem.B.dylib");
|
||||
}
|
||||
|
||||
bool canBeNullAtRuntime() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
Type type() const override {
|
||||
return Type::Unknown;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const File &_file;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_STUB_ATOMS_H
|
|
@ -1,215 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/StubAtoms_x86.hpp ---------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_STUB_ATOMS_X86_H
|
||||
#define LLD_READER_WRITER_MACHO_STUB_ATOMS_X86_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/SharedLibraryAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
|
||||
#include "ReferenceKinds.h"
|
||||
|
||||
using llvm::makeArrayRef;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// X86 Stub Atom created by the stubs pass.
|
||||
//
|
||||
class X86StubAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86StubAtom(const File &file, const Atom &lazyPointer)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::abs32, 2,
|
||||
&lazyPointer, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStub;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 6;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t instructions[] =
|
||||
{ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp *lazyPointer
|
||||
assert(sizeof(instructions) == this->size());
|
||||
return makeArrayRef(instructions);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// X86 Stub Helper Common Atom created by the stubs pass.
|
||||
//
|
||||
class X86StubHelperCommonAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86StubHelperCommonAtom(const File &file, const Atom &cache,
|
||||
const Atom &binder)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::abs32, 1,
|
||||
&cache, 0);
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::abs32, 7,
|
||||
&binder, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 12;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t instructions[] =
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache
|
||||
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
|
||||
0x90 }; // nop
|
||||
assert(sizeof(instructions) == this->size());
|
||||
return makeArrayRef(instructions);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// X86 Stub Helper Atom created by the stubs pass.
|
||||
//
|
||||
class X86StubHelperAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86StubHelperAtom(const File &file, const Atom &helperCommon)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86,
|
||||
KindHandler_x86::lazyImmediateLocation, 1, this, 0);
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::branch32, 6,
|
||||
&helperCommon, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 10;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t instructions[] =
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00 }; // jmp helperhelper
|
||||
assert(sizeof(instructions) == this->size());
|
||||
return makeArrayRef(instructions);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// X86 Lazy Pointer Atom created by the stubs pass.
|
||||
//
|
||||
class X86LazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86LazyPointerAtom(const File &file, const Atom &helper, const Atom &shlib)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::pointer32, 0,
|
||||
&helper, 0);
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::lazyPointer,
|
||||
0, &shlib, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeLazyPointer;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(2);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t bytes[] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
return makeArrayRef(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// X86 NonLazy (GOT) Pointer Atom created by the stubs pass.
|
||||
//
|
||||
class X86NonLazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86NonLazyPointerAtom(const File &file)
|
||||
: SimpleDefinedAtom(file) {}
|
||||
|
||||
X86NonLazyPointerAtom(const File &file, const Atom &shlib)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86, KindHandler_x86::pointer32, 0,
|
||||
&shlib, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeGOT;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(2);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t bytes[] = { 0x00, 0x00, 0x00, 0x0 };
|
||||
return makeArrayRef(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_STUB_ATOMS_X86_H
|
|
@ -1,218 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/StubAtoms_x86_64.hpp ------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_STUB_ATOMS_X86_64_H
|
||||
#define LLD_READER_WRITER_MACHO_STUB_ATOMS_X86_64_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/SharedLibraryAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
|
||||
#include "ReferenceKinds.h"
|
||||
|
||||
using llvm::makeArrayRef;
|
||||
using namespace llvm::MachO;
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
//
|
||||
// X86_64 Stub Atom created by the stubs pass.
|
||||
//
|
||||
class X86_64StubAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86_64StubAtom(const File &file, const Atom &lazyPointer)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::ripRel32, 2,
|
||||
&lazyPointer, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStub;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 6;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t instructions[] =
|
||||
{ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp *lazyPointer
|
||||
assert(sizeof(instructions) == this->size());
|
||||
return makeArrayRef(instructions);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// X86_64 Stub Helper Common Atom created by the stubs pass.
|
||||
//
|
||||
class X86_64StubHelperCommonAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86_64StubHelperCommonAtom(const File &file, const Atom &cache,
|
||||
const Atom &binder)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::ripRel32, 3, &cache, 0);
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::ripRel32, 11, &binder, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 16;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t instructions[] =
|
||||
{ 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
|
||||
assert(sizeof(instructions) == this->size());
|
||||
return makeArrayRef(instructions);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// X86_64 Stub Helper Atom created by the stubs pass.
|
||||
//
|
||||
class X86_64StubHelperAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86_64StubHelperAtom(const File &file, const Atom &helperCommon)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::lazyImmediateLocation, 1, this, 0);
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::ripRel32, 6,
|
||||
&helperCommon, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 10;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t instructions[] =
|
||||
{ 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00 }; // jmp helperhelper
|
||||
assert(sizeof(instructions) == this->size());
|
||||
return makeArrayRef(instructions);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// X86_64 Lazy Pointer Atom created by the stubs pass.
|
||||
//
|
||||
class X86_64LazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86_64LazyPointerAtom(const File &file, const Atom &helper,
|
||||
const Atom &shlib)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::pointer64, 0, &helper, 0);
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::lazyPointer, 0, &shlib, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeLazyPointer;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(3);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t bytes[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return makeArrayRef(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// X86_64 NonLazy (GOT) Pointer Atom created by the stubs pass.
|
||||
//
|
||||
class X86_64NonLazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
X86_64NonLazyPointerAtom(const File &file)
|
||||
: SimpleDefinedAtom(file) {}
|
||||
|
||||
X86_64NonLazyPointerAtom(const File &file, const Atom &shlib)
|
||||
: SimpleDefinedAtom(file) {
|
||||
this->addReference(Reference::KindNamespace::mach_o,
|
||||
Reference::KindArch::x86_64,
|
||||
KindHandler_x86_64::pointer64, 0, &shlib, 0);
|
||||
}
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeGOT;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(3);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t bytes[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return makeArrayRef(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_STUB_ATOMS_X86_64_H
|
|
@ -14,54 +14,371 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ArchHandler.h"
|
||||
#include "File.h"
|
||||
#include "MachOPasses.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
#include "lld/ReaderWriter/MachOLinkingContext.h"
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
#include "MachOPasses.h"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
void StubsPass::perform(std::unique_ptr<MutableFile> &mergedFile) {
|
||||
// Skip this pass if output format uses text relocations instead of stubs.
|
||||
if (!this->noTextRelocs())
|
||||
return;
|
||||
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile->defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at call-sites.
|
||||
if (!this->isCallSite(*ref))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
if (target->definition() == Atom::definitionSharedLibrary) {
|
||||
// Calls to shared libraries go through stubs.
|
||||
replaceCalleeWithStub(target, ref);
|
||||
continue;
|
||||
}
|
||||
const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
|
||||
if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo) {
|
||||
// Calls to interposable functions in same linkage unit must also go
|
||||
// through a stub.
|
||||
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
||||
replaceCalleeWithStub(target, ref);
|
||||
//
|
||||
// Lazy Pointer Atom created by the stubs pass.
|
||||
//
|
||||
class LazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
LazyPointerAtom(const File &file, bool is64)
|
||||
: SimpleDefinedAtom(file), _is64(is64) { }
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeLazyPointer;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(_is64 ? 3 : 2);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// NonLazyPointer (GOT) Atom created by the stubs pass.
|
||||
//
|
||||
class NonLazyPointerAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
NonLazyPointerAtom(const File &file, bool is64)
|
||||
: SimpleDefinedAtom(file), _is64(is64) { }
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeGOT;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(_is64 ? 3 : 2);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permRW_;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
static const uint8_t zeros[] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
return llvm::makeArrayRef(zeros, size());
|
||||
}
|
||||
|
||||
private:
|
||||
const bool _is64;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Stub Atom created by the stubs pass.
|
||||
//
|
||||
class StubAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
|
||||
: SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStub;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(_stubInfo.codeAlignment);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _stubInfo.stubSize;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(_stubInfo.stubBytes, _stubInfo.stubSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Stub Helper Atom created by the stubs pass.
|
||||
//
|
||||
class StubHelperAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
|
||||
: SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(_stubInfo.codeAlignment);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _stubInfo.stubHelperSize;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(_stubInfo.stubHelperBytes,
|
||||
_stubInfo.stubHelperSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Stub Helper Common Atom created by the stubs pass.
|
||||
//
|
||||
class StubHelperCommonAtom : public SimpleDefinedAtom {
|
||||
public:
|
||||
StubHelperCommonAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
|
||||
: SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
|
||||
|
||||
ContentType contentType() const override {
|
||||
return DefinedAtom::typeStubHelper;
|
||||
}
|
||||
|
||||
Alignment alignment() const override {
|
||||
return Alignment(_stubInfo.codeAlignment);
|
||||
}
|
||||
|
||||
uint64_t size() const override {
|
||||
return _stubInfo.stubHelperCommonSize;
|
||||
}
|
||||
|
||||
ContentPermissions permissions() const override {
|
||||
return DefinedAtom::permR_X;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> rawContent() const override {
|
||||
return llvm::makeArrayRef(_stubInfo.stubHelperCommonBytes,
|
||||
_stubInfo.stubHelperCommonSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
};
|
||||
|
||||
|
||||
class StubsPass : public Pass {
|
||||
public:
|
||||
StubsPass(const MachOLinkingContext &context)
|
||||
: _context(context)
|
||||
, _archHandler(_context.archHandler())
|
||||
, _stubInfo(_archHandler.stubInfo())
|
||||
, _file("<mach-o Stubs pass>")
|
||||
, _helperCommonAtom(nullptr)
|
||||
, _helperCacheNLPAtom(nullptr)
|
||||
, _helperBinderNLPAtom(nullptr) {
|
||||
}
|
||||
|
||||
|
||||
void perform(std::unique_ptr<MutableFile> &mergedFile) override {
|
||||
// Skip this pass if output format uses text relocations instead of stubs.
|
||||
if (!this->noTextRelocs())
|
||||
return;
|
||||
|
||||
// Scan all references in all atoms.
|
||||
for (const DefinedAtom *atom : mergedFile->defined()) {
|
||||
for (const Reference *ref : *atom) {
|
||||
// Look at call-sites.
|
||||
if (!this->isCallSite(*ref))
|
||||
continue;
|
||||
const Atom *target = ref->target();
|
||||
assert(target != nullptr);
|
||||
if (target->definition() == Atom::definitionSharedLibrary) {
|
||||
// Calls to shared libraries go through stubs.
|
||||
replaceCalleeWithStub(target, ref);
|
||||
continue;
|
||||
}
|
||||
const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
|
||||
if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){
|
||||
// Calls to interposable functions in same linkage unit must also go
|
||||
// through a stub.
|
||||
assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
|
||||
replaceCalleeWithStub(target, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Exit early if no stubs needed.
|
||||
if (_targetToStub.empty())
|
||||
return;
|
||||
|
||||
// Add reference to dyld_stub_binder in libSystem.dylib
|
||||
if (_helperBinderNLPAtom) {
|
||||
bool found = false;
|
||||
for (const SharedLibraryAtom *atom : mergedFile->sharedLibrary()) {
|
||||
if (atom->name().equals(_stubInfo.binderSymbolName)) {
|
||||
addReference(_helperBinderNLPAtom,
|
||||
_stubInfo.nonLazyPointerReferenceToBinder, atom);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found && "dyld_stub_binder not found");
|
||||
}
|
||||
|
||||
// Add all stubs to master file.
|
||||
for (auto it : _targetToStub) {
|
||||
mergedFile->addAtom(*it.second);
|
||||
}
|
||||
// Add helper code atoms.
|
||||
mergedFile->addAtom(*_helperCommonAtom);
|
||||
for (const DefinedAtom *lp : _stubHelperAtoms) {
|
||||
mergedFile->addAtom(*lp);
|
||||
}
|
||||
// Add GOT slots used for lazy binding.
|
||||
mergedFile->addAtom(*_helperBinderNLPAtom);
|
||||
mergedFile->addAtom(*_helperCacheNLPAtom);
|
||||
// Add all lazy pointers to master file.
|
||||
for (const DefinedAtom *lp : _lazyPointers) {
|
||||
mergedFile->addAtom(*lp);
|
||||
}
|
||||
}
|
||||
// Add all created stubs and support Atoms.
|
||||
this->addStubAtoms(*mergedFile);
|
||||
}
|
||||
|
||||
void StubsPass::replaceCalleeWithStub(const Atom *target,
|
||||
const Reference *ref) {
|
||||
// Make file-format specific stub and other support atoms.
|
||||
const DefinedAtom *stub = this->getStub(*target);
|
||||
assert(stub != nullptr);
|
||||
// Switch call site to reference stub atom instead.
|
||||
const_cast<Reference *>(ref)->setTarget(stub);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool noTextRelocs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isCallSite(const Reference &ref) {
|
||||
return _archHandler.isCallSite(ref);
|
||||
}
|
||||
|
||||
void replaceCalleeWithStub(const Atom *target, const Reference *ref) {
|
||||
// Make file-format specific stub and other support atoms.
|
||||
const DefinedAtom *stub = this->getStub(*target);
|
||||
assert(stub != nullptr);
|
||||
// Switch call site to reference stub atom instead.
|
||||
const_cast<Reference *>(ref)->setTarget(stub);
|
||||
}
|
||||
|
||||
const DefinedAtom* getStub(const Atom& target) {
|
||||
auto pos = _targetToStub.find(&target);
|
||||
if ( pos != _targetToStub.end() ) {
|
||||
// Reuse an existing stub.
|
||||
assert(pos->second != nullptr);
|
||||
return pos->second;
|
||||
}
|
||||
else {
|
||||
// There is no existing stub, so create a new one.
|
||||
return this->makeStub(target);
|
||||
}
|
||||
}
|
||||
|
||||
const DefinedAtom* makeStub(const Atom &target) {
|
||||
SimpleDefinedAtom* stub = new (_file.allocator())
|
||||
StubAtom(_file, _stubInfo);
|
||||
SimpleDefinedAtom* lp = new (_file.allocator())
|
||||
LazyPointerAtom(_file, _context.is64Bit());
|
||||
SimpleDefinedAtom* helper = new (_file.allocator())
|
||||
StubHelperAtom(_file, _stubInfo);
|
||||
|
||||
addReference(stub, _stubInfo.stubReferenceToLP, lp);
|
||||
addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper);
|
||||
addReference(lp, _stubInfo.lazyPointerReferenceToFinal, &target);
|
||||
addReference(helper, _stubInfo.stubHelperReferenceToImm, helper);
|
||||
addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon,
|
||||
helperCommon());
|
||||
|
||||
_stubHelperAtoms.push_back(helper);
|
||||
_targetToStub[&target] = stub;
|
||||
_lazyPointers.push_back(lp);
|
||||
|
||||
return stub;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const DefinedAtom* helperCommon() {
|
||||
if ( !_helperCommonAtom ) {
|
||||
// Lazily create common helper code and data.
|
||||
_helperCommonAtom = new (_file.allocator())
|
||||
StubHelperCommonAtom(_file, _stubInfo);
|
||||
_helperCacheNLPAtom = new (_file.allocator())
|
||||
NonLazyPointerAtom(_file, _context.is64Bit());
|
||||
_helperBinderNLPAtom = new (_file.allocator())
|
||||
NonLazyPointerAtom(_file, _context.is64Bit());
|
||||
addReference(_helperCommonAtom,
|
||||
_stubInfo.stubHelperCommonReferenceToCache,
|
||||
_helperCacheNLPAtom);
|
||||
addReference(_helperCommonAtom,
|
||||
_stubInfo.stubHelperCommonReferenceToBinder,
|
||||
_helperBinderNLPAtom);
|
||||
}
|
||||
return _helperCommonAtom;
|
||||
}
|
||||
|
||||
|
||||
const MachOLinkingContext &_context;
|
||||
mach_o::ArchHandler &_archHandler;
|
||||
const ArchHandler::StubInfo &_stubInfo;
|
||||
MachOFile _file;
|
||||
llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToStub;
|
||||
std::vector<const DefinedAtom*> _lazyPointers;
|
||||
std::vector<const DefinedAtom*> _stubHelperAtoms;
|
||||
SimpleDefinedAtom *_helperCommonAtom;
|
||||
SimpleDefinedAtom *_helperCacheNLPAtom;
|
||||
SimpleDefinedAtom *_helperBinderNLPAtom;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
pm.add(std::unique_ptr<Pass>(new StubsPass(ctx)));
|
||||
}
|
||||
|
||||
} // end namespace mach_o
|
||||
} // end namespace lld
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
//===- lib/ReaderWriter/MachO/StubsPass.hpp -------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_READER_WRITER_MACHO_STUBS_PASS_H
|
||||
#define LLD_READER_WRITER_MACHO_STUBS_PASS_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
#include "lld/Core/DefinedAtom.h"
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/Pass.h"
|
||||
#include "lld/Core/Reference.h"
|
||||
#include "lld/Core/SharedLibraryAtom.h"
|
||||
#include "lld/Core/Simple.h"
|
||||
|
||||
#include "MachOPasses.h"
|
||||
#include "ReferenceKinds.h"
|
||||
#include "StubAtoms.hpp"
|
||||
|
||||
namespace lld {
|
||||
namespace mach_o {
|
||||
|
||||
|
||||
class StubsPass : public lld::StubsPass {
|
||||
public:
|
||||
StubsPass(const MachOLinkingContext &context)
|
||||
: _context(context)
|
||||
, _kindHandler(_context.kindHandler())
|
||||
, _file(context)
|
||||
, _helperCommonAtom(nullptr)
|
||||
, _helperCacheAtom(nullptr)
|
||||
, _helperBinderAtom(nullptr) {
|
||||
}
|
||||
|
||||
bool noTextRelocs() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isCallSite(const Reference &ref) override {
|
||||
return _kindHandler.isCallSite(ref);
|
||||
}
|
||||
|
||||
const DefinedAtom* getStub(const Atom& target) override {
|
||||
auto pos = _targetToStub.find(&target);
|
||||
if ( pos != _targetToStub.end() ) {
|
||||
// Reuse an existing stub.
|
||||
assert(pos->second != nullptr);
|
||||
return pos->second;
|
||||
}
|
||||
else {
|
||||
// There is no existing stub, so create a new one.
|
||||
return this->makeStub(target);
|
||||
}
|
||||
}
|
||||
|
||||
const DefinedAtom* makeStub(const Atom& target) {
|
||||
switch (_context.arch()) {
|
||||
case MachOLinkingContext::arch_x86_64:
|
||||
return makeStub_x86_64(target);
|
||||
case MachOLinkingContext::arch_x86:
|
||||
return makeStub_x86(target);
|
||||
case MachOLinkingContext::arch_armv6:
|
||||
case MachOLinkingContext::arch_armv7:
|
||||
case MachOLinkingContext::arch_armv7s:
|
||||
return makeStub_arm(target);
|
||||
default:
|
||||
llvm_unreachable("Unknown mach-o arch");
|
||||
}
|
||||
}
|
||||
|
||||
const DefinedAtom* makeStub_x86_64(const Atom& target) {
|
||||
if ( _helperCommonAtom == nullptr ) {
|
||||
// Lazily create common helper code and data.
|
||||
_helperCacheAtom = new X86_64NonLazyPointerAtom(_file);
|
||||
_binderAtom = new StubBinderAtom(_file);
|
||||
_helperBinderAtom = new X86_64NonLazyPointerAtom(_file, *_binderAtom);
|
||||
_helperCommonAtom = new X86_64StubHelperCommonAtom(_file,
|
||||
*_helperCacheAtom, *_helperBinderAtom);
|
||||
}
|
||||
const DefinedAtom* helper = new X86_64StubHelperAtom(_file,
|
||||
*_helperCommonAtom);
|
||||
_stubHelperAtoms.push_back(helper);
|
||||
const DefinedAtom* lp = new X86_64LazyPointerAtom(_file, *helper, target);
|
||||
assert(lp->contentType() == DefinedAtom::typeLazyPointer);
|
||||
_lazyPointers.push_back(lp);
|
||||
const DefinedAtom* stub = new X86_64StubAtom(_file, *lp);
|
||||
assert(stub->contentType() == DefinedAtom::typeStub);
|
||||
_targetToStub[&target] = stub;
|
||||
return stub;
|
||||
}
|
||||
|
||||
const DefinedAtom* makeStub_x86(const Atom& target) {
|
||||
if ( _helperCommonAtom == nullptr ) {
|
||||
// Lazily create common helper code and data.
|
||||
_helperCacheAtom = new X86NonLazyPointerAtom(_file);
|
||||
_binderAtom = new StubBinderAtom(_file);
|
||||
_helperBinderAtom = new X86NonLazyPointerAtom(_file, *_binderAtom);
|
||||
_helperCommonAtom = new X86StubHelperCommonAtom(_file,
|
||||
*_helperCacheAtom, *_helperBinderAtom);
|
||||
}
|
||||
const DefinedAtom* helper = new X86StubHelperAtom(_file,
|
||||
*_helperCommonAtom);
|
||||
_stubHelperAtoms.push_back(helper);
|
||||
const DefinedAtom* lp = new X86LazyPointerAtom(_file, *helper, target);
|
||||
assert(lp->contentType() == DefinedAtom::typeLazyPointer);
|
||||
_lazyPointers.push_back(lp);
|
||||
const DefinedAtom* stub = new X86StubAtom(_file, *lp);
|
||||
assert(stub->contentType() == DefinedAtom::typeStub);
|
||||
_targetToStub[&target] = stub;
|
||||
return stub;
|
||||
}
|
||||
|
||||
const DefinedAtom* makeStub_arm(const Atom& target) {
|
||||
assert(0 && "stubs not yet implemented for arm");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void addStubAtoms(MutableFile &mergedFile) override {
|
||||
// Exit early if no stubs needed.
|
||||
if (_targetToStub.empty())
|
||||
return;
|
||||
// Add all stubs to master file.
|
||||
for (auto it : _targetToStub) {
|
||||
mergedFile.addAtom(*it.second);
|
||||
}
|
||||
// Add helper code atoms.
|
||||
mergedFile.addAtom(*_helperCommonAtom);
|
||||
for (const DefinedAtom *lp : _stubHelperAtoms) {
|
||||
mergedFile.addAtom(*lp);
|
||||
}
|
||||
// Add GOT slots used for lazy binding.
|
||||
mergedFile.addAtom(*_helperBinderAtom);
|
||||
mergedFile.addAtom(*_helperCacheAtom);
|
||||
// Add all lazy pointers to master file.
|
||||
for (const DefinedAtom *lp : _lazyPointers) {
|
||||
mergedFile.addAtom(*lp);
|
||||
}
|
||||
// Add sharedlibrary atom
|
||||
mergedFile.addAtom(*_binderAtom);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class File : public SimpleFile {
|
||||
public:
|
||||
File(const MachOLinkingContext &context) : SimpleFile("MachO Stubs pass") {}
|
||||
};
|
||||
|
||||
const MachOLinkingContext &_context;
|
||||
mach_o::KindHandler &_kindHandler;
|
||||
File _file;
|
||||
llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToStub;
|
||||
std::vector<const DefinedAtom*> _lazyPointers;
|
||||
std::vector<const DefinedAtom*> _stubHelperAtoms;
|
||||
const SharedLibraryAtom *_binderAtom;
|
||||
const DefinedAtom* _helperCommonAtom;
|
||||
const DefinedAtom* _helperCacheAtom;
|
||||
const DefinedAtom* _helperBinderAtom;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mach_o
|
||||
} // namespace lld
|
||||
|
||||
|
||||
#endif // LLD_READER_WRITER_MACHO_STUBS_PASS_H
|
|
@ -1,36 +0,0 @@
|
|||
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t && \
|
||||
# RUN: llvm-nm %t | FileCheck %s
|
||||
#
|
||||
# Test that hello-world can be linked into a mach-o executable
|
||||
#
|
||||
|
||||
--- !native
|
||||
defined-atoms:
|
||||
- name: _main
|
||||
type: code
|
||||
scope: global
|
||||
content: [ 55, 48, 89, E5, 48, 8D, 3D, 00,
|
||||
00, 00, 00, E8, 00, 00, 00, 00,
|
||||
31, C0, 5D, C3 ]
|
||||
references:
|
||||
- offset: 7
|
||||
kind: ripRel32
|
||||
target: LC1
|
||||
- offset: 12
|
||||
kind: branch32
|
||||
target: _printf
|
||||
|
||||
- ref-name: LC1
|
||||
type: c-string
|
||||
merge: by-content
|
||||
content: [ 68, 65, 6C, 6C, 6F, 0A, 00 ]
|
||||
|
||||
shared-library-atoms:
|
||||
- name: _printf
|
||||
load-name: /usr/lib/libSystem.B.dylib
|
||||
|
||||
...
|
||||
|
||||
# CHECK: {{[0-9a-f]+}} T _main
|
||||
# CHECK: U _printf
|
||||
# CHECK: U dyld_stub_binder
|
|
@ -38,6 +38,27 @@ global-symbols:
|
|||
desc: [ ]
|
||||
value: 0
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
|
||||
|
||||
...
|
||||
|
||||
# CHECK: {{[0-9a-f]+}} T _foo
|
||||
|
|
|
@ -25,6 +25,28 @@ defined-atoms:
|
|||
type: data
|
||||
content: [ 01 ]
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
|
||||
|
||||
|
||||
# CHECK-LABEL: Section {
|
||||
# CHECK: Name: __text
|
||||
# CHECK: Segment: __TEXT
|
||||
|
|
|
@ -19,6 +19,26 @@ defined-atoms:
|
|||
section-name: __CUST/__custom
|
||||
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
|
||||
# CHECK-LABEL: Section {
|
||||
# CHECK: Name: __text
|
||||
# CHECK: Segment: __TEXT
|
||||
|
|
|
@ -75,6 +75,11 @@ global-symbols:
|
|||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000001
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000001
|
||||
|
||||
...
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t && \
|
||||
# RUN: llvm-nm %t | FileCheck %s
|
||||
#
|
||||
# Test that x86_64 hello-world can be linked into a mach-o executable
|
||||
#
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_OBJECT
|
||||
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
|
||||
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: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x05, 0x00,
|
||||
0x00, 0x00, 0x00, 0x48, 0x8B, 0x38, 0x48, 0x8D,
|
||||
0x35, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0x5D, 0xC3 ]
|
||||
relocations:
|
||||
- offset: 0x00000018
|
||||
type: X86_64_RELOC_BRANCH
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 5
|
||||
- offset: 0x00000011
|
||||
type: X86_64_RELOC_SIGNED
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 0
|
||||
- offset: 0x00000007
|
||||
type: X86_64_RELOC_GOT_LOAD
|
||||
length: 2
|
||||
pc-rel: true
|
||||
extern: true
|
||||
symbol: 4
|
||||
- segment: __TEXT
|
||||
section: __cstring
|
||||
type: S_CSTRING_LITERALS
|
||||
attributes: [ ]
|
||||
address: 0x0000000000000020
|
||||
content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
|
||||
- segment: __LD
|
||||
section: __compact_unwind
|
||||
type: S_REGULAR
|
||||
attributes: [ ]
|
||||
alignment: 3
|
||||
address: 0x0000000000000028
|
||||
content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
|
||||
relocations:
|
||||
- offset: 0x00000000
|
||||
type: X86_64_RELOC_UNSIGNED
|
||||
length: 3
|
||||
pc-rel: false
|
||||
extern: false
|
||||
symbol: 1
|
||||
- segment: __TEXT
|
||||
section: __eh_frame
|
||||
type: S_COALESCED
|
||||
attributes: [ ]
|
||||
alignment: 3
|
||||
address: 0x0000000000000048
|
||||
content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01,
|
||||
0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
|
||||
0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
|
||||
0x98, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
|
||||
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
|
||||
local-symbols:
|
||||
- name: L1
|
||||
type: N_SECT
|
||||
sect: 2
|
||||
value: 0x0000000000000020
|
||||
- name: EH_frame0
|
||||
type: N_SECT
|
||||
sect: 4
|
||||
value: 0x0000000000000048
|
||||
global-symbols:
|
||||
- name: _main
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
- name: _main.eh
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 4
|
||||
value: 0x0000000000000060
|
||||
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: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: _fprintf
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
- name: ___stdoutp
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
|
||||
...
|
||||
|
||||
# CHECK: U ___stdoutp
|
||||
# CHECK: U _fprintf
|
||||
# CHECK: {{[0-9a-f]+}} T _main
|
||||
# CHECK: U dyld_stub_binder
|
|
@ -151,3 +151,25 @@ defined-atoms:
|
|||
# CHECK: SectionData (
|
||||
# CHECK-NEXT: 0000: 0E000000 00000000
|
||||
# CHECK-NEXT: )
|
||||
|
||||
|
||||
--- !mach-o
|
||||
arch: x86_64
|
||||
file-type: MH_DYLIB
|
||||
flags: [ ]
|
||||
install-name: /usr/lib/libSystem.B.dylib
|
||||
sections:
|
||||
- segment: __TEXT
|
||||
section: __text
|
||||
type: S_REGULAR
|
||||
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
|
||||
address: 0x0000000000000000
|
||||
content: [ 0x55 ]
|
||||
|
||||
global-symbols:
|
||||
- name: dyld_stub_binder
|
||||
type: N_SECT
|
||||
scope: [ N_EXT ]
|
||||
sect: 1
|
||||
value: 0x0000000000000000
|
||||
|
||||
|
|
Loading…
Reference in New Issue