#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <dlfcn.h>
#include <vector>
#include <map>
#include "MachOFileAbstraction.hpp"
#include "ld.hpp"
#include "got.h"
#include "configure.h"
namespace ld {
namespace passes {
namespace got {
class File;
class GOTEntryAtom : public ld::Atom {
public:
GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool weakDef, bool is64)
: ld::Atom(weakDef ? _s_sectionWeak : _s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))),
_fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target),
_target(target),
_is64(is64)
{ _fixup.weakImport = weakImport; internal.addAtom(*this); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _target->name(); }
virtual uint64_t size() const { return (_is64 ? 8 : 4); }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const { }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; }
private:
mutable ld::Fixup _fixup;
const ld::Atom* _target;
bool _is64;
static ld::Section _s_section;
static ld::Section _s_sectionWeak;
};
ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
ld::Section GOTEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer);
#if SUPPORT_ARCH_arm64e
class GOTAuthEntryAtom : public ld::Atom {
public:
GOTAuthEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool weakDef)
: ld::Atom(weakDef ? _s_sectionWeak : _s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
_fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetAuthData, (ld::Fixup::AuthData){ 0, true, ld::Fixup::AuthData::ptrauth_key_asia }),
_fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreTargetAddressLittleEndianAuth64, target),
_target(target)
{ _fixup2.weakImport = weakImport; internal.addAtom(*this); }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _target->name(); }
virtual uint64_t size() const { return 8; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const { }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; }
private:
mutable ld::Fixup _fixup1;
mutable ld::Fixup _fixup2;
const ld::Atom* _target;
static ld::Section _s_section;
static ld::Section _s_sectionWeak;
};
ld::Section GOTAuthEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
ld::Section GOTAuthEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer);
#endif
static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Atom* fixupAtom,
const ld::Fixup* fixup, bool* optimizable, bool* targetIsExternalWeakDef, bool* targetIsPersonalityFn)
{
*targetIsExternalWeakDef = false;
*targetIsPersonalityFn = false;
switch (fixup->kind) {
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
#endif
*optimizable = true;
if ( targetOfGOT->definition() == ld::Atom::definitionProxy )
*optimizable = false;
if ( internal.usingHugeSections && (targetOfGOT->size() > 1024*1024)
&& ( (targetOfGOT->section().type() == ld::Section::typeZeroFill)
|| (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) {
*optimizable = false;
}
if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) {
if ( ((targetOfGOT->definition() == ld::Atom::definitionRegular) || (targetOfGOT->definition() == ld::Atom::definitionProxy)) && (targetOfGOT->combine() == ld::Atom::combineByName) ) {
switch ( opts.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kKextBundle:
*targetIsExternalWeakDef = true;
*optimizable = false;
break;
case Options::kStaticExecutable:
case Options::kDyld:
case Options::kPreload:
case Options::kObjectFile:
break;
}
}
if ( opts.interposable(targetOfGOT->name()) )
*optimizable = false;
if ( targetOfGOT->contentType() == ld::Atom::typeResolver )
*optimizable = false;
if ( opts.nameSpace() != Options::kTwoLevelNameSpace )
*optimizable = false;
}
else if ( targetOfGOT->scope() == ld::Atom::scopeLinkageUnit) {
if ( opts.sharedRegionEligible() ) {
const char* segName = targetOfGOT->section().segmentName();
if ( (strcmp(segName, "__TEXT") != 0) && (strcmp(segName, "__DATA") != 0) ) {
*optimizable = false;
}
}
}
return true;
case ld::Fixup::kindStoreX86PCRel32GOT:
#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreARM64PCRelToGOT:
#endif
#if SUPPORT_ARCH_arm64e
if (opts.supportsAuthenticatedPointers()) {
if (fixupAtom->section().type() == ld::Section::typeCFI)
*targetIsPersonalityFn = true;
}
#endif
*optimizable = false;
return true;
case ld::Fixup::kindNoneGroupSubordinatePersonality:
*optimizable = false;
#if SUPPORT_ARCH_arm64e
if (opts.supportsAuthenticatedPointers())
*targetIsPersonalityFn = true;
#endif
return true;
default:
break;
}
return false;
}
struct AtomByNameSorter
{
bool operator()(const ld::Atom* left, const ld::Atom* right)
{
return (strcmp(left->name(), right->name()) < 0);
}
};
struct GotMapEntry {
const ld::Atom* atom;
bool isPersonalityFn;
bool operator<(const GotMapEntry& other) const {
if (atom != other.atom)
return atom < other.atom;
return (int)isPersonalityFn < (int)other.isPersonalityFn;
}
};
void doPass(const Options& opts, ld::Internal& internal)
{
const bool log = false;
if ( opts.outputKind() == Options::kObjectFile )
return;
std::map<GotMapEntry, const ld::Atom*> gotMap;
for (ld::Internal::FinalSection* sect : internal.sections) {
if ( sect->type() != ld::Section::typeNonLazyPointer )
continue;
for (const ld::Atom* atom : sect->atoms) {
const ld::Atom* target = NULL;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
switch (fit->kind) {
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
switch ( fit->binding ) {
case ld::Fixup::bindingsIndirectlyBound:
target = internal.indirectBindingTable[fit->u.bindingIndex];
break;
case ld::Fixup::bindingDirectlyBound:
target = fit->u.target;
break;
default:
fprintf(stderr, "non-pointer is got entry\n");
break;
}
break;
default:
break;
}
}
if ( target != NULL ) {
if (log) fprintf(stderr, "found existing got entry to %s\n", target->name());
gotMap[{ target, false }] = atom;
}
}
}
std::vector<const ld::Atom*> atomsReferencingGOT;
std::map<const ld::Atom*,bool> weakImportMap;
std::map<const ld::Atom*,bool> weakDefMap;
atomsReferencingGOT.reserve(128);
for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
bool atomUsesGOT = false;
const ld::Atom* targetOfGOT = NULL;
bool targetIsWeakImport = false;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
if ( fit->firstInCluster() )
targetOfGOT = NULL;
switch ( fit->binding ) {
case ld::Fixup::bindingsIndirectlyBound:
targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
targetIsWeakImport = fit->weakImport;
break;
case ld::Fixup::bindingDirectlyBound:
targetOfGOT = fit->u.target;
targetIsWeakImport = fit->weakImport;
break;
default:
break;
}
bool optimizable;
bool targetIsExternalWeakDef;
bool targetIsPersonalityFn;
if ( !gotFixup(opts, internal, targetOfGOT, atom, fit, &optimizable, &targetIsExternalWeakDef, &targetIsPersonalityFn) )
continue;
if ( optimizable ) {
if ( log ) fprintf(stderr, "optimized GOT usage in %s to %s\n", atom->name(), targetOfGOT->name());
switch ( fit->binding ) {
case ld::Fixup::bindingsIndirectlyBound:
case ld::Fixup::bindingDirectlyBound:
fit->binding = ld::Fixup::bindingDirectlyBound;
fit->u.target = targetOfGOT;
switch ( fit->kind ) {
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA;
break;
#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21;
break;
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12;
break;
#endif
default:
assert(0 && "unsupported GOT reference kind");
break;
}
break;
default:
assert(0 && "unsupported GOT reference");
break;
}
}
else {
if ( log ) fprintf(stderr, "found GOT use in %s\n", atom->name());
if ( !atomUsesGOT ) {
atomsReferencingGOT.push_back(atom);
atomUsesGOT = true;
}
if ( gotMap.count({ targetOfGOT, targetIsPersonalityFn }) == 0 )
gotMap[{ targetOfGOT, targetIsPersonalityFn }] = NULL;
weakDefMap[targetOfGOT] = targetIsExternalWeakDef;
std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT);
if ( pos == weakImportMap.end() ) {
if ( log ) fprintf(stderr, "weakImportMap[%s] = %d\n", targetOfGOT->name(), targetIsWeakImport);
weakImportMap[targetOfGOT] = targetIsWeakImport;
}
else {
if ( pos->second != targetIsWeakImport ) {
switch ( opts.weakReferenceMismatchTreatment() ) {
case Options::kWeakReferenceMismatchError:
throwf("mismatching weak references for symbol: %s", targetOfGOT->name());
case Options::kWeakReferenceMismatchWeak:
pos->second = true;
break;
case Options::kWeakReferenceMismatchNonWeak:
pos->second = false;
break;
}
}
}
}
}
}
}
bool is64 = false;
switch ( opts.architecture() ) {
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
is64 = false;
break;
#endif
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
is64 = true;
break;
#endif
#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
is64 = false;
break;
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
is64 = true;
break;
#endif
}
for (auto& entry : gotMap) {
if ( entry.second == NULL ) {
#if SUPPORT_ARCH_arm64e
if ( entry.first.isPersonalityFn && (opts.supportsAuthenticatedPointers()) ) {
entry.second = new GOTAuthEntryAtom(internal, entry.first.atom, weakImportMap[entry.first.atom], opts.useDataConstSegment() && weakDefMap[entry.first.atom]);
if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first.atom->name(), entry.first.atom, entry.second);
continue;
}
#endif
entry.second = new GOTEntryAtom(internal, entry.first.atom, weakImportMap[entry.first.atom], opts.useDataConstSegment() && weakDefMap[entry.first.atom], is64);
if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first.atom->name(), entry.first.atom, entry.second);
}
}
for (std::vector<const ld::Atom*>::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) {
const ld::Atom* atom = *it;
const ld::Atom* targetOfGOT = NULL;
ld::Fixup::iterator fitThatSetTarget = NULL;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
if ( fit->firstInCluster() ) {
targetOfGOT = NULL;
fitThatSetTarget = NULL;
}
switch ( fit->binding ) {
case ld::Fixup::bindingsIndirectlyBound:
targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
fitThatSetTarget = fit;
break;
case ld::Fixup::bindingDirectlyBound:
targetOfGOT = fit->u.target;
fitThatSetTarget = fit;
break;
default:
break;
}
bool optimizable;
bool targetIsExternalWeakDef;
bool targetIsPersonalityFn;
if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, atom, fit,
&optimizable, &targetIsExternalWeakDef, &targetIsPersonalityFn) )
continue;
if ( !optimizable ) {
assert(fitThatSetTarget != NULL);
switch ( fitThatSetTarget->binding ) {
case ld::Fixup::bindingsIndirectlyBound:
case ld::Fixup::bindingDirectlyBound:
if ( log ) fprintf(stderr, "updating GOT use in %s to %s\n", atom->name(), targetOfGOT->name());
fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound;
fitThatSetTarget->u.target = gotMap[{ targetOfGOT, targetIsPersonalityFn }];
break;
default:
assert(0 && "unsupported GOT reference");
break;
}
}
}
}
for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
if ( sect->type() == ld::Section::typeNonLazyPointer ) {
std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter());
}
}
}
} } }