[lld] r219655 - [mach-o] Add Pass to create are shim Atoms for ARM interworking.
Nick Kledzik
kledzik at apple.com
Mon Oct 13 18:51:42 PDT 2014
Author: kledzik
Date: Mon Oct 13 20:51:42 2014
New Revision: 219655
URL: http://llvm.org/viewvc/llvm-project?rev=219655&view=rev
Log:
[mach-o] Add Pass to create are shim Atoms for ARM interworking.
Arm code has two instruction encodings "thumb" and "arm". When branching from
one code encoding to another, you need to use an instruction that switches
the instruction mode. Usually the transition only happens at call sites, and
the linker can transform a BL instruction in BLX (or vice versa). But if the
compiler did a tail call optimization and a function ends with a branch (not
branch and link), there is no pc-rel BX instruction.
The ShimPass looks for pc-rel B instructions that will need to switch mode.
For those cases it synthesizes a shim which does the transition, then modifies
the original atom with the B instruction to target to the shim atom.
Added:
lld/trunk/lib/ReaderWriter/MachO/ShimPass.cpp
lld/trunk/test/mach-o/arm-shims.yaml
Modified:
lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h
lld/trunk/lib/ReaderWriter/MachO/ArchHandler.h
lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
lld/trunk/lib/ReaderWriter/MachO/CMakeLists.txt
lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
lld/trunk/lib/ReaderWriter/MachO/MachOPasses.h
lld/trunk/test/mach-o/arm-interworking.yaml
lld/trunk/test/mach-o/parse-arm-relocs.yaml
Modified: lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h (original)
+++ lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h Mon Oct 13 20:51:42 2014
@@ -230,6 +230,9 @@ public:
/// Pass to transform __compact_unwind into __unwind_info should be run.
bool needsCompactUnwindPass() const;
+ /// Pass to add shims switching between thumb and arm mode.
+ bool needsShimPass() const;
+
/// Magic symbol name stubs will need to help lazy bind.
StringRef binderSymbolName() const;
Modified: lld/trunk/lib/ReaderWriter/MachO/ArchHandler.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/ArchHandler.h?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/ArchHandler.h (original)
+++ lld/trunk/lib/ReaderWriter/MachO/ArchHandler.h Mon Oct 13 20:51:42 2014
@@ -9,6 +9,7 @@
#include "MachONormalizedFile.h"
#include "Atoms.h"
+#include "File.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
@@ -50,6 +51,9 @@ public:
return false;
}
+ /// Used by ShimPass to insert shims in branches that switch mode.
+ virtual bool isNonCallBranch(const Reference &) = 0;
+
/// Used by GOTPass to update GOT References
virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {}
@@ -182,6 +186,12 @@ public:
/// Only relevant for 32-bit arm archs.
virtual bool isThumbFunction(const DefinedAtom &atom) { return false; }
+ /// Only relevant for 32-bit arm archs.
+ virtual const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
+ const DefinedAtom &) {
+ llvm_unreachable("shims only support on arm");
+ }
+
struct ReferenceInfo {
Reference::KindArch arch;
uint16_t kind;
Modified: lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm.cpp?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm.cpp Mon Oct 13 20:51:42 2014
@@ -36,6 +36,7 @@ public:
bool isCallSite(const Reference &) override;
bool isPointer(const Reference &) override;
bool isPairedReloc(const normalized::Relocation &) override;
+ bool isNonCallBranch(const Reference &) override;
bool needsCompactUnwind() override {
return false;
@@ -106,8 +107,13 @@ public:
}
bool isThumbFunction(const DefinedAtom &atom) override;
+ const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
+ const DefinedAtom &) override;
private:
+ friend class Thumb2ToArmShimAtom;
+ friend class ArmToThumbShimAtom;
+
static const Registry::KindStrings _sKindStrings[];
static const StubInfo _sStubInfoArmPIC;
@@ -119,12 +125,14 @@ private:
modeData, /// Content starting at this offset is data.
// Kinds found in mach-o .o files:
- thumb_b22, /// ex: bl _foo
+ thumb_bl22, /// ex: bl _foo
+ thumb_b22, /// ex: b _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_bl24, /// ex: bl _foo
+ arm_b24, /// ex: b _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))
@@ -154,6 +162,7 @@ private:
static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word);
static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word);
+ StringRef stubName(const DefinedAtom &);
bool useExternalRelocationTo(const Atom &target);
void applyFixupFinal(const Reference &ref, uint8_t *location,
@@ -183,11 +192,13 @@ const Registry::KindStrings ArchHandler_
LLD_KIND_STRING_ENTRY(modeThumbCode),
LLD_KIND_STRING_ENTRY(modeArmCode),
LLD_KIND_STRING_ENTRY(modeData),
+ LLD_KIND_STRING_ENTRY(thumb_bl22),
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_bl24),
LLD_KIND_STRING_ENTRY(arm_b24),
LLD_KIND_STRING_ENTRY(arm_movw),
LLD_KIND_STRING_ENTRY(arm_movt),
@@ -256,13 +267,31 @@ const ArchHandler::StubInfo &ArchHandler
}
bool ArchHandler_arm::isCallSite(const Reference &ref) {
- return (ref.kindValue() == thumb_b22) || (ref.kindValue() == arm_b24);
+ switch (ref.kindValue()) {
+ case thumb_b22:
+ case thumb_bl22:
+ case arm_b24:
+ case arm_bl24:
+ return true;
+ default:
+ return false;
+ }
}
bool ArchHandler_arm::isPointer(const Reference &ref) {
return (ref.kindValue() == pointer32);
}
+bool ArchHandler_arm::isNonCallBranch(const Reference &ref) {
+ switch (ref.kindValue()) {
+ case thumb_b22:
+ case arm_b24:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) {
switch (reloc.type) {
case ARM_RELOC_SECTDIFF:
@@ -275,6 +304,23 @@ bool ArchHandler_arm::isPairedReloc(cons
}
}
+/// Trace references from stub atom to lazy pointer to target and get its name.
+StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) {
+ assert(stubAtom.contentType() == DefinedAtom::typeStub);
+ for (const Reference *ref : stubAtom) {
+ if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) {
+ if (lp->contentType() != DefinedAtom::typeLazyPointer)
+ continue;
+ for (const Reference *ref2 : *lp) {
+ if (ref2->kindValue() != lazyPointer)
+ continue;
+ return ref2->target()->name();
+ }
+ }
+ }
+ return "stub";
+}
+
/// Extract displacement from an ARM b/bl/blx instruction.
int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
// Sign-extend imm24
@@ -352,7 +398,7 @@ uint32_t ArchHandler_arm::setDisplacemen
bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
- uint32_t newInstruction = (instruction & 0xF800D000);
+ uint32_t newInstruction = (instruction & 0xD000F800);
if (is_bl || is_blx) {
if (targetIsThumb) {
newInstruction = 0xD000F000; // Use bl
@@ -363,7 +409,7 @@ uint32_t ArchHandler_arm::setDisplacemen
displacement += 2;
}
} else if (is_b) {
- assert(!targetIsThumb && "no pc-rel thumb branch instruction that "
+ assert(targetIsThumb && "no pc-rel thumb branch instruction that "
"switches to arm mode");
}
else {
@@ -461,7 +507,10 @@ std::error_code ArchHandler_arm::getRefe
switch (relocPattern(reloc)) {
case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4:
// ex: bl _foo (and _foo is undefined)
- *kind = thumb_b22;
+ if ((instruction & 0xD000F800) == 0x9000F000)
+ *kind = thumb_b22;
+ else
+ *kind = thumb_bl22;
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
// Instruction contains branch to addend.
@@ -470,13 +519,19 @@ std::error_code ArchHandler_arm::getRefe
return std::error_code();
case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
// ex: bl _foo (and _foo is defined)
- *kind = thumb_b22;
+ if ((instruction & 0xD000F800) == 0x9000F000)
+ *kind = thumb_b22;
+ else
+ *kind = thumb_bl22;
displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
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;
+ if ((instruction & 0xD000F800) == 0x9000F000)
+ *kind = thumb_b22;
+ else
+ *kind = thumb_bl22;
displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
targetAddress = fixupAddress + 4 + displacement;
if (E ec = atomFromAddress(0, reloc.value, target, addend))
@@ -487,7 +542,11 @@ std::error_code ArchHandler_arm::getRefe
return std::error_code();
case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4:
// ex: bl _foo (and _foo is undefined)
- *kind = arm_b24;
+ if (((instruction & 0x0F000000) == 0x0A000000)
+ && ((instruction & 0xF0000000) != 0xF0000000))
+ *kind = arm_b24;
+ else
+ *kind = arm_bl24;
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
// Instruction contains branch to addend.
@@ -496,13 +555,21 @@ std::error_code ArchHandler_arm::getRefe
return std::error_code();
case ARM_RELOC_BR24 | rPcRel | rLength4:
// ex: bl _foo (and _foo is defined)
- *kind = arm_b24;
+ if (((instruction & 0x0F000000) == 0x0A000000)
+ && ((instruction & 0xF0000000) != 0xF0000000))
+ *kind = arm_b24;
+ else
+ *kind = arm_bl24;
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;
+ if (((instruction & 0x0F000000) == 0x0A000000)
+ && ((instruction & 0xF0000000) != 0xF0000000))
+ *kind = arm_b24;
+ else
+ *kind = arm_bl24;
displacement = getDisplacementFromArmBranch(instruction);
targetAddress = fixupAddress + 8 + displacement;
if (E ec = atomFromAddress(0, reloc.value, target, addend))
@@ -836,6 +903,7 @@ void ArchHandler_arm::applyFixupFinal(co
case modeData:
break;
case thumb_b22:
+ case thumb_bl22:
assert(thumbMode);
displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement,
@@ -867,7 +935,8 @@ void ArchHandler_arm::applyFixupFinal(co
write32(*loc32, _swap, setWordFromThumbMov(*loc32, value16));
break;
case arm_b24:
- assert(!thumbMode);
+ case arm_bl24:
+ assert(!thumbMode);
displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb);
write32(*loc32, _swap, value32);
@@ -903,7 +972,10 @@ void ArchHandler_arm::applyFixupFinal(co
write32(*loc32, _swap, targetAddress + ref.addend());
break;
case delta32:
- write32(*loc32, _swap, targetAddress - fixupAddress + ref.addend());
+ if (targetIsThumb)
+ write32(*loc32, _swap, targetAddress - fixupAddress + ref.addend() + 1);
+ else
+ write32(*loc32, _swap, targetAddress - fixupAddress + ref.addend());
break;
case lazyPointer:
case lazyImmediateLocation:
@@ -990,6 +1062,7 @@ void ArchHandler_arm::applyFixupRelocata
case modeData:
break;
case thumb_b22:
+ case thumb_bl22:
assert(thumbMode);
if (useExternalReloc)
displacement = (ref.addend() - (fixupAddress + 4));
@@ -1026,6 +1099,7 @@ void ArchHandler_arm::applyFixupRelocata
write32(*loc32, _swap, setWordFromThumbMov(*loc32, value16));
break;
case arm_b24:
+ case arm_bl24:
assert(!thumbMode);
if (useExternalReloc)
displacement = (ref.addend() - (fixupAddress + 8));
@@ -1100,6 +1174,7 @@ void ArchHandler_arm::appendSectionReloc
// Do nothing.
break;
case thumb_b22:
+ case thumb_bl22:
if (useExternalReloc) {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4);
@@ -1179,6 +1254,7 @@ void ArchHandler_arm::appendSectionReloc
ARM_RELOC_PAIR | rScattered | rLenThmbHi);
break;
case arm_b24:
+ case arm_bl24:
if (useExternalReloc) {
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
ARM_RELOC_BR24 | rExtern | rPcRel | rLength4);
@@ -1307,6 +1383,112 @@ bool ArchHandler_arm::isThumbFunction(co
return false;
}
+
+class Thumb2ToArmShimAtom : public SimpleDefinedAtom {
+public:
+ Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName,
+ const DefinedAtom &target)
+ : SimpleDefinedAtom(file) {
+ addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
+ ArchHandler_arm::modeThumbCode, 0, this, 0);
+ addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
+ ArchHandler_arm::delta32, 8, &target, 0);
+ std::string name = std::string(targetName) + "$shim";
+ StringRef tmp(name);
+ _name = tmp.copy(file.allocator());
+ }
+
+ StringRef name() const override {
+ return _name;
+ }
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeCode;
+ }
+
+ Alignment alignment() const override {
+ return Alignment(2);
+ }
+
+ 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 bytes[] =
+ { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4
+ 0xFF, 0x44, // add ip, pc, ip
+ 0x60, 0x47, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00 }; // .long target - this
+ assert(sizeof(bytes) == size());
+ return llvm::makeArrayRef(bytes, sizeof(bytes));
+ }
+private:
+ StringRef _name;
+};
+
+
+class ArmToThumbShimAtom : public SimpleDefinedAtom {
+public:
+ ArmToThumbShimAtom(MachOFile &file, StringRef targetName,
+ const DefinedAtom &target)
+ : SimpleDefinedAtom(file) {
+ addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
+ ArchHandler_arm::delta32, 12, &target, 0);
+ std::string name = std::string(targetName) + "$shim";
+ StringRef tmp(name);
+ _name = tmp.copy(file.allocator());
+ }
+
+ StringRef name() const override {
+ return _name;
+ }
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeCode;
+ }
+
+ Alignment alignment() const override {
+ return Alignment(2);
+ }
+
+ 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 bytes[] =
+ { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4
+ 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
+ 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00 }; // .long target - this
+ assert(sizeof(bytes) == size());
+ return llvm::makeArrayRef(bytes, sizeof(bytes));
+ }
+private:
+ StringRef _name;
+};
+
+const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file,
+ bool thumbToArm,
+ const DefinedAtom &target) {
+ bool isStub = (target.contentType() == DefinedAtom::typeStub);
+ StringRef targetName = isStub ? stubName(target) : target.name();
+ if (thumbToArm)
+ return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target);
+ else
+ return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target);
+}
+
+
std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() {
return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm());
}
Modified: lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp Mon Oct 13 20:51:42 2014
@@ -74,6 +74,10 @@ public:
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;
Modified: lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86.cpp?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86.cpp Mon Oct 13 20:51:42 2014
@@ -34,6 +34,10 @@ public:
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;
Modified: lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp Mon Oct 13 20:51:42 2014
@@ -86,6 +86,10 @@ public:
const StubInfo &stubInfo() override { return _sStubInfo; }
+ bool isNonCallBranch(const Reference &) override {
+ return false;
+ }
+
bool isCallSite(const Reference &) override;
bool isPointer(const Reference &) override;
bool isPairedReloc(const normalized::Relocation &) override;
Modified: lld/trunk/lib/ReaderWriter/MachO/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/CMakeLists.txt?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/CMakeLists.txt (original)
+++ lld/trunk/lib/ReaderWriter/MachO/CMakeLists.txt Mon Oct 13 20:51:42 2014
@@ -12,6 +12,7 @@ add_lld_library(lldMachO
MachONormalizedFileFromAtoms.cpp
MachONormalizedFileToAtoms.cpp
MachONormalizedFileYAML.cpp
+ ShimPass.cpp
StubsPass.cpp
WriterMachO.cpp
)
Modified: lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp Mon Oct 13 20:51:42 2014
@@ -324,6 +324,21 @@ bool MachOLinkingContext::needsCompactUn
}
}
+bool MachOLinkingContext::needsShimPass() const {
+ // Shim pass only used in final executables.
+ if (_outputMachOType == MH_OBJECT)
+ return false;
+ // Only 32-bit arm arches use Shim pass.
+ switch (_arch) {
+ case arch_armv6:
+ case arch_armv7:
+ case arch_armv7s:
+ return true;
+ default:
+ return false;
+ }
+}
+
StringRef MachOLinkingContext::binderSymbolName() const {
return archHandler().stubInfo().binderSymbolName;
}
@@ -572,6 +587,8 @@ void MachOLinkingContext::addPasses(Pass
mach_o::addCompactUnwindPass(pm, *this);
if (needsGOTPass())
mach_o::addGOTPass(pm, *this);
+ if (needsShimPass())
+ mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass.
}
Writer &MachOLinkingContext::writer() const {
Modified: lld/trunk/lib/ReaderWriter/MachO/MachOPasses.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachOPasses.h?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachOPasses.h (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachOPasses.h Mon Oct 13 20:51:42 2014
@@ -19,6 +19,7 @@ namespace mach_o {
void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx);
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx);
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addShimPass(PassManager &pm, const MachOLinkingContext &ctx);
} // namespace mach_o
} // namespace lld
Added: lld/trunk/lib/ReaderWriter/MachO/ShimPass.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/ShimPass.cpp?rev=219655&view=auto
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/ShimPass.cpp (added)
+++ lld/trunk/lib/ReaderWriter/MachO/ShimPass.cpp Mon Oct 13 20:51:42 2014
@@ -0,0 +1,131 @@
+//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This linker pass updates branch-sites whose target is a different mode
+// (thumb vs arm).
+//
+// Arm code has two instruction encodings thumb and arm. When branching from
+// one code encoding to another, you need to use an instruction that switches
+// the instruction mode. Usually the transition only happens at call sites, and
+// the linker can transform a BL instruction in BLX (or vice versa). But if the
+// compiler did a tail call optimization and a function ends with a branch (not
+// branch and link), there is no pc-rel BX instruction.
+//
+// The ShimPass looks for pc-rel B instructions that will need to switch mode.
+// For those cases it synthesizes a shim which does the transition, then
+// modifies the original atom with the B instruction to target to the shim atom.
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+
+
+namespace lld {
+namespace mach_o {
+
+class ShimPass : public Pass {
+public:
+ ShimPass(const MachOLinkingContext &context)
+ : _context(context)
+ , _archHandler(_context.archHandler())
+ , _stubInfo(_archHandler.stubInfo())
+ , _file("<mach-o shim pass>") {
+ }
+
+
+ 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 non-call branches.
+ if (!_archHandler.isNonCallBranch(*ref))
+ continue;
+ const Atom *target = ref->target();
+ assert(target != nullptr);
+ if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) {
+ bool atomIsThumb = _archHandler.isThumbFunction(*atom);
+ bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
+ if (atomIsThumb != targetIsThumb)
+ updateBranchToUseShim(atomIsThumb, *daTarget, ref);
+ }
+ }
+ }
+ // Exit early if no shims needed.
+ if (_targetToShim.empty())
+ return;
+
+ // Sort shim atoms so the layout order is stable.
+ std::vector<const DefinedAtom *> shims;
+ shims.reserve(_targetToShim.size());
+ for (auto element : _targetToShim) {
+ shims.push_back(element.second);
+ }
+ std::sort(shims.begin(), shims.end(),
+ [](const DefinedAtom *l, const DefinedAtom *r) {
+ return (l->name() < r->name());
+ });
+
+ // Add all shims to master file.
+ for (const DefinedAtom *shim : shims) {
+ mergedFile->addAtom(*shim);
+ }
+ }
+
+private:
+
+ void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target,
+ const Reference *ref) {
+ // Make file-format specific stub and other support atoms.
+ const DefinedAtom *shim = this->getShim(thumbToArm, target);
+ assert(shim != nullptr);
+ // Switch branch site to target shim atom.
+ const_cast<Reference *>(ref)->setTarget(shim);
+ }
+
+ const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) {
+ auto pos = _targetToShim.find(&target);
+ if ( pos != _targetToShim.end() ) {
+ // Reuse an existing shim.
+ assert(pos->second != nullptr);
+ return pos->second;
+ } else {
+ // There is no existing shim, so create a new one.
+ const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
+ target);
+ _targetToShim[&target] = shim;
+ return shim;
+ }
+ }
+
+ const MachOLinkingContext &_context;
+ mach_o::ArchHandler &_archHandler;
+ const ArchHandler::StubInfo &_stubInfo;
+ MachOFile _file;
+ llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim;
+};
+
+
+
+void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ pm.add(std::unique_ptr<Pass>(new ShimPass(ctx)));
+}
+
+} // end namespace mach_o
+} // end namespace lld
Modified: lld/trunk/test/mach-o/arm-interworking.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/mach-o/arm-interworking.yaml?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/test/mach-o/arm-interworking.yaml (original)
+++ lld/trunk/test/mach-o/arm-interworking.yaml Mon Oct 13 20:51:42 2014
@@ -237,28 +237,28 @@ undefined-symbols:
# CHECK: - kind: modeThumbCode
# CHECK: offset: 0
# CHECK: target: _t1
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 0
# CHECK: target: _a1
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 6
# CHECK: target: _a2
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 12
# CHECK: target: _a2
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 16
# CHECK: target: _t1
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 22
# CHECK: target: _t1
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 28
# CHECK: target: _t2
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 34
# CHECK: target: _t2
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 38
# CHECK: target: _t3
# CHECK: - name: _t2
@@ -277,16 +277,16 @@ undefined-symbols:
# CHECK: - name: _a1
# CHECK: scope: global
# CHECK: references:
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 0
# CHECK: target: _a1
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 4
# CHECK: target: _a2
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 8
# CHECK: target: _t1
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 12
# CHECK: target: _t2
# CHECK: - name: _a2
Added: lld/trunk/test/mach-o/arm-shims.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/mach-o/arm-shims.yaml?rev=219655&view=auto
==============================================================================
--- lld/trunk/test/mach-o/arm-shims.yaml (added)
+++ lld/trunk/test/mach-o/arm-shims.yaml Mon Oct 13 20:51:42 2014
@@ -0,0 +1,179 @@
+# RUN: lld -flavor darwin -arch armv7 %s -dylib %p/Inputs/libSystem.yaml -o %t
+# RUN: macho-dump --dump-section-data %t | FileCheck %s
+#
+# Test b from arm to thumb or vice versa has shims added.s
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7,
+ 0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3,
+ 0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ]
+ relocations:
+ - offset: 0x00000014
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000010
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000006
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000002
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _a1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+ - name: _t1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _a2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7,
+ 0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3,
+ 0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ]
+ relocations:
+ - offset: 0x00000014
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000010
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000006
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000002
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _a2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+ - name: _t2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _a1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _t1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+# CHECK: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+# CHECK: ('_section_data', '00bf00f0 10e800f0 19b80000 00f020e3 000000fa 0f0000ea 00bffff7 f8ef00f0 07b80000 00f020e3 f4fffffa 050000ea dff804c0 ff446047 d4ffffff dff804c0 ff446047 e0ffffff 04c09fe5 0cc08fe0 1cff2fe1 adffffff 04c09fe5 0cc08fe0 1cff2fe1 b5ffffff')
+
+# When we get a good mach-o disassembler the above __text section content check can be change to be symbolic.
+
+
+# Input file one:
+#
+# .align 2
+# .code 16
+# .globl _t1
+# .thumb_func _t1
+#_t1:
+# nop
+# blx _a2
+# b _a2
+#
+# .code 32
+# .align 2
+# .globl _a1
+#_a1:
+# nop
+# blx _t2
+# b _t2
+
+
+
+# Input file two:
+#
+# .align 2
+# .code 16
+# .globl _t2
+# .thumb_func _t2
+#_t2:
+# nop
+# blx _a1
+# b _a1
+#
+# .code 32
+# .align 2
+# .globl _a2
+#_a2:
+# nop
+# blx _t1
+# b _t1
Modified: lld/trunk/test/mach-o/parse-arm-relocs.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/mach-o/parse-arm-relocs.yaml?rev=219655&r1=219654&r2=219655&view=diff
==============================================================================
--- lld/trunk/test/mach-o/parse-arm-relocs.yaml (original)
+++ lld/trunk/test/mach-o/parse-arm-relocs.yaml Mon Oct 13 20:51:42 2014
@@ -572,19 +572,19 @@ undefined-symbols:
# CHECK: references:
# CHECK: - kind: modeThumbCode
# CHECK: offset: 0
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 0
# CHECK: target: _x
# CHECK-NOT: addend:
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 4
# CHECK: target: _x
# CHECK: addend: 4
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 8
# CHECK: target: _undef
# CHECK-NOT: addend:
-# CHECK: - kind: thumb_b22
+# CHECK: - kind: thumb_bl22
# CHECK: offset: 12
# CHECK: target: _undef
# CHECK: addend: 4
@@ -660,19 +660,19 @@ undefined-symbols:
# CHECK: - name: _foo_arm
# CHECK: references:
# CHECK-NOT: - kind: modeThumbCode
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 0
# CHECK: target: _x
# CHECK-NOT: addend:
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 4
# CHECK: target: _x
# CHECK: addend: 4
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 8
# CHECK: target: _undef
# CHECK-NOT: addend:
-# CHECK: - kind: arm_b24
+# CHECK: - kind: arm_bl24
# CHECK: offset: 12
# CHECK: target: _undef
# CHECK: addend: 4
@@ -739,11 +739,11 @@ undefined-symbols:
# bl _undef
# bl _undef+4
# movw r1, :lower16:(_x-L1)
-# movt r1, :upper16:(_x-L1)
+# movt r1, :upper16:(_x-L1)
# movw r2, :lower16:(_x+8-L1)
-# movt r2, :upper16:(_x+8-L1)
+# movt r2, :upper16:(_x+8-L1)
# movw r1, :lower16:(_t1-L1)
-# movt r1, :upper16:(_t1-L1)
+# movt r1, :upper16:(_t1-L1)
# add r1, pc
#L1:
# movw r3, :lower16:_x
@@ -770,9 +770,9 @@ undefined-symbols:
# bl _undef
# bl _undef+4
# movw r1, :lower16:(_x-L2)
-# movt r1, :upper16:(_x-L2)
+# movt r1, :upper16:(_x-L2)
# movw r2, :lower16:(_x+8-L2)
-# movt r2, :upper16:(_x+8-L2)
+# movt r2, :upper16:(_x+8-L2)
# add r1, pc
#L2:
# movw r3, :lower16:_x
More information about the llvm-commits
mailing list