#include <stdint.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include <vector>
#include <map>
#include "MachOFileAbstraction.hpp"
#include "ld.hpp"
#include "branch_shim.h"
namespace ld {
namespace passes {
namespace branch_shim {
static bool _s_log = false;
class Thumb2ToArmShimAtom : public ld::Atom {
public:
Thumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
: ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
_fixup2(8, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
_fixup3(8, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8),
_fixup4(8, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32)
{ asprintf((char**)&_name, "%s$shim", target->name()); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 12; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
assert( ! _target->isThumb() );
if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name());
OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); OSWriteLittleInt16(&buffer[4], 0, 0x44fc); OSWriteLittleInt16(&buffer[6], 0, 0x4760); OSWriteLittleInt32(&buffer[8], 0, 0x00000000); }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
private:
const char* _name;
const ld::Atom* _target;
ld::Fixup _fixup1;
ld::Fixup _fixup2;
ld::Fixup _fixup3;
ld::Fixup _fixup4;
};
class NoPICThumb2ToArmShimAtom : public ld::Atom {
public:
NoPICThumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
: ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target)
{ asprintf((char**)&_name, "%s$shim", target->name()); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 12; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
assert( ! _target->isThumb() );
if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name());
OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); OSWriteLittleInt16(&buffer[4], 0, 0x4760); OSWriteLittleInt16(&buffer[6], 0, 0x46C0); OSWriteLittleInt32(&buffer[8], 0, 0x00000000); }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
private:
const char* _name;
const ld::Atom* _target;
ld::Fixup _fixup1;
};
class Thumb1ToArmShimAtom : public ld::Atom {
public:
Thumb1ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
: ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
_fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
_fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8),
_fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32)
{ asprintf((char**)&_name, "%s$shim", target->name()); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 16; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
assert( ! _target->isThumb() );
if (_s_log) fprintf(stderr, "6 Thumb1 instruction shim to jump to %s\n", _target->name());
OSWriteLittleInt16(&buffer[ 0], 0, 0xb402); OSWriteLittleInt16(&buffer[ 2], 0, 0x4902); OSWriteLittleInt16(&buffer[ 4], 0, 0x4479); OSWriteLittleInt16(&buffer[ 6], 0, 0x468c); OSWriteLittleInt16(&buffer[ 8], 0, 0xbc02); OSWriteLittleInt16(&buffer[10], 0, 0x4760); OSWriteLittleInt32(&buffer[12], 0, 0x00000000); }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
private:
const char* _name;
const ld::Atom* _target;
ld::Fixup _fixup1;
ld::Fixup _fixup2;
ld::Fixup _fixup3;
ld::Fixup _fixup4;
};
class ARMtoThumbShimAtom : public ld::Atom {
public:
ARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect)
: ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
_fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
_fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12),
_fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32)
{ asprintf((char**)&_name, "%s$shim", target->name()); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 16; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
assert( _target->isThumb() );
if (_s_log) fprintf(stderr, "4 ARM instruction shim to jump to %s\n", _target->name());
OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); OSWriteLittleInt32(&buffer[12], 0, 0); }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
private:
const char* _name;
const ld::Atom* _target;
ld::Fixup _fixup1;
ld::Fixup _fixup2;
ld::Fixup _fixup3;
ld::Fixup _fixup4;
};
class NoPICARMtoThumbShimAtom : public ld::Atom {
public:
NoPICARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect)
: ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target)
{ asprintf((char**)&_name, "%s$shim", target->name()); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 12; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
if (_s_log) fprintf(stderr, "3 ARM instruction shim to jump to %s\n", _target->name());
OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); OSWriteLittleInt32(&buffer[ 4], 0, 0xe12fff1c); OSWriteLittleInt32(&buffer[ 8], 0, 0); }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
private:
const char* _name;
const ld::Atom* _target;
ld::Fixup _fixup1;
};
static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const ld::Atom** target)
{
switch ( fixup->binding ) {
case ld::Fixup::bindingNone:
throw "unexpected bindingNone";
case ld::Fixup::bindingByNameUnbound:
throw "unexpected bindingByNameUnbound";
case ld::Fixup::bindingByContentBound:
case ld::Fixup::bindingDirectlyBound:
*target = fixup->u.target;
break;
case ld::Fixup::bindingsIndirectlyBound:
*target = state.indirectBindingTable[fixup->u.bindingIndex];
break;
}
}
void doPass(const Options& opts, ld::Internal& state)
{
if ( opts.outputKind() == Options::kObjectFile )
return;
if ( opts.architecture() != CPU_TYPE_ARM )
return;
const bool makingKextBundle = (opts.outputKind() == Options::kKextBundle);
for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
std::map<const Atom*, const Atom*> atomToThumbMap;
std::map<const Atom*, const Atom*> thumbToAtomMap;
std::vector<const Atom*> shims;
for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
const ld::Atom* target = NULL;
bool targetIsProxy;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
switch ( fit->kind ) {
case ld::Fixup::kindStoreTargetAddressThumbBranch22:
extractTarget(fit, state, &target);
targetIsProxy = (target->definition() == ld::Atom::definitionProxy);
if ( ! target->isThumb() ) {
const uint8_t* fixUpLocation = atom->rawContentPointer();
if ( fixUpLocation == NULL )
break;
fixUpLocation += fit->offsetInAtom;
uint32_t instruction = *((uint32_t*)fixUpLocation);
bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
if ( is_b || (targetIsProxy && makingKextBundle) ) {
if ( _s_log ) fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
const Atom* shim = NULL;
std::map<const Atom*, const Atom*>::iterator pos = thumbToAtomMap.find(target);
if ( pos == thumbToAtomMap.end() ) {
if ( opts.archSupportsThumb2() ) {
if ( makingKextBundle && opts.allowTextRelocs() )
shim = new NoPICThumb2ToArmShimAtom(target, *sect);
else
shim = new Thumb2ToArmShimAtom(target, *sect);
}
else {
shim = new Thumb1ToArmShimAtom(target, *sect);
}
shims.push_back(shim);
thumbToAtomMap[target] = shim;
}
else {
shim = pos->second;
}
fit->binding = ld::Fixup::bindingDirectlyBound;
fit->u.target = shim;
}
}
break;
case ld::Fixup::kindStoreTargetAddressARMBranch24:
extractTarget(fit, state, &target);
targetIsProxy = (target->definition() == ld::Atom::definitionProxy);
if ( target->isThumb() || (targetIsProxy && makingKextBundle) ) {
const uint8_t* fixUpLocation = atom->rawContentPointer();
if ( fixUpLocation == NULL )
break;
fixUpLocation += fit->offsetInAtom;
uint32_t instruction = *((uint32_t*)fixUpLocation);
bool is_b = ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000);
if ( is_b || (targetIsProxy && makingKextBundle) ) {
if ( _s_log ) fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
const Atom* shim = NULL;
std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target);
if ( pos == atomToThumbMap.end() ) {
if ( makingKextBundle && opts.allowTextRelocs() )
shim = new NoPICARMtoThumbShimAtom(target, *sect);
else
shim = new ARMtoThumbShimAtom(target, *sect);
shims.push_back(shim);
atomToThumbMap[target] = shim;
}
else {
shim = pos->second;
}
fit->binding = ld::Fixup::bindingDirectlyBound;
fit->u.target = shim;
}
}
break;
default:
break;
}
}
}
sect->atoms.insert(sect->atoms.end(), shims.begin(), shims.end());
}
}
} } }