llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

644 lines
24 KiB
C++

//===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ArchHandler.h"
#include "Atoms.h"
#include "MachONormalizedFileBinaryUtils.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm::MachO;
using namespace lld::mach_o::normalized;
namespace lld {
namespace mach_o {
using llvm::support::ulittle16_t;
using llvm::support::ulittle32_t;
using llvm::support::little16_t;
using llvm::support::little32_t;
class ArchHandler_x86 : public ArchHandler {
public:
ArchHandler_x86() = default;
~ArchHandler_x86() override = default;
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 isNonCallBranch(const Reference &) override {
return false;
}
bool isPointer(const Reference &) override;
bool isPairedReloc(const normalized::Relocation &) override;
bool needsCompactUnwind() override {
return false;
}
Reference::KindValue imageOffsetKind() override {
return invalid;
}
Reference::KindValue imageOffsetKindIndirect() override {
return invalid;
}
Reference::KindValue unwindRefToPersonalityFunctionKind() override {
return invalid;
}
Reference::KindValue unwindRefToCIEKind() override {
return negDelta32;
}
Reference::KindValue unwindRefToFunctionKind() override{
return delta32;
}
Reference::KindValue lazyImmediateLocationKind() override {
return lazyImmediateLocation;
}
Reference::KindValue unwindRefToEhFrameKind() override {
return invalid;
}
Reference::KindValue pointerKind() override {
return invalid;
}
uint32_t dwarfCompactUnwindType() override {
return 0x04000000U;
}
llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
const DefinedAtom *inAtom,
uint32_t offsetInAtom,
uint64_t fixupAddress, bool swap,
FindAtomBySectionAndAddress atomFromAddress,
FindAtomBySymbolIndex atomFromSymbolIndex,
Reference::KindValue *kind,
const lld::Atom **target,
Reference::Addend *addend) override;
llvm::Error
getPairReferenceInfo(const normalized::Relocation &reloc1,
const normalized::Relocation &reloc2,
const DefinedAtom *inAtom,
uint32_t offsetInAtom,
uint64_t fixupAddress, bool swap, bool scatterable,
FindAtomBySectionAndAddress atomFromAddress,
FindAtomBySymbolIndex atomFromSymbolIndex,
Reference::KindValue *kind,
const lld::Atom **target,
Reference::Addend *addend) override;
void generateAtomContent(const DefinedAtom &atom, bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
void appendSectionRelocations(const DefinedAtom &atom,
uint64_t atomSectionOffset,
const Reference &ref,
FindSymbolIndexForAtom symbolIndexForAtom,
FindSectionIndexForAtom sectionIndexForAtom,
FindAddressForAtom addressForAtom,
normalized::Relocations &relocs) override;
bool isDataInCodeTransition(Reference::KindValue refKind) override {
return refKind == modeCode || refKind == modeData;
}
Reference::KindValue dataInCodeTransitionStart(
const MachODefinedAtom &atom) override {
return modeData;
}
Reference::KindValue dataInCodeTransitionEnd(
const MachODefinedAtom &atom) override {
return modeCode;
}
private:
static const Registry::KindStrings _sKindStrings[];
static const StubInfo _sStubInfo;
enum X86Kind : Reference::KindValue {
invalid, /// for error condition
modeCode, /// Content starting at this offset is code.
modeData, /// Content starting at this offset is data.
// Kinds found in mach-o .o files:
branch32, /// ex: call _foo
branch16, /// ex: callw _foo
abs32, /// ex: movl _foo, %eax
funcRel32, /// ex: movl _foo-L1(%eax), %eax
pointer32, /// ex: .long _foo
delta32, /// ex: .long _foo - .
negDelta32, /// ex: .long . - _foo
// Kinds introduced by Passes:
lazyPointer, /// Location contains a lazy pointer.
lazyImmediateLocation, /// Location contains immediate value used in stub.
};
static bool useExternalRelocationTo(const Atom &target);
void applyFixupFinal(const Reference &ref, uint8_t *location,
uint64_t fixupAddress, uint64_t targetAddress,
uint64_t inAtomAddress);
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress);
};
//===----------------------------------------------------------------------===//
// ArchHandler_x86
//===----------------------------------------------------------------------===//
const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = {
LLD_KIND_STRING_ENTRY(invalid),
LLD_KIND_STRING_ENTRY(modeCode),
LLD_KIND_STRING_ENTRY(modeData),
LLD_KIND_STRING_ENTRY(branch32),
LLD_KIND_STRING_ENTRY(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(negDelta32),
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 },
{ false, 0, 0, 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 image cache content type
DefinedAtom::typeNonLazyPointer,
// Stub Helper-Common size and code
12,
// Stub helper alignment
2,
{ 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 },
{ false, 0, 0, 0 },
{ Reference::KindArch::x86, abs32, 7, 0 },
{ false, 0, 0, 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);
}
llvm::Error
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) {
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 (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent;
break;
case GENERIC_RELOC_VANILLA | rPcRel | rLength4:
// ex: call _foo (and _foo defined)
*kind = branch32;
targetAddress =
fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
break;
case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4:
// ex: call _foo+n (and _foo defined)
*kind = branch32;
targetAddress =
fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
return ec;
*addend = targetAddress - reloc.value;
break;
case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2:
// ex: callw _foo (and _foo undefined)
*kind = branch16;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent;
break;
case GENERIC_RELOC_VANILLA | rPcRel | rLength2:
// ex: callw _foo (and _foo defined)
*kind = branch16;
targetAddress =
fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
break;
case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2:
// ex: callw _foo+n (and _foo defined)
*kind = branch16;
targetAddress =
fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
return ec;
*addend = targetAddress - reloc.value;
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 (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = *(const ulittle32_t *)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 = *(const ulittle32_t *)fixupContent;
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
break;
case GENERIC_RELOC_VANILLA | rScattered | rLength4:
// ex: .long _foo+n (and _foo defined)
perms = inAtom->permissions();
*kind =
((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
: pointer32;
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
return ec;
*addend = *(const ulittle32_t *)fixupContent - reloc.value;
break;
default:
return llvm::make_error<GenericError>("unsupported i386 relocation type");
}
return llvm::Error::success();
}
llvm::Error
ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
const normalized::Relocation &reloc2,
const DefinedAtom *inAtom,
uint32_t offsetInAtom,
uint64_t fixupAddress, bool swap,
bool scatterable,
FindAtomBySectionAndAddress atomFromAddr,
FindAtomBySymbolIndex atomFromSymbolIndex,
Reference::KindValue *kind,
const lld::Atom **target,
Reference::Addend *addend) {
const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
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 = *(const little32_t *)fixupContent;
if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
return ec;
if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
return ec;
if (fromTarget != inAtom) {
if (*target != inAtom)
return llvm::make_error<GenericError>(
"SECTDIFF relocation where neither target is in atom");
*kind = negDelta32;
*addend = toAddress - value - fromAddress;
*target = fromTarget;
} else {
if ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) {
// 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.
*kind = funcRel32;
uint32_t ta = fromAddress + value - toAddress;
*addend = ta - offsetInFrom;
} else {
*kind = delta32;
*addend = fromAddress + value - toAddress;
}
}
return llvm::Error::success();
break;
default:
return llvm::make_error<GenericError>("unsupported i386 relocation type");
}
}
void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom,
bool relocatable,
FindAddressForAtom findAddress,
FindAddressForAtom findSectionAddress,
uint64_t imageBaseAddress,
llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
// Copy raw bytes.
std::copy(atom.rawContent().begin(), atom.rawContent().end(),
atomContentBuffer.begin());
// Apply fix-ups.
for (const Reference *ref : atom) {
uint32_t offset = ref->offsetInAtom();
const Atom *target = ref->target();
uint64_t targetAddress = 0;
if (isa<DefinedAtom>(target))
targetAddress = findAddress(*target);
uint64_t atomAddress = findAddress(atom);
uint64_t fixupAddress = atomAddress + offset;
if (relocatable) {
applyFixupRelocatable(*ref, &atomContentBuffer[offset],
fixupAddress, targetAddress,
atomAddress);
} else {
applyFixupFinal(*ref, &atomContentBuffer[offset],
fixupAddress, targetAddress,
atomAddress);
}
}
}
void ArchHandler_x86::applyFixupFinal(const Reference &ref, uint8_t *loc,
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::x86);
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
switch (static_cast<X86Kind>(ref.kindValue())) {
case branch32:
*loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
break;
case branch16:
*loc32 = (targetAddress - (fixupAddress + 2)) + ref.addend();
break;
case pointer32:
case abs32:
*loc32 = targetAddress + ref.addend();
break;
case funcRel32:
*loc32 = targetAddress - inAtomAddress + ref.addend();
break;
case delta32:
*loc32 = targetAddress - fixupAddress + ref.addend();
break;
case negDelta32:
*loc32 = fixupAddress - targetAddress + ref.addend();
break;
case modeCode:
case modeData:
case lazyPointer:
// do nothing
break;
case lazyImmediateLocation:
*loc32 = ref.addend();
break;
case invalid:
llvm_unreachable("invalid x86 Reference Kind");
break;
}
}
void ArchHandler_x86::applyFixupRelocatable(const Reference &ref,
uint8_t *loc,
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::x86);
bool useExternalReloc = useExternalRelocationTo(*ref.target());
ulittle16_t *loc16 = reinterpret_cast<ulittle16_t *>(loc);
ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
switch (static_cast<X86Kind>(ref.kindValue())) {
case branch32:
if (useExternalReloc)
*loc32 = ref.addend() - (fixupAddress + 4);
else
*loc32 =(targetAddress - (fixupAddress+4)) + ref.addend();
break;
case branch16:
if (useExternalReloc)
*loc16 = ref.addend() - (fixupAddress + 2);
else
*loc16 = (targetAddress - (fixupAddress+2)) + ref.addend();
break;
case pointer32:
case abs32:
*loc32 = targetAddress + ref.addend();
break;
case funcRel32:
*loc32 = targetAddress - inAtomAddress + ref.addend(); // FIXME
break;
case delta32:
*loc32 = targetAddress - fixupAddress + ref.addend();
break;
case negDelta32:
*loc32 = fixupAddress - targetAddress + ref.addend();
break;
case modeCode:
case modeData:
case lazyPointer:
case lazyImmediateLocation:
// do nothing
break;
case invalid:
llvm_unreachable("invalid x86 Reference Kind");
break;
}
}
bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) {
// Undefined symbols are referenced via external relocations.
if (isa<UndefinedAtom>(&target))
return true;
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
switch (defAtom->merge()) {
case DefinedAtom::mergeAsTentative:
// Tentative definitions are referenced via external relocations.
return true;
case DefinedAtom::mergeAsWeak:
case DefinedAtom::mergeAsWeakAndAddressUsed:
// Global weak-defs are referenced via external relocations.
return (defAtom->scope() == DefinedAtom::scopeGlobal);
default:
break;
}
}
// Everything else is reference via an internal relocation.
return false;
}
void ArchHandler_x86::appendSectionRelocations(
const DefinedAtom &atom,
uint64_t atomSectionOffset,
const Reference &ref,
FindSymbolIndexForAtom symbolIndexForAtom,
FindSectionIndexForAtom sectionIndexForAtom,
FindAddressForAtom addressForAtom,
normalized::Relocations &relocs) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::x86);
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
bool useExternalReloc = useExternalRelocationTo(*ref.target());
switch (static_cast<X86Kind>(ref.kindValue())) {
case modeCode:
case modeData:
break;
case branch32:
if (useExternalReloc) {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength4);
} else {
if (ref.addend() != 0)
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4);
else
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
GENERIC_RELOC_VANILLA | rPcRel | rLength4);
}
break;
case branch16:
if (useExternalReloc) {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength2);
} else {
if (ref.addend() != 0)
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2);
else
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
GENERIC_RELOC_VANILLA | rPcRel | rLength2);
}
break;
case pointer32:
case abs32:
if (useExternalReloc)
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
GENERIC_RELOC_VANILLA | rExtern | rLength4);
else {
if (ref.addend() != 0)
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
GENERIC_RELOC_VANILLA | rScattered | rLength4);
else
appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
GENERIC_RELOC_VANILLA | rLength4);
}
break;
case funcRel32:
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) - ref.addend(),
GENERIC_RELOC_PAIR | rScattered | rLength4);
break;
case delta32:
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
ref.offsetInAtom(),
GENERIC_RELOC_PAIR | rScattered | rLength4);
break;
case negDelta32:
appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
ref.offsetInAtom(),
GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
GENERIC_RELOC_PAIR | rScattered | rLength4);
break;
case lazyPointer:
case lazyImmediateLocation:
llvm_unreachable("lazy reference kind implies Stubs pass was run");
break;
case invalid:
llvm_unreachable("unknown 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