space on disk in a final linked image.
.It Fl merge_zero_fill_sections
Causes all zero-fill sections in the __DATA segment to be merged into one __zerofill section.
+.It Fl no_branch_islands
+Disables linker creation of branch islands which allows images to be created that are larger than the
+maximum branch distance. Useful with -preload when code is in multiple sections but all are within
+the branch range.
.El
.Ss Options when creating a dynamic library (dylib)
.Bl -tag
but rather leave them as private external in the resulting object file.
.It Fl d
Force definition of common symbols. That is, transform tentative definitions into real definitions.
+.It Fl rename_section Ar fromSegment fromSection toSegment toSection
+Renames section fromSegment/fromSection to toSegment/toSection.
.El
.Ss Options that control symbol resolution
.Bl -tag
"$(DEVELOPER_DIR)/usr/include",
);
INSTALL_PATH = /usr/bin;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
LINKER_DISPLAYS_MANGLED_NAMES = NO;
MACOSX_DEPLOYMENT_TARGET = "";
OTHER_CPLUSPLUSFLAGS = (
);
OTHER_LDFLAGS = (
"-stdlib=libc++",
- "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
"-Wl,-exported_symbol,__mh_execute_header",
);
PREBINDING = NO;
"$(DEVELOPER_DIR)/usr/include",
);
INSTALL_PATH = /usr/bin;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CPLUSPLUSFLAGS)",
);
OTHER_LDFLAGS = (
"-stdlib=libc++",
- "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
"-Wl,-exported_symbol,__mh_execute_header",
);
PREBINDING = NO;
);
OTHER_LDFLAGS = (
"-stdlib=libc++",
- "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
);
OTHER_REZFLAGS = "";
PREBINDING = NO;
);
OTHER_LDFLAGS = (
"-stdlib=libc++",
- "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
);
OTHER_REZFLAGS = "";
PREBINDING = NO;
"$(DEVELOPER_DIR)/usr/include",
);
INSTALL_PATH = /usr/bin;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CPLUSPLUSFLAGS)",
);
OTHER_LDFLAGS = (
"-stdlib=libc++",
- "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
"-Wl,-exported_symbol,__mh_execute_header",
);
PREBINDING = NO;
);
OTHER_LDFLAGS = (
"-stdlib=libc++",
- "@$(DERIVED_SOURCES_DIR)/LTO_option.txt",
+ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib",
);
OTHER_REZFLAGS = "";
PREBINDING = NO;
};
#endif
+#ifndef LC_LINKER_OPTIMIZATION_HINTS
+ #define LC_LINKER_OPTIMIZATION_HINTS 0x2E
+ #define LOH_ARM64_ADRP_ADRP 1
+ #define LOH_ARM64_ADRP_LDR 2
+ #define LOH_ARM64_ADRP_ADD_LDR 3
+ #define LOH_ARM64_ADRP_LDR_GOT_LDR 4
+ #define LOH_ARM64_ADRP_ADD_STR 5
+ #define LOH_ARM64_ADRP_LDR_GOT_STR 6
+ #define LOH_ARM64_ADRP_ADD 7
+ #define LOH_ARM64_ADRP_LDR_GOT 8
+#endif
+
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
#endif
#define CPU_SUBTYPE_ARM_V7EM ((cpu_subtype_t) 16)
#endif
+#ifndef CPU_SUBTYPE_X86_64_H
+ #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8)
+#endif
struct ArchInfo {
const char* archName;
static const ArchInfo archInfoArray[] = {
#if SUPPORT_ARCH_x86_64
- { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", false, false },
+ { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", true, false },
+#endif
+#if SUPPORT_ARCH_x86_64h
+ { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, "x86_64h-", "", true, false },
#endif
#if SUPPORT_ARCH_i386
{ "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, "i386-", "", false, false },
{
if ( p >= end )
throw "malformed trie, node past end";
- const uint8_t terminalSize = read_uleb128(p, end);
+ const uint64_t terminalSize = read_uleb128(p, end);
const uint8_t* children = p + terminalSize;
if ( terminalSize != 0 ) {
EntryWithOffset e;
fi
if [ -z "${RC_SUPPORTED_ARCHS}" ]; then
- RC_SUPPORTED_ARCHS="i386 x86_64 armv7 armv7s arm64"
+ RC_SUPPORTED_ARCHS="i386 x86_64 x86_64h armv6 armv7 armv7s armv7m arm64"
fi
for ANARCH in ${RC_SUPPORTED_ARCHS}
do
- KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,"
+ KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h,"
FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"`
if [ $FOUND ]; then
echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h
echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h
-# <rdar://problem/10897631> ld64 hardcodes a reference to /Developer/usr/lib/libLTO.dylib
-if [ -n "${DT_TOOLCHAIN_DIR}" ]
-then
- echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt
-else
- if [ -e "/Developer/usr/lib/libLTO.dylib" ]
- then
- echo "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt
- else
- echo "-Wl,-lazy_library,${BUILT_PRODUCTS_DIR}/../lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt
- fi
-fi
-
uint8_t* copyDependentDRLoadCommand(uint8_t* p) const;
uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const;
uint8_t* copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector<const char*>&) const;
+ uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const;
uint32_t sectionFlags(ld::Internal::FinalSection* sect) const;
bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const;
bool _hasDataInCodeLoadCommand;
bool _hasSourceVersionLoadCommand;
bool _hasDependentDRInfo;
+ bool _hasOptimizationHints;
uint32_t _dylibLoadCommmandsCount;
uint32_t _allowableClientLoadCommmandsCount;
uint32_t _dyldEnvironExrasCount;
_hasRoutinesLoadCommand = (opts.initFunctionName() != NULL);
_hasSymbolTableLoadCommand = true;
_hasUUIDLoadCommand = (opts.UUIDMode() != Options::kUUIDNone);
+ _hasOptimizationHints = (_state.someObjectHasOptimizationHints && (opts.outputKind() == Options::kObjectFile));
switch ( opts.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
if ( _hasDependentDRInfo )
sz += sizeof(macho_linkedit_data_command<P>);
+
+ if ( _hasOptimizationHints )
+ sz += sizeof(macho_linkedit_data_command<P>);
return sz;
}
if ( _hasDependentDRInfo )
++count;
+
+ if ( _hasOptimizationHints )
+ ++count;
return count;
}
bits |= MH_FORCE_FLAT;
break;
}
- if ( _writer.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols )
+ if ( _state.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols )
bits |= MH_WEAK_DEFINES;
- if ( _writer.usesWeakExternalSymbols || _writer.hasWeakExternalSymbols )
+ if ( _writer.usesWeakExternalSymbols || _state.hasWeakExternalSymbols )
bits |= MH_BINDS_TO_WEAK;
if ( _options.prebind() )
bits |= MH_PREBOUND;
bits |= MH_PIE;
if ( _options.markAutoDeadStripDylib() )
bits |= MH_DEAD_STRIPPABLE_DYLIB;
- if ( _writer.hasThreadLocalVariableDefinitions )
+ if ( _state.hasThreadLocalVariableDefinitions )
bits |= MH_HAS_TLV_DESCRIPTORS;
if ( _options.hasNonExecutableHeap() )
bits |= MH_NO_HEAP_EXECUTION;
template <>
uint32_t HeaderAndLoadCommandsAtom<x86_64>::cpuSubType() const
{
- if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) )
- return (CPU_SUBTYPE_X86_64_ALL | 0x80000000);
+ if ( (_options.outputKind() == Options::kDynamicExecutable) && (_state.cpuSubType == CPU_SUBTYPE_X86_64_ALL) && (_options.macosxVersionMin() >= ld::mac10_5) )
+ return (_state.cpuSubType | 0x80000000);
else
- return CPU_SUBTYPE_X86_64_ALL;
+ return _state.cpuSubType;
}
template <>
}
+
+template <typename A>
+uint8_t* HeaderAndLoadCommandsAtom<A>::copyOptimizationHintsLoadCommand(uint8_t* p) const
+{
+ macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)p;
+ cmd->set_cmd(LC_LINKER_OPTIMIZATION_HINTS);
+ cmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>));
+ cmd->set_dataoff(_writer.optimizationHintsSection->fileOffset);
+ cmd->set_datasize(_writer.optimizationHintsSection->size);
+ return p + sizeof(macho_linkedit_data_command<P>);
+}
+
+
template <typename A>
void HeaderAndLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
{
if ( _hasDependentDRInfo )
p = this->copyDependentDRLoadCommand(p);
+ if ( _hasOptimizationHints )
+ p = this->copyOptimizationHintsLoadCommand(p);
+
}
objOpts.warnUnwindConversionProblems = _options.needsUnwindInfoSection();
objOpts.keepDwarfUnwind = _options.keepDwarfUnwind();
objOpts.forceDwarfConversion= (_options.outputKind() == Options::kDyld);
+ objOpts.neverConvertDwarf = !_options.needsUnwindInfoSection();
+ objOpts.verboseOptimizationHints = _options.verboseOptimizationHints();
objOpts.subType = _options.subArchitecture();
ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts);
if ( objResult != NULL ) {
}
// see if it is an llvm object file
- objResult = lto::parse(p, len, info.path, info.modTime, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles());
+ objResult = lto::parse(p, len, info.path, info.modTime, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles(), _options.verboseOptimizationHints());
if ( objResult != NULL ) {
OSAtomicAdd64(len, &_totalObjectSize);
OSAtomicIncrement32(&_totalObjectLoaded);
}
}
catch (const char* msg) {
- throwf("in '%s', %s", info.path, msg);
+ warning("Auto-Linking supplied '%s', %s", info.path, msg);
}
}
}
}
}
catch (const char* msg) {
- throwf("in '%s', %s", info.path, msg);
+ warning("Auto-Linking supplied '%s', %s", info.path, msg);
}
}
}
bool inferredArch() const { return _inferredArch; }
+ void addLinkerOptionLibraries(ld::Internal& state);
+ void createIndirectDylibs();
+
// for -print_statistics
volatile int64_t _totalObjectSize;
volatile int64_t _totalArchiveSize;
void logDylib(ld::File*, bool indirect);
void logArchive(ld::File*) const;
void markExplicitlyLinkedDylibs();
- void createIndirectDylibs();
void checkDylibClientRestrictions(ld::dylib::File*);
void createOpaqueFileSections();
- void addLinkerOptionLibraries(ld::Internal& state);
bool libraryAlreadyLoaded(const char* path);
// for pipelined linking
+template <typename A>
+class OptimizationHintsAtom : public LinkEditAtom
+{
+public:
+ OptimizationHintsAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) {
+ assert(opts.outputKind() == Options::kObjectFile);
+ }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "linker optimization hints"; }
+ // overrides of LinkEditAtom
+ virtual void encode() const;
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ static ld::Section _s_section;
+
+};
+
+template <typename A>
+ld::Section OptimizationHintsAtom<A>::_s_section("__LINKEDIT", "__opt_hints", ld::Section::typeLinkEdit, true);
+
+template <typename A>
+void OptimizationHintsAtom<A>::encode() const
+{
+ if ( _state.someObjectHasOptimizationHints ) {
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() != ld::Section::typeCode )
+ continue;
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ uint64_t address = atom->finalAddress();
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint)
+ continue;
+ ld::Fixup::LOH_arm64 extra;
+ extra.addend = fit->u.addend;
+ _encodedData.append_uleb128(extra.info.kind);
+ _encodedData.append_uleb128(extra.info.count+1);
+ _encodedData.append_uleb128((extra.info.delta1 << 2) + fit->offsetInAtom + address);
+ if ( extra.info.count > 0 )
+ _encodedData.append_uleb128((extra.info.delta2 << 2) + fit->offsetInAtom + address);
+ if ( extra.info.count > 1 )
+ _encodedData.append_uleb128((extra.info.delta3 << 2) + fit->offsetInAtom + address);
+ if ( extra.info.count > 2 )
+ _encodedData.append_uleb128((extra.info.delta4 << 2) + fit->offsetInAtom + address);
+ }
+ }
+ }
+
+ this->_encodedData.pad_to_size(sizeof(pint_t));
+ }
+
+ this->_encoded = true;
+}
+
+
} // namespace tool
} // namespace ld
fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false),
fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true),
fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false),
- fGenerateDtraceDOF(true),
+ fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false),
+ fGenerateDtraceDOF(true), fAllowBranchIslands(true),
fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset),
fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL),
}
}
else {
- bool lookForDylibs = ( fOutputKind != Options::kDyld);
+ bool lookForDylibs = false;
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile: // <rdar://problem/15914513>
+ lookForDylibs = true;
+ break;
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kPreload:
+ case Options::kKextBundle:
+ lookForDylibs = false;
+ break;
+ }
switch ( fLibrarySearchMode ) {
case kSearchAllDirsForDylibsThenAllDirsForArchives:
// first look in all directories for just for dylibs
fExtraSections.push_back(info);
}
+void Options::addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection)
+{
+ if ( strlen(srcSegment) > 16 )
+ throw "-rename_section segment name max 16 chars";
+ if ( strlen(srcSection) > 16 )
+ throw "-rename_section section name max 16 chars";
+ if ( strlen(dstSegment) > 16 )
+ throw "-rename_section segment name max 16 chars";
+ if ( strlen(dstSection) > 16 )
+ throw "-rename_section section name max 16 chars";
+
+ SectionRename info;
+ info.fromSegment = srcSegment;
+ info.fromSection = srcSection;
+ info.toSegment = dstSegment;
+ info.toSection = dstSection;
+
+ fSectionRenames.push_back(info);
+}
+
+
void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr)
{
if ( strlen(segment) > 16 )
snapshotFileArgIndex = 1;
parseAliasFile(argv[++i]);
}
- // put this last so that it does not interfer with other options starting with 'i'
- else if ( strncmp(arg, "-i", 2) == 0 ) {
- const char* colon = strchr(arg, ':');
- if ( colon == NULL )
- throwf("unknown option: %s", arg);
- Options::AliasPair pair;
- char* temp = new char[colon-arg];
- strlcpy(temp, &arg[2], colon-arg-1);
- pair.realName = &colon[1];
- pair.alias = temp;
- fAliases.push_back(pair);
- }
else if ( strcmp(arg, "-save-temps") == 0 ) {
fSaveTempFiles = true;
}
fKeepDwarfUnwindForcedOn = false;
fKeepDwarfUnwindForcedOff = true;
}
+ else if ( strcmp(arg, "-verbose_optimization_hints") == 0 ) {
+ fVerboseOptimizationHints = true;
+ }
+ else if ( strcmp(arg, "-ignore_optimization_hints") == 0 ) {
+ fIgnoreOptimizationHints = true;
+ }
else if ( strcmp(arg, "-no_dtrace_dof") == 0 ) {
fGenerateDtraceDOF = false;
}
+ else if ( strcmp(arg, "-rename_section") == 0 ) {
+ if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) || (argv[i+4]==NULL) )
+ throw "-rename_section missing <segment> <section> <segment> <section>";
+ addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]);
+ i += 4;
+ }
+ else if ( strcmp(arg, "-no_branch_islands") == 0 ) {
+ fAllowBranchIslands = false;
+ }
+ // put this last so that it does not interfer with other options starting with 'i'
+ else if ( strncmp(arg, "-i", 2) == 0 ) {
+ const char* colon = strchr(arg, ':');
+ if ( colon == NULL )
+ throwf("unknown option: %s", arg);
+ Options::AliasPair pair;
+ char* temp = new char[colon-arg];
+ strlcpy(temp, &arg[2], colon-arg-1);
+ pair.realName = &colon[1];
+ pair.alias = temp;
+ fAliases.push_back(pair);
+ }
else {
throwf("unknown option: %s", arg);
}
fFunctionStartsLoadCommand = true;
break;
case Options::kObjectFile:
+ if ( !fDataInCodeInfoLoadCommandForcedOff )
+ fDataInCodeInfoLoadCommand = true;
+ if ( fFunctionStartsForcedOn )
+ fFunctionStartsLoadCommand = true;
+ break;
case Options::kDynamicExecutable:
case Options::kDyld:
case Options::kDynamicLibrary:
// <rdar://problem/5366363> -r -x implies -S
if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) )
fDebugInfoStripping = Options::kDebugInfoNone;
-
+
+ // <rdar://problem/15252891> -r implies -no_uuid
+ if ( fOutputKind == Options::kObjectFile )
+ fUUIDMode = kUUIDNone;
+
// choose how to process unwind info
switch ( fArchitecture ) {
case CPU_TYPE_I386:
fMakeCompressedDyldInfo = false;
}
- // only ARM enforces that cpu-sub-types must match
- if ( fArchitecture != CPU_TYPE_ARM )
- fAllowCpuSubtypeMismatches = true;
+ // only ARM and x86_64 enforces that cpu-sub-types must match
+ switch ( fArchitecture ) {
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_X86_64:
+ break;
+ case CPU_TYPE_I386:
+ case CPU_TYPE_ARM64:
+ fAllowCpuSubtypeMismatches = true;
+ break;
+ }
+
// only final linked images can not optimize zero fill sections
if ( fOutputKind == Options::kObjectFile )
if ( fMacVersionMin >= ld::mac10_7 ) {
fTLVSupport = true;
}
-
+ else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= 0x00080000) ) {
+ fTLVSupport = true;
+ }
+
// default to adding version load command for dynamic code, static code must opt-in
switch ( fOutputKind ) {
case Options::kObjectFile:
// -dyld_env can only be used with main executables
if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) )
throw "-dyld_env can only used used when created main executables";
+
+ // -rename_sections can only be used in -r mode
+ if ( (fSectionRenames.size() != 0) && (fOutputKind != Options::kObjectFile) )
+ throw "-rename_sections can only used used in -r mode";
}
const char* alias;
};
+ struct SectionRename {
+ const char* fromSegment;
+ const char* fromSection;
+ const char* toSegment;
+ const char* toSection;
+ };
+
+
enum { depLinkerVersion=0x00, depObjectFile=0x10, depDirectDylib=0x10, depIndirectDylib=0x10,
depUpwardDirectDylib=0x10, depUpwardIndirectDylib=0x10, depArchive=0x10,
depFileList=0x10, depSection=0x10, depBundleLoader=0x10, depMisc=0x10, depNotFound=0x11,
bool objcCategoryMerging() const { return fObjcCategoryMerging; }
bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; }
bool keepDwarfUnwind() const { return fKeepDwarfUnwind; }
+ bool verboseOptimizationHints() const { return fVerboseOptimizationHints; }
+ bool ignoreOptimizationHints() const { return fIgnoreOptimizationHints; }
bool generateDtraceDOF() const { return fGenerateDtraceDOF; }
+ bool allowBranchIslands() const { return fAllowBranchIslands; }
bool hasWeakBitTweaks() const;
bool forceWeak(const char* symbolName) const;
bool forceNotWeak(const char* symbolName) const;
linkerOptions() const { return fLinkerOptions; }
FileInfo findFramework(const char* frameworkName) const;
FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const;
+ const std::vector<SectionRename>& sectionRenames() const { return fSectionRenames; }
private:
typedef std::unordered_map<const char*, unsigned int, ld::CStringHash, ld::CStringEquals> NameToOrder;
void warnObsolete(const char* arg);
uint32_t parseProtection(const char* prot);
void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping);
+ void addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection);
bool fKeepDwarfUnwind;
bool fKeepDwarfUnwindForcedOn;
bool fKeepDwarfUnwindForcedOff;
+ bool fVerboseOptimizationHints;
+ bool fIgnoreOptimizationHints;
bool fGenerateDtraceDOF;
+ bool fAllowBranchIslands;
DebugInfoStripping fDebugInfoStripping;
const char* fTraceOutputFile;
ld::MacVersionMin fMacVersionMin;
std::vector<const char*> fSDKPaths;
std::vector<const char*> fDyldEnvironExtras;
std::vector< std::vector<const char*> > fLinkerOptions;
+ std::vector<SectionRename> fSectionRenames;
bool fSaveTempFiles;
mutable Snapshot fLinkSnapshot;
bool fSnapshotRequested;
namespace ld {
namespace tool {
+uint32_t sAdrpNA = 0;
+uint32_t sAdrpNoped = 0;
+uint32_t sAdrpNotNoped = 0;
+
OutputFile::OutputFile(const Options& opts)
:
- hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false),
- _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), hasDataInCode(false),
+ usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false),
+ _noReExportedDylibs(false), pieDisabled(false), hasDataInCode(false),
headerAndLoadCommandsSection(NULL),
rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL),
lazyBindingSection(NULL), exportSection(NULL),
splitSegInfoSection(NULL), functionStartsSection(NULL),
- dataInCodeSection(NULL), dependentDRsSection(NULL),
+ dataInCodeSection(NULL), optimizationHintsSection(NULL), dependentDRsSection(NULL),
symbolTableSection(NULL), stringPoolSection(NULL),
localRelocationsSection(NULL), externalRelocationsSection(NULL),
sectionRelocationsSection(NULL),
_hasDynamicSymbolTable(true),
_hasLocalRelocations(!opts.makeCompressedDyldInfo()),
_hasExternalRelocations(!opts.makeCompressedDyldInfo()),
+ _hasOptimizationHints(opts.outputKind() == Options::kObjectFile),
_encryptedTEXTstartOffset(0),
_encryptedTEXTendOffset(0),
_localSymbolsStartIndex(0),
_splitSegInfoAtom(NULL),
_functionStartsAtom(NULL),
_dataInCodeAtom(NULL),
- _dependentDRInfoAtom(NULL)
+ _dependentDRInfoAtom(NULL),
+ _optimizationHintsAtom(NULL)
{
}
this->buildDylibOrdinalMapping(state);
this->addLoadCommands(state);
this->addLinkEdit(state);
- this->setSectionSizesAndAlignments(state);
+ state.setSectionSizesAndAlignments();
this->setLoadCommandsPadding(state);
- this->assignFileOffsets(state);
+ _fileSize = state.assignFileOffsets();
this->assignAtomAddresses(state);
this->synthesizeDebugNotes(state);
this->buildSymbolTable(state);
if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName());
for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
- if ( log ) fprintf(stderr, " atom=%p, name=%s\n", atom, atom->name());
switch ( sect-> type() ) {
case ld::Section::typeImportProxies:
// want finalAddress() of all proxy atoms to be zero
break;
default:
(const_cast<ld::Atom*>(atom))->setSectionStartAddress(sect->address);
+ if ( log ) fprintf(stderr, " atom=%p, addr=0x%08llX, name=%s\n", atom, atom->finalAddress(), atom->name());
break;
}
}
_dataInCodeAtom->encode();
}
+ if ( _hasOptimizationHints ) {
+ // build linker-optimization-hint info
+ assert(_optimizationHintsAtom != NULL);
+ _optimizationHintsAtom->encode();
+ }
+
if ( _options.needsDependentDRInfo() ) {
// build dependent dylib DR info
assert(_dependentDRInfoAtom != NULL);
_fileSize = state.sections.back()->fileOffset + state.sections.back()->size;
}
-void OutputFile::setSectionSizesAndAlignments(ld::Internal& state)
-{
- for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
- ld::Internal::FinalSection* sect = *sit;
- if ( sect->type() == ld::Section::typeAbsoluteSymbols ) {
- // absolute symbols need their finalAddress() to their value
- for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
- const ld::Atom* atom = *ait;
- (const_cast<ld::Atom*>(atom))->setSectionOffset(atom->objectAddress());
- }
- }
- else {
- uint16_t maxAlignment = 0;
- uint64_t offset = 0;
- for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
- const ld::Atom* atom = *ait;
- bool pagePerAtom = false;
- uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2;
- uint32_t atomModulus = atom->alignment().modulus;
- if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) {
- // most objc sections cannot be padded
- bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 );
- if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 )
- contiguousObjCSection = false;
- if ( strcmp(atom->section().sectionName(), "__objc_data") == 0 )
- contiguousObjCSection = false;
- switch ( atom->section().type() ) {
- case ld::Section::typeUnclassified:
- case ld::Section::typeTentativeDefs:
- case ld::Section::typeZeroFill:
- if ( contiguousObjCSection )
- break;
- pagePerAtom = true;
- if ( atomAlignmentPowerOf2 < 12 ) {
- atomAlignmentPowerOf2 = 12;
- atomModulus = 0;
- }
- break;
- default:
- break;
- }
- }
- if ( atomAlignmentPowerOf2 > maxAlignment )
- maxAlignment = atomAlignmentPowerOf2;
- // calculate section offset for this atom
- uint64_t alignment = 1 << atomAlignmentPowerOf2;
- uint64_t currentModulus = (offset % alignment);
- uint64_t requiredModulus = atomModulus;
- if ( currentModulus != requiredModulus ) {
- if ( requiredModulus > currentModulus )
- offset += requiredModulus-currentModulus;
- else
- offset += requiredModulus+alignment-currentModulus;
- }
- // LINKEDIT atoms are laid out later
- if ( sect->type() != ld::Section::typeLinkEdit ) {
- (const_cast<ld::Atom*>(atom))->setSectionOffset(offset);
- offset += atom->size();
- if ( pagePerAtom ) {
- offset = (offset + 4095) & (-4096); // round up to end of page
- }
- }
- if ( (atom->scope() == ld::Atom::scopeGlobal)
- && (atom->definition() == ld::Atom::definitionRegular)
- && (atom->combine() == ld::Atom::combineByName)
- && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn)
- || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) {
- this->hasWeakExternalSymbols = true;
- if ( _options.warnWeakExports() )
- warning("weak external symbol: %s", atom->name());
- }
- }
- sect->size = offset;
- // section alignment is that of a contained atom with the greatest alignment
- sect->alignment = maxAlignment;
- // unless -sectalign command line option overrides
- if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) )
- sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName());
- // each atom in __eh_frame has zero alignment to assure they pack together,
- // but compilers usually make the CFIs pointer sized, so we want whole section
- // to start on pointer sized boundary.
- if ( sect->type() == ld::Section::typeCFI )
- sect->alignment = 3;
- if ( sect->type() == ld::Section::typeTLVDefs )
- this->hasThreadLocalVariableDefinitions = true;
- }
- }
-}
void OutputFile::setLoadCommandsPadding(ld::Internal& state)
{
default:
// work backwards from end of segment and lay out sections so that extra room goes to padding atom
uint64_t addr = 0;
+ uint64_t textSegPageSize = _options.segPageSize("__TEXT");
for (std::vector<ld::Internal::FinalSection*>::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) {
ld::Internal::FinalSection* sect = *it;
if ( strcmp(sect->segmentName(), "__TEXT") != 0 )
continue;
if ( sect == headerAndLoadCommandsSection ) {
addr -= headerAndLoadCommandsSection->size;
- paddingSize = addr % _options.segmentAlignment();
+ paddingSize = addr % textSegPageSize;
break;
}
addr -= sect->size;
return ((addr+pageSize-1) & (-pageSize));
}
-
-void OutputFile::assignFileOffsets(ld::Internal& state)
-{
- const bool log = false;
- const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile)
- && (_options.outputKind() != Options::kPreload));
- const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile);
-
- uint64_t address = 0;
- const char* lastSegName = "";
- uint64_t floatingAddressStart = _options.baseAddress();
-
- // first pass, assign addresses to sections in segments with fixed start addresses
- if ( log ) fprintf(stderr, "Fixed address segments:\n");
- for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
- ld::Internal::FinalSection* sect = *it;
- if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) )
- continue;
- if ( segmentsArePageAligned ) {
- if ( strcmp(lastSegName, sect->segmentName()) != 0 ) {
- address = _options.customSegmentAddress(sect->segmentName());
- lastSegName = sect->segmentName();
- }
- }
- // adjust section address based on alignment
- uint64_t unalignedAddress = address;
- uint64_t alignment = (1 << sect->alignment);
- address = ( (unalignedAddress+alignment-1) & (-alignment) );
-
- // update section info
- sect->address = address;
- sect->alignmentPaddingBytes = (address - unalignedAddress);
-
- // sanity check size
- if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile)
- && (_options.outputKind() != Options::kStaticExecutable) )
- throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range",
- sect->sectionName(), address, sect->size);
-
- if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n",
- sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName());
- // update running totals
- if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
- address += sect->size;
-
- // if TEXT segment address is fixed, then flow other segments after it
- if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) {
- floatingAddressStart = address;
- }
- }
-
- // second pass, assign section address to sections in segments that are contiguous with previous segment
- address = floatingAddressStart;
- lastSegName = "";
- ld::Internal::FinalSection* overlappingFixedSection = NULL;
- ld::Internal::FinalSection* overlappingFlowSection = NULL;
- if ( log ) fprintf(stderr, "Regular layout segments:\n");
- for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
- ld::Internal::FinalSection* sect = *it;
- if ( _options.hasCustomSegmentAddress(sect->segmentName()) )
- continue;
- if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) {
- sect->alignmentPaddingBytes = 0;
- continue;
- }
- if ( segmentsArePageAligned ) {
- if ( strcmp(lastSegName, sect->segmentName()) != 0 ) {
- // round up size of last segment if needed
- if ( *lastSegName != '\0' ) {
- address = pageAlign(address, _options.segPageSize(lastSegName));
- }
- // set segment address based on end of last segment
- address = pageAlign(address);
- lastSegName = sect->segmentName();
- }
- }
- // adjust section address based on alignment
- uint64_t unalignedAddress = address;
- uint64_t alignment = (1 << sect->alignment);
- address = ( (unalignedAddress+alignment-1) & (-alignment) );
-
- // update section info
- sect->address = address;
- sect->alignmentPaddingBytes = (address - unalignedAddress);
-
- // sanity check size
- if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile)
- && (_options.outputKind() != Options::kStaticExecutable) )
- throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range",
- sect->sectionName(), address, sect->size);
-
- // sanity check it does not overlap a fixed address segment
- for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
- ld::Internal::FinalSection* otherSect = *sit;
- if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) )
- continue;
- if ( sect->address > otherSect->address ) {
- if ( (otherSect->address+otherSect->size) > sect->address ) {
- overlappingFixedSection = otherSect;
- overlappingFlowSection = sect;
- }
- }
- else {
- if ( (sect->address+sect->size) > otherSect->address ) {
- overlappingFixedSection = otherSect;
- overlappingFlowSection = sect;
- }
- }
- }
-
- if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n",
- sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes,
- sect->segmentName(), sect->sectionName());
- // update running totals
- if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
- address += sect->size;
- }
- if ( overlappingFixedSection != NULL ) {
- fprintf(stderr, "Section layout:\n");
- for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
- ld::Internal::FinalSection* sect = *it;
- if ( sect->isSectionHidden() )
- continue;
- fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n",
- sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes,
- sect->segmentName(), sect->sectionName());
-
- }
- throwf("Section (%s/%s) overlaps fixed address section (%s/%s)",
- overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(),
- overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName());
- }
-
-
- // third pass, assign section file offsets
- uint64_t fileOffset = 0;
- lastSegName = "";
- if ( log ) fprintf(stderr, "All segments with file offsets:\n");
- for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
- ld::Internal::FinalSection* sect = *it;
- if ( hasZeroForFileOffset(sect) ) {
- // fileoff of zerofill sections is moot, but historically it is set to zero
- sect->fileOffset = 0;
-
- // <rdar://problem/10445047> align file offset with address layout
- fileOffset += sect->alignmentPaddingBytes;
- }
- else {
- // page align file offset at start of each segment
- if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) {
- fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName));
- }
- lastSegName = sect->segmentName();
-
- // align file offset with address layout
- fileOffset += sect->alignmentPaddingBytes;
-
- // update section info
- sect->fileOffset = fileOffset;
-
- // update running total
- fileOffset += sect->size;
- }
-
- if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n",
- sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment,
- sect->segmentName(), sect->sectionName());
- }
-
-
- // for encrypted iPhoneOS apps
- if ( _options.makeEncryptable() ) {
- // remember end of __TEXT for later use by load command
- for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
- ld::Internal::FinalSection* sect = *it;
- if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) {
- _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size);
- }
- }
- }
-
- // remember total file size
- _fileSize = fileOffset;
-}
-
-
static const char* makeName(const ld::Atom& atom)
{
static char buffer[4096];
}
}
+bool OutputFile::checkArmBranch24Displacement(int64_t displacement)
+{
+ return ( (displacement < 33554428LL) && (displacement > (-33554432LL)) );
+}
void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup)
{
- if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) {
- // show layout of final image
- printSectionLayout(state);
+ if ( checkArmBranch24Displacement(displacement) )
+ return;
- const ld::Atom* target;
- throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)",
- displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup),
- addressOf(state, fixup, &target));
- }
+ // show layout of final image
+ printSectionLayout(state);
+
+ const ld::Atom* target;
+ throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)",
+ displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup),
+ addressOf(state, fixup, &target));
}
-void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup)
+bool OutputFile::checkThumbBranch22Displacement(int64_t displacement)
{
- // thumb2 supports a larger displacement
+ // thumb2 supports +/- 16MB displacement
if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) {
if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) {
- // show layout of final image
- printSectionLayout(state);
-
- const ld::Atom* target;
- throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)",
- displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup),
- addressOf(state, fixup, &target));
+ return false;
}
}
else {
+ // thumb1 supports +/- 4MB displacement
if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) {
- // show layout of final image
- printSectionLayout(state);
-
- const ld::Atom* target;
- throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)",
- displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup),
- addressOf(state, fixup, &target));
+ return false;
}
}
+ return true;
+}
+
+void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup)
+{
+ if ( checkThumbBranch22Displacement(displacement) )
+ return;
+
+ // show layout of final image
+ printSectionLayout(state);
+
+ const ld::Atom* target;
+ if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) {
+ throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)",
+ displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup),
+ addressOf(state, fixup, &target));
+ }
+ else {
+ throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)",
+ displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup),
+ addressOf(state, fixup, &target));
+ }
}
uint64_t OutputFile::get64BE(uint8_t* loc) { return BigEndian::get64(*(uint64_t*)loc); }
void OutputFile::set64BE(uint8_t* loc, uint64_t value) { BigEndian::set64(*(uint64_t*)loc, value); }
+static uint32_t makeNOP() {
+ return 0xD503201F;
+}
+
+enum SignExtension { signedNot, signed32, signed64 };
+struct LoadStoreInfo {
+ uint32_t reg;
+ uint32_t baseReg;
+ uint32_t offset; // after scaling
+ uint32_t size; // 1,2,4,8, or 16
+ bool isStore;
+ bool isFloat; // if destReg is FP/SIMD
+ SignExtension signEx; // if load is sign extended
+};
+
+static uint32_t makeLDR_literal(const LoadStoreInfo& info, uint64_t targetAddress, uint64_t instructionAddress)
+{
+ int64_t delta = targetAddress - instructionAddress;
+ assert(delta < 1024*1024);
+ assert(delta > -1024*1024);
+ assert((info.reg & 0xFFFFFFE0) == 0);
+ assert((targetAddress & 0x3) == 0);
+ assert((instructionAddress & 0x3) == 0);
+ assert(!info.isStore);
+ uint32_t imm19 = (delta << 3) & 0x00FFFFE0;
+ uint32_t instruction = 0;
+ switch ( info.size ) {
+ case 4:
+ if ( info.isFloat ) {
+ assert(info.signEx == signedNot);
+ instruction = 0x1C000000;
+ }
+ else {
+ if ( info.signEx == signed64 )
+ instruction = 0x98000000;
+ else
+ instruction = 0x18000000;
+ }
+ break;
+ case 8:
+ assert(info.signEx == signedNot);
+ instruction = info.isFloat ? 0x5C000000 : 0x58000000;
+ break;
+ case 16:
+ assert(info.signEx == signedNot);
+ instruction = 0x9C000000;
+ break;
+ default:
+ assert(0 && "invalid load size for literal");
+ }
+ return (instruction | imm19 | info.reg);
+}
+
+static uint32_t makeADR(uint32_t destReg, uint64_t targetAddress, uint64_t instructionAddress)
+{
+ assert((destReg & 0xFFFFFFE0) == 0);
+ assert((instructionAddress & 0x3) == 0);
+ uint32_t instruction = 0x10000000;
+ int64_t delta = targetAddress - instructionAddress;
+ assert(delta < 1024*1024);
+ assert(delta > -1024*1024);
+ uint32_t immhi = (delta & 0x001FFFFC) << 3;
+ uint32_t immlo = (delta & 0x00000003) << 29;
+ return (instruction | immhi | immlo | destReg);
+}
+
+static uint32_t makeLoadOrStore(const LoadStoreInfo& info)
+{
+ uint32_t instruction = 0x39000000;
+ if ( info.isFloat )
+ instruction |= 0x04000000;
+ instruction |= info.reg;
+ instruction |= (info.baseReg << 5);
+ uint32_t sizeBits = 0;
+ uint32_t opcBits = 0;
+ uint32_t imm12Bits = 0;
+ switch ( info.size ) {
+ case 1:
+ sizeBits = 0;
+ imm12Bits = info.offset;
+ if ( info.isStore ) {
+ opcBits = 0;
+ }
+ else {
+ switch ( info.signEx ) {
+ case signedNot:
+ opcBits = 1;
+ break;
+ case signed32:
+ opcBits = 3;
+ break;
+ case signed64:
+ opcBits = 2;
+ break;
+ }
+ }
+ break;
+ case 2:
+ sizeBits = 1;
+ assert((info.offset % 2) == 0);
+ imm12Bits = info.offset/2;
+ if ( info.isStore ) {
+ opcBits = 0;
+ }
+ else {
+ switch ( info.signEx ) {
+ case signedNot:
+ opcBits = 1;
+ break;
+ case signed32:
+ opcBits = 3;
+ break;
+ case signed64:
+ opcBits = 2;
+ break;
+ }
+ }
+ break;
+ case 4:
+ sizeBits = 2;
+ assert((info.offset % 4) == 0);
+ imm12Bits = info.offset/4;
+ if ( info.isStore ) {
+ opcBits = 0;
+ }
+ else {
+ switch ( info.signEx ) {
+ case signedNot:
+ opcBits = 1;
+ break;
+ case signed32:
+ assert(0 && "cannot use signed32 with 32-bit load/store");
+ break;
+ case signed64:
+ opcBits = 2;
+ break;
+ }
+ }
+ break;
+ case 8:
+ sizeBits = 3;
+ assert((info.offset % 8) == 0);
+ imm12Bits = info.offset/8;
+ if ( info.isStore ) {
+ opcBits = 0;
+ }
+ else {
+ opcBits = 1;
+ assert(info.signEx == signedNot);
+ }
+ break;
+ case 16:
+ sizeBits = 0;
+ assert((info.offset % 16) == 0);
+ imm12Bits = info.offset/16;
+ assert(info.isFloat);
+ if ( info.isStore ) {
+ opcBits = 2;
+ }
+ else {
+ opcBits = 3;
+ }
+ break;
+ default:
+ assert(0 && "bad load/store size");
+ break;
+ }
+ assert(imm12Bits < 4096);
+ return (instruction | (sizeBits << 30) | (opcBits << 22) | (imm12Bits << 10));
+}
+
+static bool parseLoadOrStore(uint32_t instruction, LoadStoreInfo& info)
+{
+ if ( (instruction & 0x3B000000) != 0x39000000 )
+ return false;
+ info.isFloat = ( (instruction & 0x04000000) != 0 );
+ info.reg = (instruction & 0x1F);
+ info.baseReg = ((instruction>>5) & 0x1F);
+ switch (instruction & 0xC0C00000) {
+ case 0x00000000:
+ info.size = 1;
+ info.isStore = true;
+ info.signEx = signedNot;
+ break;
+ case 0x00400000:
+ info.size = 1;
+ info.isStore = false;
+ info.signEx = signedNot;
+ break;
+ case 0x00800000:
+ if ( info.isFloat ) {
+ info.size = 16;
+ info.isStore = true;
+ info.signEx = signedNot;
+ }
+ else {
+ info.size = 1;
+ info.isStore = false;
+ info.signEx = signed64;
+ }
+ break;
+ case 0x00C00000:
+ if ( info.isFloat ) {
+ info.size = 16;
+ info.isStore = false;
+ info.signEx = signedNot;
+ }
+ else {
+ info.size = 1;
+ info.isStore = false;
+ info.signEx = signed32;
+ }
+ break;
+ case 0x40000000:
+ info.size = 2;
+ info.isStore = true;
+ info.signEx = signedNot;
+ break;
+ case 0x40400000:
+ info.size = 2;
+ info.isStore = false;
+ info.signEx = signedNot;
+ break;
+ case 0x40800000:
+ info.size = 2;
+ info.isStore = false;
+ info.signEx = signed64;
+ break;
+ case 0x40C00000:
+ info.size = 2;
+ info.isStore = false;
+ info.signEx = signed32;
+ break;
+ case 0x80000000:
+ info.size = 4;
+ info.isStore = true;
+ info.signEx = signedNot;
+ break;
+ case 0x80400000:
+ info.size = 4;
+ info.isStore = false;
+ info.signEx = signedNot;
+ break;
+ case 0x80800000:
+ info.size = 4;
+ info.isStore = false;
+ info.signEx = signed64;
+ break;
+ case 0xC0000000:
+ info.size = 8;
+ info.isStore = true;
+ info.signEx = signedNot;
+ break;
+ case 0xC0400000:
+ info.size = 8;
+ info.isStore = false;
+ info.signEx = signedNot;
+ break;
+ default:
+ return false;
+ }
+ info.offset = ((instruction >> 10) & 0x0FFF) * info.size;
+ return true;
+}
+
+struct AdrpInfo {
+ uint32_t destReg;
+};
+
+static bool parseADRP(uint32_t instruction, AdrpInfo& info)
+{
+ if ( (instruction & 0x9F000000) != 0x90000000 )
+ return false;
+ info.destReg = (instruction & 0x1F);
+ return true;
+}
+
+struct AddInfo {
+ uint32_t destReg;
+ uint32_t srcReg;
+ uint32_t addend;
+};
+
+static bool parseADD(uint32_t instruction, AddInfo& info)
+{
+ if ( (instruction & 0xFFC00000) != 0x91000000 )
+ return false;
+ info.destReg = (instruction & 0x1F);
+ info.srcReg = ((instruction>>5) & 0x1F);
+ info.addend = ((instruction>>10) & 0xFFF);
+ return true;
+}
+
+
+
+#if 0
+static uint32_t makeLDR_scaledOffset(const LoadStoreInfo& info)
+{
+ assert((info.reg & 0xFFFFFFE0) == 0);
+ assert((info.baseReg & 0xFFFFFFE0) == 0);
+ assert(!info.isFloat || (info.signEx != signedNot));
+ uint32_t sizeBits = 0;
+ uint32_t opcBits = 1;
+ uint32_t vBit = info.isFloat;
+ switch ( info.signEx ) {
+ case signedNot:
+ opcBits = 1;
+ break;
+ case signed32:
+ opcBits = 3;
+ break;
+ case signed64:
+ opcBits = 2;
+ break;
+ default:
+ assert(0 && "bad SignExtension runtime value");
+ }
+ switch ( info.size ) {
+ case 1:
+ sizeBits = 0;
+ break;
+ case 2:
+ sizeBits = 1;
+ break;
+ case 4:
+ sizeBits = 2;
+ break;
+ case 8:
+ sizeBits = 3;
+ break;
+ case 16:
+ sizeBits = 0;
+ vBit = 1;
+ opcBits = 3;
+ break;
+ default:
+ assert(0 && "invalid load size for literal");
+ }
+ assert((info.offset % info.size) == 0);
+ uint32_t scaledOffset = info.offset/info.size;
+ assert(scaledOffset < 4096);
+ return (0x39000000 | (sizeBits<<30) | (vBit<<26) | (opcBits<<22) | (scaledOffset<<10) | (info.baseReg<<5) | info.reg);
+}
+
+static uint32_t makeLDR_literal(uint32_t destReg, uint32_t loadSize, bool isFloat, uint64_t targetAddress, uint64_t instructionAddress)
+{
+ int64_t delta = targetAddress - instructionAddress;
+ assert(delta < 1024*1024);
+ assert(delta > -1024*1024);
+ assert((destReg & 0xFFFFFFE0) == 0);
+ assert((targetAddress & 0x3) == 0);
+ assert((instructionAddress & 0x3) == 0);
+ uint32_t imm19 = (delta << 3) & 0x00FFFFE0;
+ uint32_t instruction = 0;
+ switch ( loadSize ) {
+ case 4:
+ instruction = isFloat ? 0x1C000000 : 0x18000000;
+ break;
+ case 8:
+ instruction = isFloat ? 0x5C000000 : 0x58000000;
+ break;
+ case 16:
+ instruction = 0x9C000000;
+ break;
+ default:
+ assert(0 && "invalid load size for literal");
+ }
+ return (instruction | imm19 | destReg);
+}
+
+
+static bool ldrInfo(uint32_t instruction, uint8_t* size, uint8_t* destReg, bool* v, uint32_t* scaledOffset)
+{
+ *v = ( (instruction & 0x04000000) != 0 );
+ *destReg = (instruction & 0x1F);
+ uint32_t imm12 = ((instruction >> 10) & 0x00000FFF);
+ switch ( (instruction & 0xC0000000) >> 30 ) {
+ case 0:
+ // vector and byte LDR have same "size" bits, need to check other bits to differenciate
+ if ( (instruction & 0x00800000) == 0 ) {
+ *size = 1;
+ *scaledOffset = imm12;
+ }
+ else {
+ *size = 16;
+ *scaledOffset = imm12 * 16;
+ }
+ break;
+ case 1:
+ *size = 2;
+ *scaledOffset = imm12 * 2;
+ break;
+ case 2:
+ *size = 4;
+ *scaledOffset = imm12 * 4;
+ break;
+ case 3:
+ *size = 8;
+ *scaledOffset = imm12 * 8;
+ break;
+ }
+ return ((instruction & 0x3B400000) == 0x39400000);
+}
+#endif
+
+static bool withinOneMeg(uint64_t addr1, uint64_t addr2) {
+ int64_t delta = (addr2 - addr1);
+ return ( (delta < 1024*1024) && (delta > -1024*1024) );
+}
+
+void OutputFile::setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map<uint32_t, const Fixup*>& usedByHints,
+ uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info)
+{
+ info->offsetInAtom = offsetInAtom + delta;
+ std::map<uint32_t, const Fixup*>::const_iterator pos = usedByHints.find(info->offsetInAtom);
+ if ( (pos != usedByHints.end()) && (pos->second != NULL) ) {
+ info->fixup = pos->second;
+ info->targetAddress = addressOf(state, info->fixup, &info->target);
+ if ( info->fixup->clusterSize != ld::Fixup::k1of1 ) {
+ assert(info->fixup->firstInCluster());
+ const ld::Fixup* nextFixup = info->fixup + 1;
+ if ( nextFixup->kind == ld::Fixup::kindAddAddend ) {
+ info->targetAddress += nextFixup->u.addend;
+ }
+ else {
+ assert(0 && "expected addend");
+ }
+ }
+ }
+ else {
+ info->fixup = NULL;
+ info->targetAddress = 0;
+ info->target = NULL;
+ }
+ info->instructionContent = &buffer[info->offsetInAtom];
+ info->instructionAddress = atom->finalAddress() + info->offsetInAtom;
+ info->instruction = get32LE(info->instructionContent);
+}
+
+static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false)
+{
+ if ( fixup == NULL )
+ return false;
+ const ld::Fixup* f;
+ switch ( fixup->kind ) {
+ case ld::Fixup::kindStoreTargetAddressARM64Page21:
+ return !mustBeGOT;
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
+ return true;
+ case ld::Fixup::kindSetTargetAddress:
+ f = fixup;
+ do {
+ ++f;
+ } while ( ! f->lastInCluster() );
+ switch (f->kind ) {
+ case ld::Fixup::kindStoreARM64Page21:
+ return !mustBeGOT;
+ case ld::Fixup::kindStoreARM64GOTLoadPage21:
+ case ld::Fixup::kindStoreARM64GOTLeaPage21:
+ return true;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false)
+{
+ if ( fixup == NULL )
+ return false;
+ const ld::Fixup* f;
+ switch ( fixup->kind ) {
+ case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
+ return !mustBeGOT;
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12:
+ return true;
+ case ld::Fixup::kindSetTargetAddress:
+ f = fixup;
+ do {
+ ++f;
+ } while ( ! f->lastInCluster() );
+ switch (f->kind ) {
+ case ld::Fixup::kindStoreARM64PageOff12:
+ return !mustBeGOT;
+ case ld::Fixup::kindStoreARM64GOTLoadPageOff12:
+ case ld::Fixup::kindStoreARM64GOTLeaPageOff12:
+ return true;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+
+#define LOH_ASSERT(cond) \
+ if ( !(cond) ) { \
+ warning("ignoring linker optimzation hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \
+ break; \
+ }
+
+
void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer)
{
//fprintf(stderr, "applyFixUps() on %s\n", atom->name());
bool is_blx;
bool is_b;
bool thumbTarget = false;
+ std::map<uint32_t, const Fixup*> usedByHints;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
uint8_t* fixUpLocation = &buffer[fit->offsetInAtom];
+ ld::Fixup::LOH_arm64 lohExtra;
switch ( (ld::Fixup::Kind)(fit->kind) ) {
case ld::Fixup::kindNone:
case ld::Fixup::kindNoneFollowOn:
}
break;
case ld::Fixup::kindLazyTarget:
+ case ld::Fixup::kindIslandTarget:
break;
case ld::Fixup::kindSetLazyOffset:
assert(fit->binding == ld::Fixup::bindingDirectlyBound);
case ld::Fixup::kindDataInCodeStartJTA32:
case ld::Fixup::kindDataInCodeEnd:
break;
+ case ld::Fixup::kindLinkerOptimizationHint:
+ // expand table of address/offsets used by hints
+ lohExtra.addend = fit->u.addend;
+ usedByHints[fit->offsetInAtom + (lohExtra.info.delta1 << 2)] = NULL;
+ if ( lohExtra.info.count > 0 )
+ usedByHints[fit->offsetInAtom + (lohExtra.info.delta2 << 2)] = NULL;
+ if ( lohExtra.info.count > 1 )
+ usedByHints[fit->offsetInAtom + (lohExtra.info.delta3 << 2)] = NULL;
+ if ( lohExtra.info.count > 2 )
+ usedByHints[fit->offsetInAtom + (lohExtra.info.delta4 << 2)] = NULL;
+ break;
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
accumulator = addressOf(state, fit, &toTarget);
thumbTarget = targetIsThumb(state, fit);
case ld::Fixup::kindStoreTargetAddressARMBranch24:
accumulator = addressOf(state, fit, &toTarget);
thumbTarget = targetIsThumb(state, fit);
+ if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) {
+ // Branching to island. If ultimate target is in range, branch there directly.
+ for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); islandfit != end; ++islandfit) {
+ if ( islandfit->kind == ld::Fixup::kindIslandTarget ) {
+ const ld::Atom* islandTarget = NULL;
+ uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget);
+ delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4);
+ if ( checkArmBranch24Displacement(delta) ) {
+ toTarget = islandTarget;
+ accumulator = islandTargetAddress;
+ thumbTarget = targetIsThumb(state, islandfit);
+ }
+ break;
+ }
+ }
+ }
if ( thumbTarget )
accumulator |= 1;
if ( fit->contentDetlaToAddendOnly )
delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8);
rangeCheckARMBranch24(delta, state, atom, fit);
instruction = get32LE(fixUpLocation);
- // Make sure we are calling arm with bl, thumb with blx
+ // Make sure we are calling arm with bl, thumb with blx
is_bl = ((instruction & 0xFF000000) == 0xEB000000);
is_blx = ((instruction & 0xFE000000) == 0xFA000000);
is_b = !is_blx && ((instruction & 0x0F000000) == 0x0A000000);
- if ( is_bl && thumbTarget ) {
- uint32_t opcode = 0xFA000000;
+ if ( (is_bl | is_blx) && thumbTarget ) {
+ uint32_t opcode = 0xFA000000; // force to be blx
uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF;
uint32_t h_bit = (uint32_t)(delta << 23) & 0x01000000;
newInstruction = opcode | h_bit | disp;
}
- else if ( is_blx && !thumbTarget ) {
- uint32_t opcode = 0xEB000000;
+ else if ( (is_bl | is_blx) && !thumbTarget ) {
+ uint32_t opcode = 0xEB000000; // force to be bl
uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF;
newInstruction = opcode | disp;
}
case ld::Fixup::kindStoreTargetAddressThumbBranch22:
accumulator = addressOf(state, fit, &toTarget);
thumbTarget = targetIsThumb(state, fit);
+ if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) {
+ // branching to island, so see if ultimate target is in range
+ // and if so branch to ultimate target instead.
+ for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); islandfit != end; ++islandfit) {
+ if ( islandfit->kind == ld::Fixup::kindIslandTarget ) {
+ const ld::Atom* islandTarget = NULL;
+ uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget);
+ delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4);
+ if ( checkThumbBranch22Displacement(delta) ) {
+ toTarget = islandTarget;
+ accumulator = islandTargetAddress;
+ thumbTarget = targetIsThumb(state, islandfit);
+ }
+ break;
+ }
+ }
+ }
if ( thumbTarget )
accumulator |= 1;
if ( fit->contentDetlaToAddendOnly )
case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
case ld::Fixup::kindStoreTargetAddressARM64Page21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21:
accumulator = addressOf(state, fit, &toTarget);
// fall into kindStoreARM64Branch26 case
case ld::Fixup::kindStoreARM64GOTLeaPage21:
case ld::Fixup::kindStoreARM64GOTLoadPage21:
case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21:
case ld::Fixup::kindStoreARM64Page21:
{
// the ADRP instruction adds the imm << 12 to the page that the pc is on
break;
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
accumulator = addressOf(state, fit, &toTarget);
// fall into kindAddressARM64PageOff12 case
case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
set32LE(fixUpLocation, newInstruction);
}
break;
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindStoreARM64TLVPLeaPageOff12 case
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12:
+ {
+ // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD
+ instruction = get32LE(fixUpLocation);
+ if ( (instruction & 0xFFC00000) != 0xF9400000 )
+ throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name());
+ uint32_t offset = accumulator & 0x00000FFF;
+ uint32_t imm12 = offset << 10;
+ newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF);
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
case ld::Fixup::kindStoreARM64PointerToGOT:
set64LE(fixUpLocation, accumulator);
break;
#endif
}
}
+
+ // after all fixups are done on atom, if there are potential optimizations, do those
+ if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) {
+ // fill in second part of usedByHints map, so we can see the target of fixups that might be optimized
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindLinkerOptimizationHint:
+ case ld::Fixup::kindNoneFollowOn:
+ case ld::Fixup::kindNoneGroupSubordinate:
+ case ld::Fixup::kindNoneGroupSubordinateFDE:
+ case ld::Fixup::kindNoneGroupSubordinateLSDA:
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ break;
+ default:
+ if ( fit->firstInCluster() ) {
+ std::map<uint32_t, const Fixup*>::iterator pos = usedByHints.find(fit->offsetInAtom);
+ if ( pos != usedByHints.end() ) {
+ assert(pos->second == NULL && "two fixups in same hint location");
+ pos->second = fit;
+ //fprintf(stderr, "setting %s usedByHints[0x%04X], kind = %d\n", atom->name(), fit->offsetInAtom, fit->kind);
+ }
+ }
+ }
+ }
+
+ // apply hints pass 1
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint )
+ continue;
+ InstructionInfo infoA;
+ InstructionInfo infoB;
+ InstructionInfo infoC;
+ InstructionInfo infoD;
+ LoadStoreInfo ldrInfoB, ldrInfoC;
+ AddInfo addInfoB;
+ AdrpInfo adrpInfoA;
+ bool usableSegment;
+ bool targetFourByteAligned;
+ bool literalableSize, isADRP, isADD, isLDR, isSTR;
+ //uint8_t loadSize, destReg;
+ //uint32_t scaledOffset;
+ //uint32_t imm12;
+ ld::Fixup::LOH_arm64 alt;
+ alt.addend = fit->u.addend;
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA);
+ if ( alt.info.count > 0 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB);
+ if ( alt.info.count > 1 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC);
+ if ( alt.info.count > 2 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD);
+
+ switch ( alt.info.kind ) {
+ case LOH_ARM64_ADRP_ADRP:
+ // processed in pass 2 beacuse some ADRP may have been removed
+ break;
+ case LOH_ARM64_ADRP_LDR:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg);
+ LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF));
+ literalableSize = ( (ldrInfoB.size != 1) && (ldrInfoB.size != 2) );
+ targetFourByteAligned = ( (infoA.targetAddress & 0x3) == 0 );
+ if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr at 0x%08llX not transformed, isLDR=%d, literalableSize=%d, inRange=%d, usableSegment=%d, scaledOffset=%d\n",
+ infoB.instructionAddress, isLDR, literalableSize, withinOneMeg(infoB.instructionAddress, infoA.targetAddress), usableSegment, ldrInfoB.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD_LDR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ targetFourByteAligned = ( ((infoB.targetAddress+ldrInfoC.offset) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can do T1 transformation to LDR literal
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress+ldrInfoC.offset, infoC.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
+ }
+ }
+ else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can to T4 transformation and turn ADRP/ADD into ADR
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF);
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoB.instructionAddress);
+ }
+ else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transformation by merging ADD into LD
+ // Leave ADRP as-is
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset += addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX could not be transformed, loadSize=%d, literalableSize=%d, inRange=%d, usableSegment=%d, targetFourByteAligned=%d, imm12=%d\n",
+ infoC.instructionAddress, ldrInfoC.size, literalableSize, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, targetFourByteAligned, ldrInfoC.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) {
+ // can do T4 transformation and use ADR
+ set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add at 0x%08llX transformed to ADR\n", infoB.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add at 0x%08llX not transformed, isAdd=%d, inRange=%d, usableSegment=%d\n",
+ infoB.instructionAddress, isADD, withinOneMeg(infoA.targetAddress, infoA.instructionAddress), usableSegment);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_LDR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(ldrInfoC.offset == 0);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ if ( isLDR ) {
+ // target of GOT is external
+ LOH_ASSERT(ldrInfoB.size == 8);
+ LOH_ASSERT(!ldrInfoB.isFloat);
+ LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
+ //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T5 transformed to LDR literal of GOT plus LDR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX no optimization done\n", infoC.instructionAddress);
+ }
+ }
+ else if ( isADD ) {
+ // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target
+ LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) {
+ // can do T1 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
+ }
+ else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress);
+ }
+ }
+ else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) {
+ // can do T2 transform
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.baseReg = adrpInfoA.destReg;
+ ldrInfoC.offset = addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ // T3 transform already done by ld::passes:got:doPass()
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T3 transformed to ADRP/ADD/LDR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX not ADD or LDR\n", infoC.instructionAddress);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD_STR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
+ LOH_ASSERT(isSTR);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can to T4 transformation and turn ADRP/ADD into ADR
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF);
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX T4 transformed to ADR/STR\n", infoB.instructionAddress);
+ }
+ else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transformation by merging ADD into STR
+ // Leave ADRP as-is
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset += addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX T2 transformed to ADRP/STR \n", infoC.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX could not be transformed, loadSize=%d, inRange=%d, usableSegment=%d, imm12=%d\n",
+ infoC.instructionAddress, ldrInfoC.size, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, ldrInfoC.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_STR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
+ LOH_ASSERT(isSTR);
+ LOH_ASSERT(ldrInfoC.offset == 0);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ if ( isLDR ) {
+ // target of GOT is external
+ LOH_ASSERT(ldrInfoB.size == 8);
+ LOH_ASSERT(!ldrInfoB.isFloat);
+ LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T5 transformed to LDR literal of GOT plus STR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX no optimization done\n", infoC.instructionAddress);
+ }
+ }
+ else if ( isADD ) {
+ // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target
+ LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress);
+ }
+ }
+ else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transform
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.baseReg = adrpInfoA.destReg;
+ ldrInfoC.offset = addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ // T3 transform already done by ld::passes:got:doPass()
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T3 transformed to ADRP/ADD/STR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX not ADD or LDR\n", infoC.instructionAddress);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ if ( isADRP ) {
+ if ( isLDR ) {
+ if ( usableSegment && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform (LDR literal load of GOT)
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX T5 transformed to NOP/LDR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else if ( isADD ) {
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform (ADR to compute local address)
+ set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX not LDR or ADD\n", infoB.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX not ADRP\n", infoA.instructionAddress);
+ }
+ break;
+ default:
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "unknown hint kind %d alt.info.kind at 0x%08llX\n", alt.info.kind, infoA.instructionAddress);
+ break;
+ }
+ }
+ // apply hints pass 2
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint )
+ continue;
+ InstructionInfo infoA;
+ InstructionInfo infoB;
+ ld::Fixup::LOH_arm64 alt;
+ alt.addend = fit->u.addend;
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA);
+ if ( alt.info.count > 0 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB);
+
+ switch ( alt.info.kind ) {
+ case LOH_ARM64_ADRP_ADRP:
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageKind(infoB.fixup));
+ if ( (infoA.instruction & 0x9F000000) != 0x90000000 ) {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoA.instructionAddress, infoA.instruction);
+ sAdrpNA++;
+ break;
+ }
+ if ( (infoB.instruction & 0x9F000000) != 0x90000000 ) {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoB.instructionAddress, infoA.instruction);
+ sAdrpNA++;
+ break;
+ }
+ if ( (infoA.targetAddress & (-4096)) == (infoB.targetAddress & (-4096)) ) {
+ set32LE(infoB.instructionContent, 0xD503201F);
+ sAdrpNoped++;
+ }
+ else {
+ sAdrpNotNoped++;
+ }
+ break;
+ }
+ }
+ }
+
+
+
+
}
void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb)
}
}
}
+
+ if ( _options.verboseOptimizationHints() ) {
+ //fprintf(stderr, "ADRP optimized away: %d\n", sAdrpNA);
+ //fprintf(stderr, "ADRPs changed to NOPs: %d\n", sAdrpNoped);
+ //fprintf(stderr, "ADRPs unchanged: %d\n", sAdrpNotNoped);
+ }
}
_dataInCodeAtom = new DataInCodeAtom<x86>(_options, state, *this);
dataInCodeSection = state.addAtom(*_dataInCodeAtom);
}
+ if ( _hasOptimizationHints ) {
+ _optimizationHintsAtom = new OptimizationHintsAtom<x86>(_options, state, *this);
+ optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
+ }
if ( _hasDependentDRInfo ) {
_dependentDRInfoAtom = new DependentDRAtom<x86>(_options, state, *this);
dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
_dataInCodeAtom = new DataInCodeAtom<x86_64>(_options, state, *this);
dataInCodeSection = state.addAtom(*_dataInCodeAtom);
}
+ if ( _hasOptimizationHints ) {
+ _optimizationHintsAtom = new OptimizationHintsAtom<x86_64>(_options, state, *this);
+ optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
+ }
if ( _hasDependentDRInfo ) {
_dependentDRInfoAtom = new DependentDRAtom<x86_64>(_options, state, *this);
dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
_dataInCodeAtom = new DataInCodeAtom<arm>(_options, state, *this);
dataInCodeSection = state.addAtom(*_dataInCodeAtom);
}
+ if ( _hasOptimizationHints ) {
+ _optimizationHintsAtom = new OptimizationHintsAtom<arm>(_options, state, *this);
+ optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
+ }
if ( _hasDependentDRInfo ) {
_dependentDRInfoAtom = new DependentDRAtom<arm>(_options, state, *this);
dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
_dataInCodeAtom = new DataInCodeAtom<arm64>(_options, state, *this);
dataInCodeSection = state.addAtom(*_dataInCodeAtom);
}
+ if ( _hasOptimizationHints ) {
+ _optimizationHintsAtom = new OptimizationHintsAtom<arm64>(_options, state, *this);
+ optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
+ }
if ( _hasDependentDRInfo ) {
_dependentDRInfoAtom = new DependentDRAtom<arm64>(_options, state, *this);
dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
}
-
-
-
void OutputFile::generateLinkEditInfo(ld::Internal& state)
{
for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
+ // record end of last __TEXT section encrypted iPhoneOS apps.
+ if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) ) {
+ _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size);
+ }
bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer)
&& (strcmp(sect->sectionName(), "__cls_refs") == 0)
&& (strcmp(sect->segmentName(), "__OBJC") == 0) );
if ( target == NULL )
return;
- bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 );
+ bool inReadOnlySeg = ((_options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE) == 0);
bool needsRebase = false;
bool needsBinding = false;
bool needsLazyBinding = false;
uint64_t fileSize() const { return _fileSize; }
- bool hasWeakExternalSymbols;
bool usesWeakExternalSymbols;
bool overridesWeakExternalSymbols;
bool _noReExportedDylibs;
- bool hasThreadLocalVariableDefinitions;
bool pieDisabled;
bool hasDataInCode;
ld::Internal::FinalSection* headerAndLoadCommandsSection;
ld::Internal::FinalSection* splitSegInfoSection;
ld::Internal::FinalSection* functionStartsSection;
ld::Internal::FinalSection* dataInCodeSection;
+ ld::Internal::FinalSection* optimizationHintsSection;
ld::Internal::FinalSection* dependentDRsSection;
ld::Internal::FinalSection* symbolTableSection;
ld::Internal::FinalSection* stringPoolSection;
void generateLinkEditInfo(ld::Internal& state);
void buildSymbolTable(ld::Internal& state);
void writeOutputFile(ld::Internal& state);
- void assignFileOffsets(ld::Internal& state);
- void setSectionSizesAndAlignments(ld::Internal& state);
void addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect,
const ld::Atom* atom, ld::Fixup* fixupWithTarget,
ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithAddend,
bool hasZeroForFileOffset(const ld::Section* sect);
void printSectionLayout(ld::Internal& state);
+
+ bool checkThumbBranch22Displacement(int64_t displacement);
+ bool checkArmBranch24Displacement(int64_t displacement);
+
void rangeCheck8(int64_t delta, ld::Internal& state, const ld::Atom* atom,
const ld::Fixup* fixup);
void rangeCheck16(int64_t delta, ld::Internal& state, const ld::Atom* atom,
void noteTextReloc(const ld::Atom* atom, const ld::Atom* target);
+ struct InstructionInfo {
+ uint32_t offsetInAtom;
+ const ld::Fixup* fixup;
+ const ld::Atom* target;
+ uint64_t targetAddress;
+ uint8_t* instructionContent;
+ uint64_t instructionAddress;
+ uint32_t instruction;
+ };
+
+ void setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map<uint32_t, const Fixup*>& usedHints,
+ uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info);
+
static uint16_t get16LE(uint8_t* loc);
static void set16LE(uint8_t* loc, uint16_t value);
static uint32_t get32LE(uint8_t* loc);
bool _hasDynamicSymbolTable;
bool _hasLocalRelocations;
bool _hasExternalRelocations;
+ bool _hasOptimizationHints;
uint64_t _fileSize;
std::map<uint64_t, uint32_t> _lazyPointerAddressToInfoOffset;
uint32_t _encryptedTEXTstartOffset;
class LinkEditAtom* _functionStartsAtom;
class LinkEditAtom* _dataInCodeAtom;
class LinkEditAtom* _dependentDRInfoAtom;
+ class LinkEditAtom* _optimizationHintsAtom;
};
} // namespace tool
break;
case CPU_TYPE_X86_64:
- _internal.cpuSubType = CPU_SUBTYPE_X86_64_ALL;
+ if ( _options.subArchitecture() != nextObjectSubType ) {
+ if ( _options.allowSubArchitectureMismatches() ) {
+ warning("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)",
+ file.path(), nextObjectSubType, _options.subArchitecture());
+ }
+ else {
+ throwf("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)",
+ file.path(), nextObjectSubType, _options.subArchitecture());
+ }
+ }
break;
}
}
void Resolver::doAtom(const ld::Atom& atom)
{
- //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s\n", &atom, atom.name(), atom.section().sectionName());
+ //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s, scope=%d\n", &atom, atom.name(), atom.section().sectionName(), atom.scope());
+ if ( _ltoCodeGenFinished && (atom.contentType() == ld::Atom::typeLTOtemporary) && (atom.scope() != ld::Atom::scopeTranslationUnit) )
+ warning("'%s' is implemented in bitcode, but it was loaded too late", atom.name());
// add to list of known atoms
_atoms.push_back(&atom);
const ld::Atom* dummy;
ld::Fixup::iterator end = atom.fixupsEnd();
for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != end; ++fit) {
+ if ( fit->kind == ld::Fixup::kindLinkerOptimizationHint )
+ _internal.someObjectHasOptimizationHints = true;
switch ( fit->binding ) {
case ld::Fixup::bindingByNameUnbound:
if ( isDtraceProbe(fit->kind) && (_options.outputKind() != Options::kObjectFile ) ) {
std::vector<const char*> undefineNames;
_symbolTable.undefines(undefineNames);
for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) {
- // make proxy
- this->doAtom(*new UndefinedProxyAtom(*it));
+ const char* undefName = *it;
+ // <rdar://problem/14547001> "ld -r -exported_symbol _foo" has wrong error message if _foo is undefined
+ bool makeProxy = true;
+ if ( (_options.outputKind() == Options::kObjectFile) && _options.hasExportMaskList() && _options.shouldExport(undefName) )
+ makeProxy = false;
+
+ if ( makeProxy )
+ this->doAtom(*new UndefinedProxyAtom(undefName));
}
}
if ( _haveLLVMObjs && !force ) {
// <rdar://problem/9777977> don't remove combinable atoms, they may come back in lto output
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end());
+ _symbolTable.removeDeadAtoms();
}
else {
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end());
if ( ! _haveLLVMObjs )
return;
+ // <rdar://problem/15314161> LTO: Symbol multiply defined error should specify exactly where the symbol is found
+ _symbolTable.checkDuplicateSymbols();
+
// run LLVM lto code-gen
lto::OptimizeOptions optOpt;
optOpt.outputFilePath = _options.outputFilePath();
std::vector<const char*> additionalUndefines;
if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) )
return; // if nothing done
-
+ _ltoCodeGenFinished = true;
// add all newly created atoms to _atoms and update symbol table
for(std::vector<const ld::Atom*>::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it)
this->doAtom(**it);
-
+
// some atoms might have been optimized way (marked coalesced), remove them
this->removeCoalescedAwayAtoms();
aliasAtom->setFinalAliasOf();
}
+ // <rdar://problem/14609792> add any auto-link libraries requested by LTO output to dylibs to search
+ _inputFiles.addLinkerOptionLibraries(_internal);
+ _inputFiles.createIndirectDylibs();
+
// resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up)
for(std::vector<const char*>::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) {
const char *targetName = *uit;
}
else {
// last chance to check for undefines
+ this->resolveUndefines();
this->checkUndefines(true);
// check new code does not override some dylib
}
}
+void Resolver::dumpAtoms()
+{
+ fprintf(stderr, "Resolver all atoms:\n");
+ for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
+ const ld::Atom* atom = *it;
+ fprintf(stderr, " %p name=%s, def=%d\n", atom, atom->name(), atom->definition());
+ }
+}
void Resolver::resolve()
{
: _options(opts), _inputFiles(inputs), _internal(state),
_symbolTable(opts, state.indirectBindingTable),
_haveLLVMObjs(false),
- _completedInitialObjectFiles(false) {}
+ _completedInitialObjectFiles(false),
+ _ltoCodeGenFinished(false) {}
virtual void doAtom(const ld::Atom&);
bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot);
void tweakWeakness();
void doLinkerOption(const std::vector<const char*>& linkerOption, const char* fileName);
+ void dumpAtoms();
typedef std::unordered_set<const char*, CStringHash, CStringEquals> StringSet;
SymbolTable _symbolTable;
bool _haveLLVMObjs;
bool _completedInitialObjectFiles;
+ bool _ltoCodeGenFinished;
};
return slot;
}
+void SymbolTable::removeDeadAtoms()
+{
+ for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) {
+ IndirectBindingSlot slot = it->second;
+ const ld::Atom* atom = _indirectBindingTable[slot];
+ if ( atom != NULL ) {
+ if ( !atom->live() && !atom->dontDeadStrip() ) {
+ //fprintf(stderr, "removing from symbolTable[%u] %s\n", slot, atom->name());
+ _indirectBindingTable[slot] = NULL;
+ }
+ }
+ }
+}
// find existing or create new slot
SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom)
unsigned int updateCount() { return _indirectBindingTable.size(); }
void undefines(std::vector<const char*>& undefines);
void tentativeDefs(std::vector<const char*>& undefines);
+ void removeDeadAtoms();
bool hasName(const char* name);
bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; }
byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); }
};
-
-
-
class InternalState : public ld::Internal
{
public:
virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom);
virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&);
+ uint64_t assignFileOffsets();
+ void setSectionSizesAndAlignments();
void sortSections();
void markAtomsOrdered() { _atomsOrderedInSections = true; }
virtual ~InternalState() {}
FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile);
static int sectionComparer(const void* l, const void* r);
static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill);
- static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal);
+ static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&);
private:
friend class InternalState;
static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen);
static ld::Section _s_DATA_zerofill;
};
+ bool hasZeroForFileOffset(const ld::Section* sect);
+ uint64_t pageAlign(uint64_t addr);
+ uint64_t pageAlign(uint64_t addr, uint64_t pageSize);
struct SectionHash {
size_t operator()(const ld::Section*) const;
return sect;
}
-const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal)
+const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, const Options& options)
{
+ const std::vector<Options::SectionRename>& renames = options.sectionRenames();
+ for ( std::vector<Options::SectionRename>::const_iterator it=renames.begin(); it != renames.end(); ++it) {
+ if ( (strcmp(sect.sectionName(), it->fromSection) == 0) && (strcmp(sect.segmentName(), it->fromSegment) == 0) ) {
+ ld::Section* s = new ld::Section(it->toSegment, it->toSection, sect.type());
+ return *s;
+ }
+ }
+
+
// in -r mode the only section that ever changes is __tenative -> __common with -d option
- if ( (sect.type() == ld::Section::typeTentativeDefs) && makeTentativeDefsReal)
+ if ( (sect.type() == ld::Section::typeTentativeDefs) && options.makeTentativeDefinitionsReal())
return _s_DATA_common;
return sect;
}
}
break;
case Options::kObjectFile:
- baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options.makeTentativeDefinitionsReal());
+ baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options);
pos = _sectionInToFinalMap.find(baseForFinalSection);
if ( pos != _sectionInToFinalMap.end() ) {
_sectionInToFinalMap[&inputSection] = pos->second;
}
+
+bool InternalState::hasZeroForFileOffset(const ld::Section* sect)
+{
+ switch ( sect->type() ) {
+ case ld::Section::typeZeroFill:
+ case ld::Section::typeTLVZeroFill:
+ return _options.optimizeZeroFill();
+ case ld::Section::typePageZero:
+ case ld::Section::typeStack:
+ case ld::Section::typeTentativeDefs:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+uint64_t InternalState::pageAlign(uint64_t addr)
+{
+ const uint64_t alignment = _options.segmentAlignment();
+ return ((addr+alignment-1) & (-alignment));
+}
+
+uint64_t InternalState::pageAlign(uint64_t addr, uint64_t pageSize)
+{
+ return ((addr+pageSize-1) & (-pageSize));
+}
+
+void InternalState::setSectionSizesAndAlignments()
+{
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = sections.begin(); sit != sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() == ld::Section::typeAbsoluteSymbols ) {
+ // absolute symbols need their finalAddress() to their value
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ (const_cast<ld::Atom*>(atom))->setSectionOffset(atom->objectAddress());
+ }
+ }
+ else {
+ uint16_t maxAlignment = 0;
+ uint64_t offset = 0;
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ bool pagePerAtom = false;
+ uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2;
+ uint32_t atomModulus = atom->alignment().modulus;
+ if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) {
+ // most objc sections cannot be padded
+ bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 );
+ if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 )
+ contiguousObjCSection = false;
+ if ( strcmp(atom->section().sectionName(), "__objc_data") == 0 )
+ contiguousObjCSection = false;
+ switch ( atom->section().type() ) {
+ case ld::Section::typeUnclassified:
+ case ld::Section::typeTentativeDefs:
+ case ld::Section::typeZeroFill:
+ if ( contiguousObjCSection )
+ break;
+ pagePerAtom = true;
+ if ( atomAlignmentPowerOf2 < 12 ) {
+ atomAlignmentPowerOf2 = 12;
+ atomModulus = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ( atomAlignmentPowerOf2 > maxAlignment )
+ maxAlignment = atomAlignmentPowerOf2;
+ // calculate section offset for this atom
+ uint64_t alignment = 1 << atomAlignmentPowerOf2;
+ uint64_t currentModulus = (offset % alignment);
+ uint64_t requiredModulus = atomModulus;
+ if ( currentModulus != requiredModulus ) {
+ if ( requiredModulus > currentModulus )
+ offset += requiredModulus-currentModulus;
+ else
+ offset += requiredModulus+alignment-currentModulus;
+ }
+ // LINKEDIT atoms are laid out later
+ if ( sect->type() != ld::Section::typeLinkEdit ) {
+ (const_cast<ld::Atom*>(atom))->setSectionOffset(offset);
+ offset += atom->size();
+ if ( pagePerAtom ) {
+ offset = (offset + 4095) & (-4096); // round up to end of page
+ }
+ }
+ if ( (atom->scope() == ld::Atom::scopeGlobal)
+ && (atom->definition() == ld::Atom::definitionRegular)
+ && (atom->combine() == ld::Atom::combineByName)
+ && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn)
+ || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) {
+ this->hasWeakExternalSymbols = true;
+ if ( _options.warnWeakExports() )
+ warning("weak external symbol: %s", atom->name());
+ }
+ }
+ sect->size = offset;
+ // section alignment is that of a contained atom with the greatest alignment
+ sect->alignment = maxAlignment;
+ // unless -sectalign command line option overrides
+ if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) )
+ sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName());
+ // each atom in __eh_frame has zero alignment to assure they pack together,
+ // but compilers usually make the CFIs pointer sized, so we want whole section
+ // to start on pointer sized boundary.
+ if ( sect->type() == ld::Section::typeCFI )
+ sect->alignment = 3;
+ if ( sect->type() == ld::Section::typeTLVDefs )
+ this->hasThreadLocalVariableDefinitions = true;
+ }
+ }
+}
+
+uint64_t InternalState::assignFileOffsets()
+{
+ const bool log = false;
+ const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile)
+ && (_options.outputKind() != Options::kPreload));
+ const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile);
+
+ uint64_t address = 0;
+ const char* lastSegName = "";
+ uint64_t floatingAddressStart = _options.baseAddress();
+
+ // first pass, assign addresses to sections in segments with fixed start addresses
+ if ( log ) fprintf(stderr, "Fixed address segments:\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
+ ld::Internal::FinalSection* sect = *it;
+ if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) )
+ continue;
+ if ( segmentsArePageAligned ) {
+ if ( strcmp(lastSegName, sect->segmentName()) != 0 ) {
+ address = _options.customSegmentAddress(sect->segmentName());
+ lastSegName = sect->segmentName();
+ }
+ }
+ // adjust section address based on alignment
+ uint64_t unalignedAddress = address;
+ uint64_t alignment = (1 << sect->alignment);
+ address = ( (unalignedAddress+alignment-1) & (-alignment) );
+
+ // update section info
+ sect->address = address;
+ sect->alignmentPaddingBytes = (address - unalignedAddress);
+
+ // sanity check size
+ if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile)
+ && (_options.outputKind() != Options::kStaticExecutable) )
+ throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range",
+ sect->sectionName(), address, sect->size);
+
+ if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n",
+ sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName());
+ // update running totals
+ if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
+ address += sect->size;
+
+ // if TEXT segment address is fixed, then flow other segments after it
+ if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) {
+ floatingAddressStart = address;
+ }
+ }
+
+ // second pass, assign section address to sections in segments that are contiguous with previous segment
+ address = floatingAddressStart;
+ lastSegName = "";
+ ld::Internal::FinalSection* overlappingFixedSection = NULL;
+ ld::Internal::FinalSection* overlappingFlowSection = NULL;
+ if ( log ) fprintf(stderr, "Regular layout segments:\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
+ ld::Internal::FinalSection* sect = *it;
+ if ( _options.hasCustomSegmentAddress(sect->segmentName()) )
+ continue;
+ if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) {
+ sect->alignmentPaddingBytes = 0;
+ continue;
+ }
+ if ( segmentsArePageAligned ) {
+ if ( strcmp(lastSegName, sect->segmentName()) != 0 ) {
+ // round up size of last segment if needed
+ if ( *lastSegName != '\0' ) {
+ address = pageAlign(address, _options.segPageSize(lastSegName));
+ }
+ // set segment address based on end of last segment
+ address = pageAlign(address);
+ lastSegName = sect->segmentName();
+ }
+ }
+ // adjust section address based on alignment
+ uint64_t unalignedAddress = address;
+ uint64_t alignment = (1 << sect->alignment);
+ address = ( (unalignedAddress+alignment-1) & (-alignment) );
+
+ // update section info
+ sect->address = address;
+ sect->alignmentPaddingBytes = (address - unalignedAddress);
+
+ // sanity check size
+ if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile)
+ && (_options.outputKind() != Options::kStaticExecutable) )
+ throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range",
+ sect->sectionName(), address, sect->size);
+
+ // sanity check it does not overlap a fixed address segment
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = sections.begin(); sit != sections.end(); ++sit) {
+ ld::Internal::FinalSection* otherSect = *sit;
+ if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) )
+ continue;
+ if ( sect->address > otherSect->address ) {
+ if ( (otherSect->address+otherSect->size) > sect->address ) {
+ overlappingFixedSection = otherSect;
+ overlappingFlowSection = sect;
+ }
+ }
+ else {
+ if ( (sect->address+sect->size) > otherSect->address ) {
+ overlappingFixedSection = otherSect;
+ overlappingFlowSection = sect;
+ }
+ }
+ }
+
+ if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n",
+ sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes,
+ sect->segmentName(), sect->sectionName());
+ // update running totals
+ if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
+ address += sect->size;
+ }
+ if ( overlappingFixedSection != NULL ) {
+ fprintf(stderr, "Section layout:\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
+ ld::Internal::FinalSection* sect = *it;
+ if ( sect->isSectionHidden() )
+ continue;
+ fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n",
+ sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes,
+ sect->segmentName(), sect->sectionName());
+
+ }
+ throwf("Section (%s/%s) overlaps fixed address section (%s/%s)",
+ overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(),
+ overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName());
+ }
+
+
+ // third pass, assign section file offsets
+ uint64_t fileOffset = 0;
+ lastSegName = "";
+ if ( log ) fprintf(stderr, "All segments with file offsets:\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
+ ld::Internal::FinalSection* sect = *it;
+ if ( hasZeroForFileOffset(sect) ) {
+ // fileoff of zerofill sections is moot, but historically it is set to zero
+ sect->fileOffset = 0;
+
+ // <rdar://problem/10445047> align file offset with address layout
+ fileOffset += sect->alignmentPaddingBytes;
+ }
+ else {
+ // page align file offset at start of each segment
+ if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) {
+ fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName));
+ }
+ lastSegName = sect->segmentName();
+
+ // align file offset with address layout
+ fileOffset += sect->alignmentPaddingBytes;
+
+ // update section info
+ sect->fileOffset = fileOffset;
+
+ // update running total
+ fileOffset += sect->size;
+ }
+
+ if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n",
+ sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment,
+ sect->segmentName(), sect->sectionName());
+ }
+
+#if 0
+ // for encrypted iPhoneOS apps
+ if ( _options.makeEncryptable() ) {
+ // remember end of __TEXT for later use by load command
+ for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
+ ld::Internal::FinalSection* sect = *it;
+ if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) {
+ _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size);
+ }
+ }
+ }
+#endif
+
+ // return total file size
+ return fileOffset;
+}
+
static char* commatize(uint64_t in, char* out)
{
char* result = out;
mac10_9=0x000A0900, mac10_Future=0x10000000 };
enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100,
iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000,
- iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_Future=0x10000000};
+ iOS_6_0=0x00060000, iOS_7_0=0x00070000,
+ iOS_Future=0x10000000};
namespace relocatable {
//
kindStoreARM64GOTLoadPage21, kindStoreARM64GOTLoadPageOff12,
kindStoreARM64GOTLeaPage21, kindStoreARM64GOTLeaPageOff12,
kindStoreARM64TLVPLoadPage21, kindStoreARM64TLVPLoadPageOff12,
+ kindStoreARM64TLVPLoadNowLeaPage21, kindStoreARM64TLVPLoadNowLeaPageOff12,
kindStoreARM64PointerToGOT, kindStoreARM64PCRelToGOT,
#endif
// dtrace probes
kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear,
// lazy binding
kindLazyTarget, kindSetLazyOffset,
+ // islands
+ kindIslandTarget,
// data-in-code markers
kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16,
kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd,
+ // linker optimzation hints
+ kindLinkerOptimizationHint,
// pointer store combinations
kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32
kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64
kindStoreTargetAddressARM64GOTLoadPageOff12,// kindSetTargetAddress + kindStoreARM64GOTLoadPageOff12
kindStoreTargetAddressARM64GOTLeaPage21, // kindSetTargetAddress + kindStoreARM64GOTLeaPage21
kindStoreTargetAddressARM64GOTLeaPageOff12, // kindSetTargetAddress + kindStoreARM64GOTLeaPageOff12
+ kindStoreTargetAddressARM64TLVPLoadPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadPage21
+ kindStoreTargetAddressARM64TLVPLoadPageOff12,// kindSetTargetAddress + kindStoreARM64TLVPLoadPageOff12
+ kindStoreTargetAddressARM64TLVPLoadNowLeaPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPage21
+ kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPageOff12
#endif
};
contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false)
{ u.addend = addend; }
+#if SUPPORT_ARCH_arm64
+ Fixup(Kind k, uint32_t lohKind, uint32_t off1, uint32_t off2) :
+ offsetInAtom(off1), kind(k), clusterSize(k1of1),
+ weakImport(false), binding(Fixup::bindingNone), contentAddendOnly(false),
+ contentDetlaToAddendOnly(false), contentIgnoresAddend(false) {
+ assert(k == kindLinkerOptimizationHint);
+ LOH_arm64 extra;
+ extra.addend = 0;
+ extra.info.kind = lohKind;
+ extra.info.count = 1;
+ extra.info.delta1 = 0;
+ extra.info.delta2 = (off2 - off1) >> 2;
+ u.addend = extra.addend;
+ }
+#endif
+
+
bool firstInCluster() const {
switch (clusterSize) {
case k1of1:
return false;
}
+#if SUPPORT_ARCH_arm64
+ union LOH_arm64 {
+ uint64_t addend;
+ struct {
+ unsigned kind : 6,
+ count : 2, // 00 => 1 addr, 11 => 4 addrs
+ delta1 : 14, // 16-bit delta, low 2 bits assumed zero
+ delta2 : 14,
+ delta3 : 14,
+ delta4 : 14;
+ } info;
+ };
+#endif
+
};
//
}
return false;
}
+ virtual void setFile(const File* f) { }
virtual UnwindInfo::iterator beginUnwind() const { return NULL; }
virtual UnwindInfo::iterator endUnwind() const { return NULL; }
bool hasExternalRelocs;
};
-
+ virtual uint64_t assignFileOffsets() = 0;
+ virtual void setSectionSizesAndAlignments() = 0;
virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0;
virtual ld::Internal::FinalSection* getFinalSection(const ld::Section& inputSection) = 0;
virtual ~Internal() {}
objcDylibConstraint(ld::File::objcConstraintNone),
cpuSubType(0),
allObjectFilesScatterable(true),
- someObjectFileHasDwarf(false), usingHugeSections(false) { }
+ someObjectFileHasDwarf(false), usingHugeSections(false),
+ hasThreadLocalVariableDefinitions(false),
+ hasWeakExternalSymbols(false),
+ someObjectHasOptimizationHints(false) { }
std::vector<FinalSection*> sections;
std::vector<ld::dylib::File*> dylibs;
bool allObjectFilesScatterable;
bool someObjectFileHasDwarf;
bool usingHugeSections;
+ bool hasThreadLocalVariableDefinitions;
+ bool hasWeakExternalSymbols;
+ bool someObjectHasOptimizationHints;
};
// see if member is llvm bitcode file
result = lto::parse(member->content(), member->contentSize(),
mPath, member->modificationTime(), ordinal,
- _objOpts.architecture, _objOpts.subType, _logAllFiles);
+ _objOpts.architecture, _objOpts.subType, _logAllFiles, _objOpts.verboseOptimizationHints);
if ( result != NULL ) {
MemberState state = {result, member, false, false, memberIndex};
_instantiatedEntries[member] = state;
typedef typename A::sint_t sint_t;
static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
- const pint_t cuStarts[], uint32_t cuCount, bool keepDwarfWhichHasCU, bool forceDwarfConversion,
+ const pint_t cuStarts[], uint32_t cuCount,
+ bool keepDwarfWhichHasCU, bool forceDwarfConversion, bool neverConvertToCU,
CFI_Atom_Info<A>* infos, uint32_t& infosCount, void* ref, WarnFunc warn);
template <typename A, typename R>
const char* DwarfInstructions<A,R>::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
- const pint_t cuStarts[], uint32_t cuCount, bool keepDwarfWhichHasCU, bool forceDwarfConversion,
+ const pint_t cuStarts[], uint32_t cuCount,
+ bool keepDwarfWhichHasCU, bool forceDwarfConversion, bool neverConvertToCU,
CFI_Atom_Info<A>* infos, uint32_t& infosCount, void* ref, WarnFunc warn)
{
typename CFI_Parser<A>::CIE_Info cieInfo;
++entry;
}
else {
- if ( (cuCount != 0) && !forceDwarfConversion ) {
+ if ( neverConvertToCU || ((cuCount != 0) && !forceDwarfConversion) ) {
// Have some compact unwind, so this is a new .o file, therefore anything without
// compact unwind must be something not expressable in compact unwind.
R dummy;
#define __STDC_CONSTANT_MACROS 1
#include "llvm-c/lto.h"
-
namespace lto {
static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch);
static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength);
static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles);
+ time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch,
+ bool logAllFiles, bool verboseOptimizationHints);
static bool libLTOisLoaded() { return (::lto_get_version() != NULL); }
static bool optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
private:
static const char* tripletPrefixForArch(cpu_type_t arch);
static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options);
+#if LTO_API_VERSION >= 7
+ static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t, const char*, void*);
+#endif
typedef std::unordered_set<const char*, ld::CStringHash, ld::CStringEquals> CStringSet;
typedef std::unordered_map<const char*, Atom*, ld::CStringHash, ld::CStringEquals> CStringToAtom;
}
File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal,
- cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles)
+ cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints)
{
File* f = new File(path, modTime, ordinal, fileContent, fileLength, architecture);
_s_files.push_back(f);
objOpts.warnUnwindConversionProblems = options.needsUnwindInfoSection;
objOpts.keepDwarfUnwind = options.keepDwarfUnwind;
objOpts.forceDwarfConversion = false;
+ objOpts.neverConvertDwarf = false;
+ objOpts.verboseOptimizationHints = options.verboseOptimizationHints;
objOpts.subType = 0;
// mach-o parsing is done in-memory, but need path for debug notes
};
+#if LTO_API_VERSION >= 7
+void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*)
+{
+ switch ( severity ) {
+ case LTO_DS_NOTE:
+ case LTO_DS_WARNING:
+ warning("%s", message);
+ break;
+ case LTO_DS_ERROR:
+ throwf("%s", message);
+ }
+}
+#endif
+
bool Parser::optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
const OptimizeOptions& options,
// create optimizer and add each Reader
lto_code_gen_t generator = ::lto_codegen_create();
+#if LTO_API_VERSION >= 7
+ lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL);
+#endif
+
// <rdar://problem/12379604> The order that files are merged must match command line order
std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter());
ld::File::Ordinal lastOrdinal;
break;
case ld::Fixup::bindingsIndirectlyBound:
target = state.indirectBindingTable[fit->u.bindingIndex];
- if ( target == NULL )
- throwf("'%s' in %s contains undefined reference", atom->name(), atom->file()->path());
- assert(target != NULL);
- if ( target->contentType() == ld::Atom::typeLTOtemporary )
+ if ( (target != NULL) && (target->contentType() == ld::Atom::typeLTOtemporary) )
nonLLVMRefs.insert(target->name());
default:
break;
void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
{
+ static const ld::Atom* lastProxiedAtom = NULL;
+ static const ld::File* lastProxiedFile = NULL;
// update proxy atoms to point to real atoms and find new atoms
const char* name = machoAtom.name();
if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) {
if ( pos != _llvmAtoms.end() ) {
// turn Atom into a proxy for this mach-o atom
pos->second->setCompiledAtom(machoAtom);
+ lastProxiedAtom = &machoAtom;
+ lastProxiedFile = pos->second->file();
}
else {
// an atom of this name was not in the allAtoms list the linker gave us
else {
// ld only knew about non-static atoms, so this one must be new
_newAtoms.push_back(&machoAtom);
+ // <rdar://problem/15469363> if new static atom in same section as previous non-static atom, assign to same file as previous
+ if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) {
+ ld::Atom* ma = const_cast<ld::Atom*>(&machoAtom);
+ ma->setFile(lastProxiedFile);
+ }
}
// adjust fixups to go through proxy atoms
//
ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime, ld::File::Ordinal ordinal,
- cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles)
+ cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles,
+ bool verboseOptimizationHints)
{
Mutex lock;
if ( Parser::validFile(fileContent, fileLength, architecture, subarch) )
- return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles);
+ return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints);
else
return NULL;
}
extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime, ld::File::Ordinal ordinal,
- cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles);
+ cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles,
+ bool verboseOptimizationHints);
struct OptimizeOptions {
const char* outputFilePath;
bool linkerDeadStripping;
bool needsUnwindInfoSection;
bool keepDwarfUnwind;
+ bool verboseOptimizationHints;
cpu_type_t arch;
const char* mcpu;
const std::vector<const char*>* llvmOptions;
void buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
const macho_nlist<P>* symbolTable, const char* strings,
const uint8_t* fileContent);
+ static uint32_t parseVersionNumber32(const char* versionString);
static const char* objCInfoSegmentName();
static const char* objCInfoSectionName();
bool _explictReExportFound;
bool _wrongOS;
bool _installPathOverride;
+ bool _indirectDylibsProcessed;
static bool _s_logHashtable;
};
_parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL),
_noRexports(false), _hasWeakExports(false),
_deadStrippable(false), _hasPublicInstallName(false),
- _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false)
+ _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), _indirectDylibsProcessed(false)
{
const macho_header<P>* header = (const macho_header<P>*)fileContent;
const uint32_t cmd_count = header->ncmds();
munmap((caddr_t)fileContent, fileLength);
}
+//
+// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz
+//
+template <typename A>
+uint32_t File<A>::parseVersionNumber32(const char* versionString)
+{
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint32_t z = 0;
+ char* end;
+ x = strtoul(versionString, &end, 10);
+ if ( *end == '.' ) {
+ y = strtoul(&end[1], &end, 10);
+ if ( *end == '.' ) {
+ z = strtoul(&end[1], &end, 10);
+ }
+ }
+ if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) )
+ throwf("malformed 32-bit x.y.z version number: %s", versionString);
+
+ return (x << 16) | ( y << 8 ) | z;
+}
template <typename A>
void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
_dylibInstallPath = symName;
_installPathOverride = true;
+ // <rdar://problem/14448206> CoreGraphics redirects to ApplicationServices, but with wrong compat version
+ if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 )
+ _dylibCompatibilityVersion = parseVersionNumber32("1.0");
+ return;
+ }
+ else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
+ _dylibCompatibilityVersion = parseVersionNumber32(symName);
return;
}
else {
template <typename A>
void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
{
+ // only do this once
+ if ( _indirectDylibsProcessed )
+ return;
const static bool log = false;
if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
if ( _linkingFlat ) {
chain.prev = NULL;
chain.file = this;
this->assertNoReExportCycles(&chain);
+
+ _indirectDylibsProcessed = true;
}
template <typename A>
{
if ( Parser<x86_64>::validFile(fileContent, false) ) {
*result = CPU_TYPE_X86_64;
- *subResult = CPU_SUBTYPE_X86_64_ALL;
+ const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
+ *subResult = header->cpusubtype();
return true;
}
if ( Parser<x86>::validFile(fileContent, false) ) {
Atom<A>* findContentAtomByAddress(pint_t addr, class Atom<A>* start, class Atom<A>* end);
uint32_t x86_64PcRelOffset(uint8_t r_type);
+ void addLOH(class Parser<A>& parser, int kind, int count, const uint64_t addrs[]);
static const char* makeSegmentName(const macho_section<typename A::P>* s);
static bool readable(const macho_section<typename A::P>* s);
static bool writable(const macho_section<typename A::P>* s);
{
public:
// overrides of ld::Atom
- virtual ld::File* file() const { return §().file(); }
+ virtual const ld::File* file() const;
virtual const char* translationUnitSource() const
{ return sect().file().translationUnitSource(); }
virtual const char* name() const { return _name; }
virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex+_unwindInfoCount]; }
virtual ld::Atom::LineInfo::iterator beginLineInfo() const{ return &machofile()._lineInfos[_lineInfoStartIndex]; }
virtual ld::Atom::LineInfo::iterator endLineInfo() const { return &machofile()._lineInfos[_lineInfoStartIndex+_lineInfoCount]; }
+ virtual void setFile(const ld::File* f);
private:
throwf("too may fixups in %s", name()); ++_fixupsCount; }
const uint8_t* contentPointer() const;
uint32_t fixupCount() const { return _fixupsCount; }
- void verifyAlignment() const;
+ void verifyAlignment(const macho_section<typename A::P>&) const;
typedef typename A::P P;
typedef typename A::P::E E;
if ( _scope == ld::Atom::scopeGlobal &&
(sym.n_desc() & (N_WEAK_DEF|N_WEAK_REF)) == (N_WEAK_DEF|N_WEAK_REF) )
this->setAutoHide();
- this->verifyAlignment();
+ this->verifyAlignment(*sct.machoSection());
}
private:
_fixupsCount : kFixupCountBits,
_lineInfoCount : kLineInfoCountBits,
_unwindInfoCount : kUnwindInfoCountBits;
-
+
+ static std::map<const ld::Atom*, const ld::File*> _s_fileOverride;
};
+template <typename A>
+std::map<const ld::Atom*, const ld::File*> Atom<A>::_s_fileOverride;
+
+template <typename A>
+void Atom<A>::setFile(const ld::File* f) {
+ _s_fileOverride[this] = f;
+}
+template <typename A>
+const ld::File* Atom<A>::file() const
+{
+ std::map<const ld::Atom*, const ld::File*>::iterator pos = _s_fileOverride.find(this);
+ if ( pos != _s_fileOverride.end() )
+ return pos->second;
+
+ return §().file();
+}
template <typename A>
void Atom<A>::setFixupsRange(uint32_t startIndex, uint32_t count)
}
template <>
-void Atom<arm>::verifyAlignment() const
+void Atom<arm>::verifyAlignment(const macho_section<P>&) const
{
if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) {
if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) )
}
}
+#if SUPPORT_ARCH_arm64
+template <>
+void Atom<arm64>::verifyAlignment(const macho_section<P>& sect) const
+{
+ if ( (this->section().type() == ld::Section::typeCode) && (sect.size() != 0) ) {
+ if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) )
+ warning("arm64 function not 4-byte aligned: %s from %s", this->name(), this->file()->path());
+ }
+}
+#endif
+
template <typename A>
-void Atom<A>::verifyAlignment() const
+void Atom<A>::verifyAlignment(const macho_section<P>&) const
{
}
const ParserOptions& opts) {
Parser p(fileContent, fileLength, path, modTime,
ordinal, opts.warnUnwindConversionProblems,
- opts.keepDwarfUnwind, opts.forceDwarfConversion);
+ opts.keepDwarfUnwind, opts.forceDwarfConversion,
+ opts.neverConvertDwarf, opts.verboseOptimizationHints);
return p.parse(opts);
}
bool hasDataInCodeLabels() { return _hasDataInCodeLabels; }
bool keepDwarfUnwind() { return _keepDwarfUnwind; }
bool forceDwarfConversion() { return _forceDwarfConversion; }
+ bool verboseOptimizationHints() { return _verboseOptimizationHints; }
+ bool neverConvertDwarf() { return _neverConvertDwarf; }
macho_data_in_code_entry<P>* dataInCodeStart() { return _dataInCodeStart; }
macho_data_in_code_entry<P>* dataInCodeEnd() { return _dataInCodeEnd; }
+ const uint8_t* optimizationHintsStart() { return _lohStart; }
+ const uint8_t* optimizationHintsEnd() { return _lohEnd; }
+ bool hasOptimizationHints() { return _lohStart != _lohEnd; }
+
void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target);
void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase);
Parser(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime, ld::File::Ordinal ordinal,
- bool warnUnwindConversionProblems, bool keepDwarfUnwind, bool forceDwarfConversion);
+ bool warnUnwindConversionProblems, bool keepDwarfUnwind,
+ bool forceDwarfConversion, bool neverConvertDwarf, bool verboseOptimizationHints);
ld::relocatable::File* parse(const ParserOptions& opts);
uint8_t loadCommandSizeMask();
bool parseLoadCommands();
bool _hasUUID;
macho_data_in_code_entry<P>* _dataInCodeStart;
macho_data_in_code_entry<P>* _dataInCodeEnd;
+ const uint8_t* _lohStart;
+ const uint8_t* _lohEnd;
// filled in by parse()
CFISection<A>* _EHFrameSection;
bool _hasDataInCodeLabels;
bool _keepDwarfUnwind;
bool _forceDwarfConversion;
+ bool _neverConvertDwarf;
+ bool _verboseOptimizationHints;
unsigned int _stubsSectionNum;
const macho_section<P>* _stubsMachOSection;
std::vector<const char*> _dtraceProviderInfo;
template <typename A>
Parser<A>::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime,
- ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion)
+ ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion,
+ bool neverConvertDwarf, bool verboseOptimizationHints)
: _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime),
_ordinal(ordinal), _file(NULL),
_symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0),
_undefinedStartIndex(0), _undefinedEndIndex(0),
_sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false),
_dataInCodeStart(NULL), _dataInCodeEnd(NULL),
+ _lohStart(NULL), _lohEnd(NULL),
_EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL),
_tentativeDefinitionCount(0), _absoluteSymbolCount(0),
_symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false),
_overlappingSymbols(false), _warnUnwindConversionProblems(convertDUI), _hasDataInCodeLabels(false),
_keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion),
+ _neverConvertDwarf(neverConvertDwarf),
+ _verboseOptimizationHints(verboseOptimizationHints),
_stubsSectionNum(0), _stubsMachOSection(NULL)
{
}
throw "malformed LC_LINKER_OPTION";
}
break;
+ case LC_LINKER_OPTIMIZATION_HINTS:
+ {
+ const macho_linkedit_data_command<P>* loh = (macho_linkedit_data_command<P>*)cmd;
+ _lohStart = _fileContent + loh->dataoff();
+ _lohEnd = _fileContent + loh->dataoff() + loh->datasize();
+ if ( _lohEnd > endOfFile )
+ throw "LC_LINKER_OPTIMIZATION_HINTS table extends beyond end of file";
+ }
+ break;
default:
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
if ( segment != NULL )
case ld::Fixup::kindStoreARM64GOTLoadPageOff12:
firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12;
break;
+ case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+ firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21;
+ break;
+ case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
+ firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12;
+ break;
#endif
default:
combined = false;
return false;
vers = A::P::E::get16(*(uint16_t*)di);
- if (vers < 2 || vers > 3)
+ if (vers < 2 || vers > 4)
/* DWARF version wrong for this code.
Chances are we could continue anyway, but we don't know for sure. */
return false;
ld::Atom::Alignment Section<A>::alignmentForAddress(pint_t addr)
{
const uint32_t sectionAlignment = this->_machOSection->align();
- return ld::Atom::Alignment(sectionAlignment, (addr % (1 << sectionAlignment)));
+ uint32_t modulus = (addr % (1 << sectionAlignment));
+ if ( modulus > 0xFFFF )
+ warning("alignment for symbol at address 0x%08llX in %s exceeds 2^16", (uint64_t)addr, this->file().path());
+ return ld::Atom::Alignment(sectionAlignment, modulus);
}
template <typename A>
const char* msg;
msg = libunwind::DwarfInstructions<OAS, libunwind::Registers_x86_64>::parseCFIs(
oas, this->_machOSection->addr(), this->_machOSection->size(),
- cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), cfiArray, count, (void*)&parser, warnFunc);
+ cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(),
+ cfiArray, count, (void*)&parser, warnFunc);
if ( msg != NULL )
throwf("malformed __eh_frame section: %s", msg);
}
const char* msg;
msg = libunwind::DwarfInstructions<OAS, libunwind::Registers_x86>::parseCFIs(
oas, this->_machOSection->addr(), this->_machOSection->size(),
- cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), cfiArray, count, (void*)&parser, warnFunc);
+ cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(),
+ cfiArray, count, (void*)&parser, warnFunc);
if ( msg != NULL )
throwf("malformed __eh_frame section: %s", msg);
}
const char* msg;
msg = libunwind::DwarfInstructions<OAS, libunwind::Registers_arm64>::parseCFIs(
oas, this->_machOSection->addr(), this->_machOSection->size(),
- cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(),
+ cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(),
cfiArray, count, (void*)&parser, warnFunc);
if ( msg != NULL )
throwf("malformed __eh_frame section: %s", msg);
*ct = contentUTF16;
*count = (targetAtom->size()+1)/2; // round up incase of buggy compiler that has only one trailing zero byte
}
- assert(target != NULL);
+ else {
+ *ct = contentUnknown;
+ *count = 0;
+ return NULL;
+ }
return target->contentPointer();
}
assert(0);
}
return hash;
case contentUnknown:
- return 0;
+ // <rdar://problem/14134211> For malformed CFStrings, hash to address of atom so they have unique hashes
+ return ULONG_MAX - (unsigned long)(atom);
}
return 0;
}
if ( thisType != rhsType )
return false;
+ if ( thisType == contentUnknown )
+ return false;
+
+ if ( rhsType == contentUnknown )
+ return false;
+
// no need to compare content of pointers are already the same
if ( cstringContent == rhsStringContent )
return true;
return PointerToCStringSection<x86>::addRelocFixup(parser, reloc);
}
+#if SUPPORT_ARCH_arm64
+template <>
+void Section<arm64>::addLOH(class Parser<arm64>& parser, int kind, int count, const uint64_t addrs[]) {
+ switch (kind) {
+ case LOH_ARM64_ADRP_ADRP:
+ case LOH_ARM64_ADRP_LDR:
+ case LOH_ARM64_ADRP_ADD:
+ case LOH_ARM64_ADRP_LDR_GOT:
+ if ( count != 2 )
+ warning("arm64 Linker Optimiztion Hint %d has wrong number of arguments", kind);
+ break;
+ case LOH_ARM64_ADRP_ADD_LDR:
+ case LOH_ARM64_ADRP_LDR_GOT_LDR:
+ case LOH_ARM64_ADRP_ADD_STR:
+ case LOH_ARM64_ADRP_LDR_GOT_STR:
+ if ( count != 3 )
+ warning("arm64 Linker Optimiztion Hint %d has wrong number of arguments", kind);
+ }
+
+ // pick lowest address in tuple for use as offsetInAtom
+ uint64_t lowestAddress = addrs[0];
+ for(int i=1; i < count; ++i) {
+ if ( addrs[i] < lowestAddress )
+ lowestAddress = addrs[i];
+ }
+ // verify all other address are in same atom
+ Atom<arm64>* inAtom = parser.findAtomByAddress(lowestAddress);
+ const uint64_t atomStartAddr = inAtom->objectAddress();
+ const uint64_t atomEndAddr = atomStartAddr + inAtom->size();
+ for(int i=0; i < count; ++i) {
+ if ( (addrs[i] < atomStartAddr) || (addrs[i] >= atomEndAddr) ) {
+ warning("arm64 Linker Optimiztion Hint addresses are not in same atom: 0x%08llX and 0x%08llX",
+ lowestAddress, addrs[i]);
+ return; // skip this LOH
+ }
+ if ( (addrs[i] & 0x3) != 0 ) {
+ warning("arm64 Linker Optimiztion Hint address is not 4-byte aligned: 0x%08llX", addrs[i]);
+ return; // skip this LOH
+ }
+ if ( (addrs[i] - lowestAddress) > 0xFFFF ) {
+ if ( parser.verboseOptimizationHints() ) {
+ warning("arm64 Linker Optimiztion Hint addresses are too far apart: 0x%08llX and 0x%08llX",
+ lowestAddress, addrs[i]);
+ }
+ return; // skip this LOH
+ }
+ }
+
+ // encoded kind, count, and address deltas in 64-bit addend
+ ld::Fixup::LOH_arm64 extra;
+ extra.addend = 0;
+ extra.info.kind = kind;
+ extra.info.count = count-1;
+ extra.info.delta1 = (addrs[0] - lowestAddress) >> 2;
+ extra.info.delta2 = (count > 1) ? ((addrs[1] - lowestAddress) >> 2) : 0;
+ extra.info.delta3 = (count > 2) ? ((addrs[2] - lowestAddress) >> 2) : 0;
+ extra.info.delta4 = (count > 3) ? ((addrs[3] - lowestAddress) >> 2) : 0;
+ typename Parser<arm64>::SourceLocation src(inAtom, lowestAddress- inAtom->objectAddress());
+ parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindLinkerOptimizationHint, extra.addend);
+}
+#endif
+
+template <typename A>
+void Section<A>::addLOH(class Parser<A>& parser, int kind, int count, const uint64_t addrs[]) {
+
+}
template <typename A>
void Section<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&)
}
}
+ // <rdar://problem/11945700> convert linker optimization hints into internal format
+ if ( this->type() == ld::Section::typeCode && parser.hasOptimizationHints() ) {
+ const pint_t startAddr = this->_machOSection->addr();
+ const pint_t endAddr = startAddr + this->_machOSection->size();
+ for (const uint8_t* p = parser.optimizationHintsStart(); p < parser.optimizationHintsEnd(); ) {
+ uint64_t addrs[4];
+ int32_t kind = read_uleb128(&p, parser.optimizationHintsEnd());
+ if ( kind == 0 ) // padding at end of loh buffer
+ break;
+ if ( kind == -1 ) {
+ warning("malformed uleb128 kind in LC_LINKER_OPTIMIZATION_HINTS");
+ break;
+ }
+ int32_t count = read_uleb128(&p, parser.optimizationHintsEnd());
+ if ( count == -1 ) {
+ warning("malformed uleb128 count in LC_LINKER_OPTIMIZATION_HINTS");
+ break;
+ }
+ if ( count > 3 ) {
+ warning("address count > 3 in LC_LINKER_OPTIMIZATION_HINTS");
+ break;
+ }
+ for (int32_t i=0; i < count; ++i) {
+ addrs[i] = read_uleb128(&p, parser.optimizationHintsEnd());
+ }
+ if ( (startAddr <= addrs[0]) && (addrs[0] < endAddr) ) {
+ this->addLOH(parser, kind, count, addrs);
+ //fprintf(stderr, "kind=%d", kind);
+ //for (int32_t i=0; i < count; ++i) {
+ // fprintf(stderr, ", addr=0x%08llX", addrs[i]);
+ //}
+ //fprintf(stderr, "\n");
+ }
+ }
+ }
+
// add follow-on fixups for aliases
if ( _hasAliases ) {
{
if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) {
*result = CPU_TYPE_X86_64;
- *subResult = CPU_SUBTYPE_X86_64_ALL;
+ const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
+ *subResult = header->cpusubtype();
return true;
}
if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ) {
bool warnUnwindConversionProblems;
bool keepDwarfUnwind;
bool forceDwarfConversion;
+ bool neverConvertDwarf;
+ bool verboseOptimizationHints;
uint32_t subType;
};
namespace branch_island {
+static std::map<const Atom*, uint64_t> sAtomToAddress;
struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; };
ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland,
ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
_name(nm),
- _target(target),
- _finalTarget(finalTarget) { }
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, target),
+ _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) {
+ if (_s_log) fprintf(stderr, "%s: ARM jump instruction branch island to final target %s\n",
+ target->name(), finalTarget.atom->name());
+ }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 4; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
- int64_t displacement = _target->finalAddress() - this->finalAddress() - 8;
- if ( _target->contentType() == ld::Atom::typeBranchIsland ) {
- // an ARM branch can branch farther than a thumb branch. The branch
- // island generation was conservative and put islands every thumb
- // branch distance apart. Check to see if this is a an island
- // hopping branch that could be optimized to go directly to target.
- int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 8;
- if ( (skipToFinalDisplacement < 33554428LL) && (skipToFinalDisplacement > (-33554432LL)) ) {
- // can skip branch island and jump straight to target
- if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n",
- _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress());
- displacement = skipToFinalDisplacement;
- }
- else {
- // ultimate target is too far, jump to island
- if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n",
- _target->name(), _finalTarget.atom->finalAddress());
- }
- }
- uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF;
- int32_t branchInstruction = 0xEA000000 | imm24;
- OSWriteLittleInt32(buffer, 0, branchInstruction);
+ OSWriteLittleInt32(buffer, 0, 0xEA000000);
}
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:
const char* _name;
- const ld::Atom* _target;
- TargetAndOffset _finalTarget;
+ ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
};
ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland,
ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)),
_name(nm),
- _target(target),
- _finalTarget(finalTarget) { }
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressThumbBranch22, target),
+ _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) {
+ if (_s_log) fprintf(stderr, "%s: Thumb jump instruction branch island to final target %s\n",
+ target->name(), finalTarget.atom->name());
+ }
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return 4; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const {
- int64_t displacement = _target->finalAddress() - this->finalAddress() - 4;
- if ( _target->contentType() == ld::Atom::typeBranchIsland ) {
- // an ARM branch can branch farther than a thumb branch. The branch
- // island generation was conservative and put islands every thumb
- // branch distance apart. Check to see if this is a an island
- // hopping branch that could be optimized to go directly to target.
- int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 4;
- if ( (skipToFinalDisplacement < 16777214) && (skipToFinalDisplacement > (-16777216LL)) ) {
- // can skip branch island and jump straight to target
- if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n",
- _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress());
- displacement = skipToFinalDisplacement;
- }
- else {
- // ultimate target is too far for thumb2 branch, jump to island
- if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n",
- _target->name(), _finalTarget.atom->finalAddress());
- }
- }
- // The instruction is really two instructions:
- // The lower 16 bits are the first instruction, which contains the high
- // 11 bits of the displacement.
- // The upper 16 bits are the second instruction, which contains the low
- // 11 bits of the displacement, as well as differentiating bl and blx.
- uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
- uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
- uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
- uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
- uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
- uint32_t j1 = (i1 == s);
- uint32_t j2 = (i2 == s);
- uint32_t opcode = 0x9000F000;
- uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
- uint32_t firstDisp = (s << 10) | imm10;
- uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp;
- //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n",
- // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName());
- OSWriteLittleInt32(buffer, 0, newInstruction);
+ OSWriteLittleInt32(buffer, 0, 0xf0008000);
}
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:
const char* _name;
- const ld::Atom* _target;
- TargetAndOffset _finalTarget;
+ ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+};
+
+
+
+class Thumb2toThumbBranchAbsoluteIslandAtom : public ld::Atom {
+public:
+ Thumb2toThumbBranchAbsoluteIslandAtom(const char* nm, const ld::Section& inSect, TargetAndOffset finalTarget)
+ : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland,
+ ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)),
+ _name(nm),
+ _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom),
+ _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbLow16),
+ _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom),
+ _fixup4(4, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbHigh16),
+ _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { }
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual const char* name() const { return _name; }
+ virtual uint64_t size() const { return 10; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[0], 0, 0x0c00f240); // movw r12, #0x5678
+ OSWriteLittleInt32(&buffer[4], 0, 0x0c00f2c0); // movt r12, #0x1234
+ OSWriteLittleInt16(&buffer[8], 0, 0x4760); // bx r12
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup5)[1]; }
+
+private:
+ const char* _name;
+ ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+ ld::Fixup _fixup4;
+ ld::Fixup _fixup5;
};
+
class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom {
public:
NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget)
};
-static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget)
+static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget,
+ TargetAndOffset finalTarget, const ld::Section& inSect, bool crossSectionBranch)
{
char* name;
if ( finalTarget.offset == 0 ) {
case ld::Fixup::kindStoreThumbBranch22:
case ld::Fixup::kindStoreTargetAddressARMBranch24:
case ld::Fixup::kindStoreTargetAddressThumbBranch22:
- if ( finalTarget.atom->isThumb() ) {
+ if ( crossSectionBranch && opts.preferSubArchitecture() && opts.archSupportsThumb2() ) {
+ return new Thumb2toThumbBranchAbsoluteIslandAtom(name, inSect, finalTarget);
+ }
+ else if ( finalTarget.atom->isThumb() ) {
if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) {
return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget);
}
// before any branches could be pushed out of range.
//
-void doPass(const Options& opts, ld::Internal& state)
-{
- // only make branch islands in final linked images
- if ( opts.outputKind() == Options::kObjectFile )
- return;
- // only ARM needs branch islands
- switch ( opts.architecture() ) {
- case CPU_TYPE_ARM:
- break;
- default:
- return;
- }
-
- // scan to find __text section
- ld::Internal::FinalSection* textSection = NULL;
- for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
- ld::Internal::FinalSection* sect = *sit;
- if ( strcmp(sect->sectionName(), "__text") == 0 )
- textSection = sect;
- }
- if ( textSection == NULL )
- return;
-
+static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection)
+{
// assign section offsets to each atom in __text section, watch for thumb branches, and find total size
- const bool isARM = (opts.architecture() == CPU_TYPE_ARM);
bool hasThumbBranches = false;
+ bool haveCrossSectionBranches = false;
+ const bool preload = (opts.outputKind() == Options::kPreload);
uint64_t offset = 0;
for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
- // check for thumb branches
- if ( isARM && ~hasThumbBranches ) {
- for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
- switch ( fit->kind ) {
- case ld::Fixup::kindStoreThumbBranch22:
- case ld::Fixup::kindStoreTargetAddressThumbBranch22:
- hasThumbBranches = true;
- break;
- default:
- break;
- }
+ // check for thumb branches and cross section branches
+ const ld::Atom* target = NULL;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->firstInCluster() ) {
+ target = NULL;
+ }
+ switch ( fit->binding ) {
+ case ld::Fixup::bindingNone:
+ case ld::Fixup::bindingByNameUnbound:
+ break;
+ case ld::Fixup::bindingByContentBound:
+ case ld::Fixup::bindingDirectlyBound:
+ target = fit->u.target;
+ break;
+ case ld::Fixup::bindingsIndirectlyBound:
+ target = state.indirectBindingTable[fit->u.bindingIndex];
+ break;
+ }
+ bool haveBranch = false;
+ switch (fit->kind) {
+ case ld::Fixup::kindStoreThumbBranch22:
+ case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+ hasThumbBranches = true;
+ // fall into arm branch case
+ case ld::Fixup::kindStoreARMBranch24:
+ case ld::Fixup::kindStoreTargetAddressARMBranch24:
+ haveBranch = true;
+ break;
+ default:
+ break;
+ }
+ if ( haveBranch && (target->contentType() != ld::Atom::typeStub) ) {
+ // <rdar://problem/14792124> haveCrossSectionBranches only applies to -preload builds
+ if ( preload && (atom->section() != target->section()) )
+ haveCrossSectionBranches = true;
}
}
// align atom
offset += atom->size();
}
uint64_t totalTextSize = offset;
- if ( totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches) )
+ if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches )
return;
- if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize);
+ if (_s_log) fprintf(stderr, "ld: section %s size=%llu, might need branch islands\n", textSection->sectionName(), totalTextSize);
// Figure out how many regions of branch islands will be needed, and their locations.
// Construct a vector containing the atoms after which branch islands will be inserted,
const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section
std::vector<const ld::Atom*> branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted
uint64_t previousIslandEndAddr = 0;
- const ld::Atom *insertionPoint;
+ const ld::Atom *insertionPoint = NULL;
branchIslandInsertionPoints.reserve(totalTextSize/kBetweenRegions*2);
for (std::vector<const ld::Atom*>::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) {
const ld::Atom* atom = *it;
// if we move past the next atom, will the run length exceed kBetweenRegions?
- if ( atom->sectionOffset() + atom->size() - previousIslandEndAddr > kBetweenRegions ) {
+ if ( atom->sectionOffset() + atom->size() > previousIslandEndAddr + kBetweenRegions ) {
// yes. Add the last known good location (atom) for inserting a branch island.
if ( insertionPoint == NULL )
throwf("Unable to insert branch island. No insertion point available.");
if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) )
insertionPoint = atom;
}
- // add one more island after the last atom
- if (insertionPoint != NULL)
+ // add one more island after the last atom if close to limit
+ if ( (insertionPoint != NULL) && (insertionPoint->sectionOffset() + insertionPoint->size() > previousIslandEndAddr + (kBetweenRegions-0x100000)) )
branchIslandInsertionPoints.push_back(insertionPoint);
- const int kIslandRegionsCount = branchIslandInsertionPoints.size();
- if (_s_log) {
- fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
- for (std::vector<const ld::Atom*>::iterator it = branchIslandInsertionPoints.begin(); it != branchIslandInsertionPoints.end(); ++it) {
- const ld::Atom* atom = *it;
- const ld::File *file = atom->file();
- fprintf(stderr, "ld: branch island will be inserted at 0x%llx after %s", atom->sectionOffset()+atom->size(), atom->name());
- if (file) fprintf(stderr, " (%s)", atom->file()->path());
- fprintf(stderr, "\n");
- }
+ if ( haveCrossSectionBranches && branchIslandInsertionPoints.empty() ) {
+ branchIslandInsertionPoints.push_back(textSection->atoms.back());
}
+ const int kIslandRegionsCount = branchIslandInsertionPoints.size();
-
+ if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
typedef std::map<TargetAndOffset,const ld::Atom*, TargetAndOffsetComparor> AtomToIsland;
AtomToIsland* regionsMap[kIslandRegionsCount];
+ uint64_t regionAddresses[kIslandRegionsCount];
std::vector<const ld::Atom*>* regionsIslands[kIslandRegionsCount];
for(int i=0; i < kIslandRegionsCount; ++i) {
regionsMap[i] = new AtomToIsland();
regionsIslands[i] = new std::vector<const ld::Atom*>();
+ regionAddresses[i] = branchIslandInsertionPoints[i]->sectionOffset() + branchIslandInsertionPoints[i]->size();
+ if (_s_log) fprintf(stderr, "ld: branch islands will be inserted at 0x%08llX after %s\n", regionAddresses[i], branchIslandInsertionPoints[i]->name());
}
unsigned int islandCount = 0;
break;
}
if ( haveBranch ) {
+ bool crossSectionBranch = ( preload && (atom->section() != target->section()) );
int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom;
int64_t dstAddr = target->sectionOffset() + addend;
+ if ( preload ) {
+ srcAddr = sAtomToAddress[atom] + fit->offsetInAtom;
+ dstAddr = sAtomToAddress[target] + addend;
+ }
if ( target->section().type() == ld::Section::typeStub )
dstAddr = totalTextSize;
int64_t displacement = dstAddr - srcAddr;
TargetAndOffset finalTargetAndOffset = { target, addend };
const int64_t kBranchLimit = kBetweenRegions;
- if ( displacement > kBranchLimit ) {
+ if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) {
+ const ld::Atom* island;
+ AtomToIsland* region = regionsMap[0];
+ AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
+ if ( pos == region->end() ) {
+ island = makeBranchIsland(opts, fit->kind, 0, target, finalTargetAndOffset, atom->section(), true);
+ (*region)[finalTargetAndOffset] = island;
+ if (_s_log) fprintf(stderr, "added absolute branching island %p %s, displacement=%lld\n",
+ island, island->name(), displacement);
+ ++islandCount;
+ regionsIslands[0]->push_back(island);
+ }
+ else {
+ island = pos->second;
+ }
+ if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", island, island->name(), target->name(), atom->name());
+ fixupWithTarget->u.target = island;
+ fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound;
+ }
+ else if ( displacement > kBranchLimit ) {
// create forward branch chain
const ld::Atom* nextTarget = target;
+ if (_s_log) fprintf(stderr, "need forward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n",
+ srcAddr, dstAddr, target->name());
for (int i=kIslandRegionsCount-1; i >=0 ; --i) {
AtomToIsland* region = regionsMap[i];
- int64_t islandRegionAddr = kBetweenRegions * (i+1);
- if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) {
+ int64_t islandRegionAddr = regionAddresses[i];
+ if ( (srcAddr < islandRegionAddr) && ((islandRegionAddr <= dstAddr)) ) {
AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
if ( pos == region->end() ) {
- ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset);
+ ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset, atom->section(), false);
(*region)[finalTargetAndOffset] = island;
- if (_s_log) fprintf(stderr, "added island %s to region %d for %s\n", island->name(), i, atom->name());
+ if (_s_log) fprintf(stderr, "added forward branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name());
regionsIslands[i]->push_back(island);
++islandCount;
nextTarget = island;
}
}
}
- if (_s_log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->name(), target->name(), atom->name());
+ if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", nextTarget, nextTarget->name(), target->name(), atom->name());
fixupWithTarget->u.target = nextTarget;
fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound;
}
const ld::Atom* prevTarget = target;
for (int i=0; i < kIslandRegionsCount ; ++i) {
AtomToIsland* region = regionsMap[i];
- int64_t islandRegionAddr = kBetweenRegions * (i+1);
- if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) {
+ int64_t islandRegionAddr = regionAddresses[i];
+ if ( (dstAddr < islandRegionAddr) && (islandRegionAddr <= srcAddr) ) {
+ if (_s_log) fprintf(stderr, "need backward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", srcAddr, dstAddr, target->name());
AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
if ( pos == region->end() ) {
- ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset);
+ ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset, atom->section(), false);
(*region)[finalTargetAndOffset] = island;
- if (_s_log) fprintf(stderr, "added back island %s to region %d for %s\n", island->name(), i, atom->name());
+ if (_s_log) fprintf(stderr, "added back branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name());
regionsIslands[i]->push_back(island);
++islandCount;
prevTarget = island;
}
}
}
- if (_s_log) fprintf(stderr, "using back island %s for %s\n", prevTarget->name(), atom->name());
+ if (_s_log) fprintf(stderr, "using back island %p %s for %s\n", prevTarget, prevTarget->name(), atom->name());
fixupWithTarget->u.target = prevTarget;
fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound;
}
std::vector<const ld::Atom*> newAtomList;
newAtomList.reserve(textSection->atoms.size()+islandCount);
- uint64_t regionIndex = 0;
+ int regionIndex = 0;
for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ait++) {
- newAtomList.push_back(*ait);
- // copy over atoms until we find an island insertion point
- // Note that the last insertion point is the last atom, so this loop never moves the iterator to atoms.end().
- while (*ait != branchIslandInsertionPoints[regionIndex]) {
- ait++;
- newAtomList.push_back(*ait);
+ const ld::Atom* atom = *ait;
+ newAtomList.push_back(atom);
+ if ( (regionIndex < kIslandRegionsCount) && (atom == branchIslandInsertionPoints[regionIndex]) ) {
+ std::vector<const ld::Atom*>* islands = regionsIslands[regionIndex];
+ newAtomList.insert(newAtomList.end(), islands->begin(), islands->end());
+ ++regionIndex;
}
-
- // insert the branch island atoms after the insertion point atom
- std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex];
- for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
- const ld::Atom* islandAtom = *rit;
- newAtomList.push_back(islandAtom);
- if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name());
- }
- regionIndex++;
}
// swap in new list of atoms for __text section
textSection->atoms.clear();
}
+static void buildAddressMap(const Options& opts, ld::Internal& state) {
+ // Assign addresses to sections
+ state.setSectionSizesAndAlignments();
+ state.assignFileOffsets();
+
+ // Assign addresses to atoms in a side table
+ const bool log = false;
+ if ( log ) fprintf(stderr, "buildAddressMap()\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ uint16_t maxAlignment = 0;
+ uint64_t offset = 0;
+ if ( log ) fprintf(stderr, " section=%s/%s, address=0x%08llX\n", sect->segmentName(), sect->sectionName(), sect->address);
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2;
+ uint32_t atomModulus = atom->alignment().modulus;
+ if ( atomAlignmentPowerOf2 > maxAlignment )
+ maxAlignment = atomAlignmentPowerOf2;
+ // calculate section offset for this atom
+ uint64_t alignment = 1 << atomAlignmentPowerOf2;
+ uint64_t currentModulus = (offset % alignment);
+ uint64_t requiredModulus = atomModulus;
+ if ( currentModulus != requiredModulus ) {
+ if ( requiredModulus > currentModulus )
+ offset += requiredModulus-currentModulus;
+ else
+ offset += requiredModulus+alignment-currentModulus;
+ }
+
+ if ( log ) fprintf(stderr, " 0x%08llX atom=%p, name=%s\n", sect->address+offset, atom, atom->name());
+ sAtomToAddress[atom] = sect->address + offset;
+
+ offset += atom->size();
+ }
+ }
+
+
+}
+
+void doPass(const Options& opts, ld::Internal& state)
+{
+ // only make branch islands in final linked images
+ if ( opts.outputKind() == Options::kObjectFile )
+ return;
+
+ // Allow user to disable branch island generation
+ if ( !opts.allowBranchIslands() )
+ return;
+
+ // only ARM needs branch islands
+ switch ( opts.architecture() ) {
+ case CPU_TYPE_ARM:
+ break;
+ default:
+ return;
+ }
+
+ if ( opts.outputKind() == Options::kPreload ) {
+ buildAddressMap(opts, state);
+ }
+
+ // scan sections and add island to each code section
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() == ld::Section::typeCode )
+ makeIslandsForSection(opts, state, sect);
+ }
+}
+
+
} // namespace branch_island
} // namespace passes
} // namespace ld
// calculate worst case size for all unwind info pages when allocating buffer
const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry);
assert(uniqueEntries.size() > 0);
- const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 1;
+ const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 2;
_pagesForDelete = (uint8_t*)calloc(pageCount,4096);
if ( _pagesForDelete == NULL ) {
warning("could not allocate space for compact unwind info");
secondLevelPagesStarts[secondLevelPageCount] = pageEnd;
secondLevelFirstFuncs[secondLevelPageCount] = uniqueEntries[endIndex].func;
++secondLevelPageCount;
- pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size
+ // if this requires more than one page, align so that next starts on page boundary
+ if ( (pageSize != 4096) && (endIndex > 0) ) {
+ pageEnd = (uint8_t*)((uintptr_t)(pageEnd) & -4096);
+ pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size
+ }
}
_pages = pageEnd;
_pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd;
warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() );
}
-
// <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zeroed data
if ( ! moveToData.empty() ) {
+ // <rdar://problem/14919139> only move zero fill symbols to __data if there is a __data section
+ ld::Internal::FinalSection* dataSect = NULL;
for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
- switch ( sect->type() ) {
- case ld::Section::typeZeroFill:
- case ld::Section::typeTentativeDefs:
- sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end());
- break;
- case ld::Section::typeUnclassified:
- if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) )
- sect->atoms.insert(sect->atoms.end(), moveToData.begin(), moveToData.end());
- break;
- default:
- break;
+ if ( sect->type() == ld::Section::typeUnclassified ) {
+ if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) )
+ dataSect = sect;
+ }
+ }
+
+ if ( dataSect != NULL ) {
+ // add atoms to __data
+ dataSect->atoms.insert(dataSect->atoms.end(), moveToData.begin(), moveToData.end());
+ // remove atoms from original sections
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ switch ( sect->type() ) {
+ case ld::Section::typeZeroFill:
+ case ld::Section::typeTentativeDefs:
+ sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end());
+ break;
+ default:
+ break;
+ }
}
}
}
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedImageCache(pass)),
_fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedImageCache(pass)),
_fixup3(12, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedFastBinder(pass)),
- _fixup4(16, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedFastBinder(pass))
+ _fixup4(16, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedFastBinder(pass)),
+ _fixup5(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_ADD, 0, 4),
+ _fixup6(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 12, 16)
{ pass.addAtom(*this); }
virtual ld::File* file() const { return NULL; }
}
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
- virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup6)[1]; }
private:
static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) {
ld::Fixup _fixup2;
ld::Fixup _fixup3;
ld::Fixup _fixup4;
+ ld::Fixup _fixup5;
+ ld::Fixup _fixup6;
static ld::Section _s_section;
};
_stubTo(stubTo),
_lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport),
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer),
- _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer)
- { pass.addAtom(*this); }
+ _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer),
+ _fixup3(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 0, 4)
+ { pass.addAtom(*this); }
virtual const ld::File* file() const { return _stubTo.file(); }
virtual const char* name() const { return _stubTo.name(); }
}
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
- virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; }
private:
const ld::Atom& _stubTo;
LazyPointerAtom _lazyPointer;
mutable ld::Fixup _fixup1;
mutable ld::Fixup _fixup2;
+ mutable ld::Fixup _fixup3;
static ld::Section _s_section;
};
case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad:
case ld::Fixup::kindStoreX86PCRel32TLVLoad:
case ld::Fixup::kindStoreX86Abs32TLVLoad:
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+#endif
ref.fixupWithTLVStore = fit;
break;
default:
case ld::Fixup::kindStoreX86Abs32TLVLoad:
it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA;
break;
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+ it->fixupWithTLVStore->kind = ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21;
+ break;
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+ it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12;
+ break;
+#endif
default:
assert(0 && "bad store kind for TLV optimization");
}
case ld::Fixup::kindSetLazyOffset:
printf("offset of lazy binding info for %s", referenceTargetAtomName(ref));
break;
+ case ld::Fixup::kindIslandTarget:
+ printf("ultimate target of island %s", referenceTargetAtomName(ref));
+ break;
case ld::Fixup::kindDataInCodeStartData:
printf("start of data in code");
break;
case ld::Fixup::kindDataInCodeEnd:
printf("end of data in code");
break;
+ case ld::Fixup::kindLinkerOptimizationHint:
+#if SUPPORT_ARCH_arm64
+ ld::Fixup::LOH_arm64 extra;
+ extra.addend = ref->u.addend;
+ printf("ARM64 hint: ");
+ switch(extra.info.kind) {
+ case LOH_ARM64_ADRP_ADRP:
+ printf("ADRP-ADRP");
+ break;
+ case LOH_ARM64_ADRP_LDR:
+ printf("ADRP-LDR");
+ break;
+ case LOH_ARM64_ADRP_ADD_LDR:
+ printf("ADRP-ADD-LDR");
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_LDR:
+ printf("ADRP-LDR-GOT-LDR");
+ break;
+ case LOH_ARM64_ADRP_ADD_STR:
+ printf("ADRP-ADD-STR");
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_STR:
+ printf("ADRP-LDR-GOT-STR");
+ break;
+ case LOH_ARM64_ADRP_ADD:
+ printf("ADRP-ADD");
+ break;
+ default:
+ printf("kind=%d", extra.info.kind);
+ break;
+ }
+ printf(", offset1=0x%X", (extra.info.delta1 << 2) + ref->offsetInAtom);
+ if ( extra.info.count > 0 )
+ printf(", offset2=0x%X", (extra.info.delta2 << 2) + ref->offsetInAtom);
+ if ( extra.info.count > 1 )
+ printf(", offset3=0x%X", (extra.info.delta3 << 2) + ref->offsetInAtom);
+ if ( extra.info.count > 2 )
+ printf(", offset4=0x%X", (extra.info.delta4 << 2) + ref->offsetInAtom);
+#endif
+ break;
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref));
break;
case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
printf("ARM64 store 12-bit page offset of %s", referenceTargetAtomName(ref));
break;
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPage21:
+ printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref));
+ break;
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPageOff12:
+ printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref));
+ break;
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
printf("ARM64 store 21-bit pcrel ADRP to GOT for %s", referenceTargetAtomName(ref));
break;
objOpts.warnUnwindConversionProblems = true;
objOpts.keepDwarfUnwind = false;
objOpts.forceDwarfConversion = false;
+ objOpts.verboseOptimizationHints = true;
objOpts.subType = sPreferredSubArch;
#if 1
if ( ! foundFatSlice ) {
return objResult;
// see if it is an llvm object file
- objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false);
+ objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false, true);
if ( objResult != NULL )
return objResult;
// success
return NULL;
}
-
-
-// <rdar://problem/12764051> Switch libprunetrie to use libc++ instead of libstdc++
-// The one undefined when building libprunetrie.a with libc++ is throw_length_error().
-// Adding this define here resolves that and means libprunetrie.a can be linked
-// by cctools with libc++ or libstdc++.
-extern "C" void foobar() __asm("__ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv");
-void foobar()
-{
- throw "Size of vecor cannot be grown";
-}
-
-
char* cummulativeString, int curStrOffset)
{
const uint8_t* const me = p;
- const uint8_t terminalSize = read_uleb128(p, end);
+ const uint64_t terminalSize = read_uleb128(p, end);
const uint8_t* children = p + terminalSize;
if ( terminalSize != 0 ) {
uint32_t flags = read_uleb128(p, end);
LD = ld
OBJECTDUMP = ObjectDump
MACHOCHECK = machocheck
-OTOOL = otool
+OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/otool
REBASE = rebase
DYLDINFO = dyldinfo
CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra}
CXXFLAGS = -Wall -stdlib=libc++
-IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path)
+IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path 2>/dev/null)
ifeq ($(ARCH),armv6)
LDFLAGS := -syslibroot $(IOS_SDK)
ifeq ($(ARCH),arm64)
LDFLAGS := -syslibroot $(IOS_SDK)
- CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK)
+ CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK)
+ #CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK)
+ #CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/clang-loh -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK)
CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0
VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
LD_SYSROOT = -syslibroot $(IOS_SDK)
LD_NEW_LINKEDIT = -ios_version_min 7.0
+ OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/otool
+ #OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool)
else
FILEARCH = $(ARCH)
endif
all:
# Verify that we fail if there is no valid place to insert branch islands.
- ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN}
+ #${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN}
- ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS}
+ ${CC} ${CCFLAGS} hello.c space.s extra.c -Os -o hello ${ARCH_FLAGS}
${PASS_IFF_GOOD_MACHO} hello
#include <stdio.h>
+extern void back();
void foo()
{
fprintf(stdout, "foo\n");
+ back();
}
int main()
{
fprintf(stdout, "hello\n");
- foo();
+ foo();
return 0;
}
+void back()
+{
+}
\ No newline at end of file
#if __thumb2__
// thumb2 branches are +/- 16MB
_space1:
- .space 14*1024*1024
+ .space 13*1024*1024
_space2:
- .space 14*1024*1024
+ .space 13*1024*1024
_space3:
- .space 14*1024*1024
+ .space 13*1024*1024
#elif __thumb__
--- /dev/null
+##
+# Copyright (c) 2013 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+
+#
+# test thumb2 branch ranges
+#
+
+run: all
+
+all: all-${ARCH}
+
+all-i386:
+ ${PASS_IFF} true
+
+all-x86_64:
+ ${PASS_IFF} true
+
+all-armv7:
+ ${CC} ${CCFLAGS} -static foo.c -c
+ ${CC} ${CCFLAGS} bar.s -c
+ # verify islands are created and used
+ ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar -nostdlib -Wl,-preload -segaddr __MY 0x3000000
+ ${OTOOL} -s __MY __text -V foobar | grep '_myweak1.island:' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -s __MY __text -V foobar | grep '_foo2.island:' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -s __MY __text -V foobar | grep '_foo.island:' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -s __MY __text -V foobar | grep "bl\t_foo.island" | ${FAIL_IF_EMPTY}
+ # verify that is close enough islands are bypassed and target called directly
+ ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar2 -nostdlib -Wl,-preload -segaddr __MY 0x30000
+ ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_foo2' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_myweak1' | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} true
+
+clean:
+ rm *.o foobar*
--- /dev/null
+
+#if __arm__
+
+ .section __MY,__text,regular,pure_instructions
+ .align 4
+
+#if __thumb__
+ .thumb_func _bar
+ .code 16
+#endif
+ .globl _bar
+_bar:
+ nop
+ bl _foo
+ blx _foo2
+ bl _myweak1
+
+
+#endif // __arm__
+
+
+
+ .subsections_via_symbols
+
\ No newline at end of file
--- /dev/null
+
+int x = 1;
+int y = 2;
+
+__attribute__((weak))
+void myweak1()
+{
+}
+
+int foo()
+{
+ myweak1();
+ return 1;
+}
+
+int foo1()
+{
+ return x;
+}
+
+int foo2()
+{
+ return y;
+}
+
# Validate cpu subtypes processing
#
+CC_ARM = $(shell xcrun -find clang) -miphoneos-version-min=5.0 -isysroot ${IOS_SDK}
+
test: test-${ARCH}
test-ppc64:
test-armv7: test-arm
test-arm:
- clang foo.c -arch armv4t -c -o foo-v4.o
+ ${CC_ARM} foo.c -arch armv4t -c -o foo-v4.o
${FAIL_IF_BAD_OBJ} foo-v4.o
- clang foo.c -arch armv5 -c -o foo-v5.o
+ ${CC_ARM} foo.c -arch armv5 -c -o foo-v5.o
${FAIL_IF_BAD_OBJ} foo-v5.o
- clang foo.c -arch armv6 -c -o foo-v6.o
+ ${CC_ARM} foo.c -arch armv6 -c -o foo-v6.o
${FAIL_IF_BAD_OBJ} foo-v6.o
- clang foo.c -arch armv7 -c -o foo-v7.o
+ ${CC_ARM} foo.c -arch armv7 -c -o foo-v7.o
${FAIL_IF_BAD_OBJ} foo-v7.o
- clang foo.c -arch xscale -c -o foo-xscale.o
+ ${CC_ARM} foo.c -arch xscale -c -o foo-xscale.o
${FAIL_IF_BAD_OBJ} foo-xscale.o
- clang main.c -arch armv4t -c -o main-v4.o
+ ${CC_ARM} main.c -arch armv4t -c -o main-v4.o
${FAIL_IF_BAD_OBJ} main-v4.o
- clang main.c -arch armv5 -c -o main-v5.o
+ ${CC_ARM} main.c -arch armv5 -c -o main-v5.o
${FAIL_IF_BAD_OBJ} main-v5.o
- clang main.c -arch armv6 -c -o main-v6.o
+ ${CC_ARM} main.c -arch armv6 -c -o main-v6.o
${FAIL_IF_BAD_OBJ} main-v6.o
- clang main.c -arch xscale -c -o main-xscale.o
+ ${CC_ARM} main.c -arch xscale -c -o main-xscale.o
${FAIL_IF_BAD_OBJ} main-xscale.o
- clang main.c -arch armv7 -c -o main-v7.o
+ ${CC_ARM} main.c -arch armv7 -c -o main-v7.o
${FAIL_IF_BAD_OBJ} main-v7.o
# check V4+V4 -> V4
- ${LD} -r main-v4.o foo-v4.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY}
+# ${LD} -r main-v4.o foo-v4.o -o all.o
+# ${FAIL_IF_BAD_OBJ} all.o
+# otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY}
# check V4+V5 -> V5
#${LD} -r main-v4.o foo-v5.o -o all.o
#otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
# check V5+V5 -> V5
- ${LD} -r main-v5.o foo-v5.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY}
+# ${LD} -r main-v5.o foo-v5.o -o all.o
+# ${FAIL_IF_BAD_OBJ} all.o
+# otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY}
# check V5+V6 -> V6
#${LD} -r main-v5.o foo-v6.o -o all.o
${FAIL_IF_BAD_MACHO} test
${CC} ${CCFLAGS} test.c -DDSO_DEF=1 -o test-def 2>warnings.txt
${FAIL_IF_BAD_MACHO} test-def
- ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent
+ ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent -Wl,-w
${PASS_IFF_GOOD_MACHO} test-tent
#
# <rdar://problem/5726215> comdat warnings in ld -r
#
-# also use -falign-functions to force an out of order coalesing
#
run: all
all:
${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o
- ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32
+ ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o
${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o
${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log
grep warning warnings.log | ${PASS_IFF_EMPTY}
--- /dev/null
+
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, _foo@PAGE
+L2: add x0, x0, _foo@PAGEOFF
+ nop
+
+ .loh AdrpAdd L1, L2
+
+#if PADDING
+_pad:
+ .space 1100000
+#endif
+
+#if FOO_AS_CONST
+ .const
+#endif
+
+#if FOO_AS_DATA
+ .data
+#endif
+
+_foo: .long 0
+
--- /dev/null
+
+#ifndef ADDEND
+ #define ADDEND 0
+#endif
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, _foo@PAGE
+L2: add x0, x0, _foo@PAGEOFF
+#if LOAD_GPR_8
+L3: ldr b1, [x0, #ADDEND]
+#elif LOAD_GPR_16
+L3: ldr h1, [x0, #ADDEND]
+#elif LOAD_GPR_32
+L3: ldr w1, [x0, #ADDEND]
+#elif LOAD_GPR_64
+L3: ldr x1, [x0, #ADDEND]
+#elif LOAD_FPR_32
+L3: ldr s1, [x0, #ADDEND]
+#elif LOAD_FPR_64
+L3: ldr d1, [x0, #ADDEND]
+#elif LOAD_VEC_128
+L3: ldr q1, [x0, #ADDEND]
+#endif
+ nop
+
+ .loh AdrpAddLdr L1, L2, L3
+
+#if PADDING
+_pad:
+ .space 1100000
+#endif
+
+#if FOO_AS_CONSTANT
+ .literal4
+#endif
+
+#if FOO_AS_DATA
+ .data
+_makePageOffsetNonZero: .long 0,0,0,0
+#endif
+
+#if MISALIGN_DATA
+_junk: .byte 0
+#endif
+
+_foo: .long 0
+ .long 0
+_8foo8: .long 0
+ .long 0
+_16foo16: .long 0
+
+
--- /dev/null
+
+#ifndef ADDEND
+ #define ADDEND 0
+#endif
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, _foo@PAGE
+L2: add x0, x0, _foo@PAGEOFF
+#if LOAD_GPR_8
+L3: str b1, [x0, #ADDEND]
+#elif LOAD_GPR_16
+L3: str h1, [x0, #ADDEND]
+#elif LOAD_GPR_32
+L3: str w1, [x0, #ADDEND]
+#elif LOAD_GPR_64
+L3: str x1, [x0, #ADDEND]
+#elif LOAD_FPR_32
+L3: str s1, [x0, #ADDEND]
+#elif LOAD_FPR_64
+L3: str d1, [x0, #ADDEND]
+#elif LOAD_VEC_128
+L3: str q1, [x0, #ADDEND]
+#endif
+ nop
+
+ .loh AdrpAddStr L1, L2, L3
+
+ .data
+_makePageOffsetNonZero: .long 0,0,0,0
+
+#if MISALIGN_DATA
+_junk: .byte 0
+#endif
+
+_foo: .long 0
+ .long 0
+_8foo8: .long 0
+ .long 0
+_16foo16: .long 0
+
+
--- /dev/null
+
+#ifndef ADDEND
+ #define ADDEND
+#endif
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, _foo ADDEND@PAGE
+#if LOAD_GPR_8
+L3: ldr b1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_16
+L3: ldr h1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_32
+L3: ldr w1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_32_S16
+L3: ldrsh w1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_32_S8
+L3: ldrsb w1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_64
+L3: ldr x1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_64_S32
+L3: ldrsw x1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_64_S16
+L3: ldrsh x1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_GPR_64_S8
+L3: ldrsb x1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_FPR_32
+L3: ldr s1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_FPR_64
+L3: ldr d1, [x0, _foo ADDEND@PAGEOFF]
+#elif LOAD_VEC_128
+L3: ldr q1, [x0, _foo ADDEND@PAGEOFF]
+#endif
+ nop
+
+ .loh AdrpLdr L1, L3
+
+#if PADDING
+_pad:
+ .space 1100000
+#endif
+
+#if FOO_AS_CONSTANT
+ .literal4
+#endif
+
+#if FOO_AS_DATA
+ .data
+_makePageOffsetNonZero: .long 0,0,0,0
+#endif
+
+#if MISALIGN_DATA
+_junk: .byte 0
+#endif
+
+_foo: .long 0
+ .long 0
+_8foo8: .long 0
+ .long 0
+_16foo16: .long 0
+
+
--- /dev/null
+
+#ifndef TARGET
+ #define TARGET _malloc
+#endif
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, TARGET@GOTPAGE
+L2: ldr x1, [x0, #TARGET@GOTPAGEOFF]
+ nop
+
+ .loh AdrpLdrGot L1, L2
+
+#if PADDING
+_pad:
+ .space 1100000
+#endif
+
+_fooCode:
+ nop
+
+
+ .data
+_makePageOffsetNonZero: .long 0,0,0,0
+
+_fooData: .long 0
+
+
--- /dev/null
+
+#ifndef TARGET
+ #define TARGET _foo
+#endif
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, TARGET@GOTPAGE
+L2: ldr x1, [x0, #TARGET@GOTPAGEOFF]
+#if LOAD_GPR_8
+L3: ldr b2, [x1]
+#elif LOAD_GPR_16
+L3: ldr h2, [x1]
+#elif LOAD_GPR_32
+L3: ldr w2, [x1]
+#elif LOAD_GPR_64
+L3: ldr x2, [x1]
+#elif LOAD_FPR_32
+L3: ldr s2, [x1]
+#elif LOAD_FPR_64
+L3: ldr d2, [x1]
+#elif LOAD_VEC_128
+L3: ldr q2, [x1]
+#endif
+ nop
+
+ .loh AdrpLdrGotLdr L1, L2, L3
+
+#if PADDING
+_pad:
+ .space 1100000
+#endif
+
+#if FOO_AS_CONST
+ .const
+ .align 4
+#endif
+
+#if FOO_AS_DATA
+ .data
+_makePageOffsetNonZero: .long 0,0,0,0
+#endif
+
+#if MISALIGN_DATA
+_junk: .byte 0
+#endif
+
+_foo: .long 0
+ .long 0
+_8foo8: .long 0
+ .long 0
+_16foo16: .long 0
+
+
--- /dev/null
+
+#ifndef TARGET
+ #define TARGET _foo
+#endif
+
+ .text
+ .align 2
+_test:
+ nop
+L1: adrp x0, TARGET@GOTPAGE
+L2: ldr x1, [x0, #TARGET@GOTPAGEOFF]
+#if LOAD_GPR_8
+L3: str b2, [x1]
+#elif LOAD_GPR_16
+L3: str h2, [x1]
+#elif LOAD_GPR_32
+L3: str w2, [x1]
+#elif LOAD_GPR_64
+L3: str x2, [x1]
+#elif LOAD_FPR_32
+L3: str s2, [x1]
+#elif LOAD_FPR_64
+L3: str d2, [x1]
+#elif LOAD_VEC_128
+L3: str q2, [x1]
+#endif
+ nop
+
+ .loh AdrpLdrGotStr L1, L2, L3
+
+#if PADDING
+_pad:
+ .space 1100000
+#endif
+
+ .data
+_makePageOffsetNonZero: .long 0,0,0,0
+
+#if MISALIGN_DATA
+_junk: .byte 0
+#endif
+
+_foo: .long 0
+ .long 0
+_8foo8: .long 0
+ .long 0
+_16foo16: .long 0
+
+
--- /dev/null
+##
+# Copyright (c) 2013 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Check that linker does/doesnot apply optimization hints
+#
+
+all: all-${ARCH}
+
+all-i386: skip
+all-x86_64: skip
+all-armv6: skip
+all-armv7: skip
+
+all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpAddStr AdrpLdrGotStr AdrpLdrGot
+
+main.o:
+ ${CC} ${CCFLAGS} main.s -c -o main.o
+
+AdrpAdd: main.o
+ # test ADRP/ADD -> ADR when target is in __TEXT
+ ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd1.o -DFOO_AS_CONST=1
+ ${CC} ${CCFLAGS} AdrpAdd1.o main.o -o AdrpAdd1.exe
+ ${OTOOL} -tV AdrpAdd1.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd1.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAdd1.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ # test ADRP/ADD -> ADR when target is in __DATA and main executable
+ ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd2.o -DFOO_AS_DATA=1
+ ${CC} ${CCFLAGS} AdrpAdd2.o main.o -o AdrpAdd2.exe
+ ${OTOOL} -tV AdrpAdd2.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd2.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAdd2.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ # test ADRP/ADD -> ADR when target is in __DATA and dylib
+ ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd3.o -DFOO_AS_DATA=1
+ ${CC} ${CCFLAGS} AdrpAdd3.o -dynamiclib -o AdrpAdd3.dylib
+ ${OTOOL} -tV AdrpAdd3.dylib | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd3.dylib | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAdd3.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ # test ADRP/ADD !-> when target is in __DATA and dylib in shared region
+ ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd4.o -DFOO_AS_DATA=1
+ ${CC} ${CCFLAGS} AdrpAdd4.o -dynamiclib -o AdrpAdd4.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAdd4.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd4.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd4.dylib | grep 'adr x0' | ${FAIL_IF_STDIN}
+ # test ADRP/ADD !-> when target is in __TEXT but too far
+ ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd5.o -DFOO_AS_CONST=1 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAdd5.o main.o -o AdrpAdd5.exe
+ ${OTOOL} -tV AdrpAdd5.exe | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd5.exe | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAdd5.exe | grep 'adr x0' | ${FAIL_IF_STDIN}
+
+
+
+AdrpAddLdr: AdrpAddLdr-ldr AdrpAddLdr-far AdrpAddLdr-seg AdrpAddLdr-align AdrpAddLdr-addend
+ true
+
+AdrpAddLdr-ldr: main.o
+ # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g8.o main.o -o AdrpAddLdr-ldr-g8.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g16.o main.o -o AdrpAddLdr-ldr-g16.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g32.o -DFOO_AS_CONST -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g32.o main.o -o AdrpAddLdr-ldr-g32.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'ldr w1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g64.o main.o -o AdrpAddLdr-ldr-g64.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-f32.o -DFOO_AS_CONST -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-f32.o main.o -o AdrpAddLdr-ldr-f32.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'ldr s1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-f64.o -DFOO_AS_CONST -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-f64.o main.o -o AdrpAddLdr-ldr-f64.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'ldr d1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-v128.o -DFOO_AS_CONST -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpAddLdr-ldr-v128.o main.o -o AdrpAddLdr-ldr-v128.exe
+ ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'ldr q1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+AdrpAddLdr-far: main.o
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-g8.o main.o -o AdrpAddLdr-far-g8.exe
+ ${OTOOL} -tV AdrpAddLdr-far-g8.exe | grep 'ldr b1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-g16.o main.o -o AdrpAddLdr-far-g16.exe
+ ${OTOOL} -tV AdrpAddLdr-far-g16.exe | grep 'ldr h1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-g32.o main.o -o AdrpAddLdr-far-g32.exe
+ ${OTOOL} -tV AdrpAddLdr-far-g32.exe | grep 'ldr w1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-g64.o main.o -o AdrpAddLdr-far-g64.exe
+ ${OTOOL} -tV AdrpAddLdr-far-g64.exe | grep 'ldr x1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-f32.o main.o -o AdrpAddLdr-far-f32.exe
+ ${OTOOL} -tV AdrpAddLdr-far-f32.exe | grep 'ldr s1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-f64.o main.o -o AdrpAddLdr-far-f64.exe
+ ${OTOOL} -tV AdrpAddLdr-far-f64.exe | grep 'ldr d1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING
+ ${CC} ${CCFLAGS} AdrpAddLdr-far-v128.o main.o -o AdrpAddLdr-far-v128.exe
+ ${OTOOL} -tV AdrpAddLdr-far-v128.exe | grep 'ldr q1, \[x0, ' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-far-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+
+AdrpAddLdr-align: main.o
+ # test ADRP/ADD/LD -> ADR/LD when target is for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-g8.o main.o -o AdrpAddLdr-align-g8.exe
+ ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-g16.o main.o -o AdrpAddLdr-align-g16.exe
+ ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'ldr h1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-g32.o main.o -o AdrpAddLdr-align-g32.exe
+ ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'ldr w1, \[x0\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-g64.o main.o -o AdrpAddLdr-align-g64.exe
+ ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'ldr x1, \[x0\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-f32.o main.o -o AdrpAddLdr-align-f32.exe
+ ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'ldr s1, \[x0\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-f64.o main.o -o AdrpAddLdr-align-f64.exe
+ ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'ldr d1, \[x0\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddLdr-align-v128.o main.o -o AdrpAddLdr-align-v128.exe
+ ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'ldr q1, \[x0\]' | ${FAIL_IF_EMPTY}
+
+
+
+AdrpAddLdr-seg:
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-g8.o -dynamiclib -o AdrpAddLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr b1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-g16.o -dynamiclib -o AdrpAddLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr h1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-g32.o -dynamiclib -o AdrpAddLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr w1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-g64.o -dynamiclib -o AdrpAddLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr x1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-f32.o -dynamiclib -o AdrpAddLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr s1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-f64.o -dynamiclib -o AdrpAddLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr d1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in movable segmentfor 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpAddLdr-seg-v128.o -dynamiclib -o AdrpAddLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr q1, \[x0,' | ${FAIL_IF_EMPTY}
+
+
+
+AdrpAddLdr-addend:
+ # test ADRP/ADD/LD -> ADRP/LD when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-g8.o main.o -o AdrpAddLdr-addend-g8.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> ADRP/LD when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-g16.o main.o -o AdrpAddLdr-addend-g16.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-g32.o main.o -o AdrpAddLdr-addend-g32.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'ldr w1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-g64.o main.o -o AdrpAddLdr-addend-g64.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'ldr x1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-f32.o main.o -o AdrpAddLdr-addend-f32.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'ldr s1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-f64.o main.o -o AdrpAddLdr-addend-f64.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'ldr d1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/LD -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DADDEND=16
+ ${CC} ${CCFLAGS} AdrpAddLdr-addend-v128.o main.o -o AdrpAddLdr-addend-v128.exe
+ ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'ldr q1, _16foo16' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+
+
+AdrpLdr: AdrpLdr-ldr AdrpLdr-seg AdrpLdr-addend
+ true
+
+AdrpLdr-ldr: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g8.o main.o -o AdrpLdr-ldr-g8.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g16.o main.o -o AdrpLdr-ldr-g16.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g16.exe | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32.o -DFOO_AS_CONST -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g32.o main.o -o AdrpLdr-ldr-g32.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'ldr w1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR left untouched when target is in __TEXT for 32-bit load from signed 8-bit value
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32s8.o -DFOO_AS_CONST -DLOAD_GPR_32_S8
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g32s8.o main.o -o AdrpLdr-ldr-g32s8.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g32s8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g32s8.exe | grep 'ldrsb\tw1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load from signed 16-bit value
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32s16.o -DFOO_AS_CONST -DLOAD_GPR_32_S16
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g32s16.o main.o -o AdrpLdr-ldr-g32s16.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g32s16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g32s16.exe | grep 'ldrsh\tw1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g64.o main.o -o AdrpLdr-ldr-g64.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR left untouched when target is in __TEXT for 64-bit load from signed 8-bit value
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s8.o -DFOO_AS_CONST -DLOAD_GPR_64_S8
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s8.o main.o -o AdrpLdr-ldr-g64s8.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g64s8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g64s8.exe | grep 'ldrsb\tx1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR left untouched when target is in __TEXT for 64-bit load from signed 16-bit value
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s16.o -DFOO_AS_CONST -DLOAD_GPR_64_S16
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s16.o main.o -o AdrpLdr-ldr-g64s16.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g64s16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g64s16.exe | grep 'ldrsh\tx1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load from signed 32-bit value
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s32.o -DFOO_AS_CONST -DLOAD_GPR_64_S32
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s32.o main.o -o AdrpLdr-ldr-g64s32.exe
+ ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'ldrsw x1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-f32.o -DFOO_AS_CONST -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-f32.o main.o -o AdrpLdr-ldr-f32.exe
+ ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'ldr s1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-f64.o -DFOO_AS_CONST -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-f64.o main.o -o AdrpLdr-ldr-f64.exe
+ ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'ldr d1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-v128.o -DFOO_AS_CONST -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpLdr-ldr-v128.o main.o -o AdrpLdr-ldr-v128.exe
+ ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'ldr q1, _foo' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+AdrpLdr-seg:
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpLdr-seg-g8.o -dynamiclib -o AdrpLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-g8.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-g8.dylib | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpLdr-seg-g16.o -dynamiclib -o AdrpLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-g16.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-g16.dylib | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpLdr-seg-g32.o -dynamiclib -o AdrpLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-g32.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-g32.dylib | grep 'ldr\tw1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpLdr-seg-g64.o -dynamiclib -o AdrpLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-g64.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-g64.dylib | grep 'ldr\tx1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpLdr-seg-f32.o -dynamiclib -o AdrpLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-f32.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-f32.dylib | grep 'ldr\ts1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpLdr-seg-f64.o -dynamiclib -o AdrpLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-f64.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-f64.dylib | grep 'ldr\td1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpLdr-seg-v128.o -dynamiclib -o AdrpLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpLdr-seg-v128.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-seg-v128.dylib | grep 'ldr\tq1, \[x0,' | ${FAIL_IF_EMPTY}
+
+AdrpLdr-addend: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 -DADDEND=+8
+ ${CC} ${CCFLAGS} AdrpLdr-addend-g8.o main.o -o AdrpLdr-addend-g8.exe
+ ${OTOOL} -tV AdrpLdr-addend-g8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-g8.exe | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 -DADDEND=+8
+ ${CC} ${CCFLAGS} AdrpLdr-addend-g16.o main.o -o AdrpLdr-addend-g16.exe
+ ${OTOOL} -tV AdrpLdr-addend-g16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-g16.exe | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 -DADDEND=+8
+ ${CC} ${CCFLAGS} AdrpLdr-addend-g32.o main.o -o AdrpLdr-addend-g32.exe
+ ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'ldr w1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 -DADDEND=+8
+ ${CC} ${CCFLAGS} AdrpLdr-addend-g64.o main.o -o AdrpLdr-addend-g64.exe
+ ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'ldr x1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 -DADDEND=+8
+ ${CC} ${CCFLAGS} AdrpLdr-addend-f32.o main.o -o AdrpLdr-addend-f32.exe
+ ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'ldr s1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 -DADDEND=+8
+ ${CC} ${CCFLAGS} AdrpLdr-addend-f64.o main.o -o AdrpLdr-addend-f64.exe
+ ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'ldr d1, _8foo8' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 -DADDEND=+16
+ ${CC} ${CCFLAGS} AdrpLdr-addend-v128.o main.o -o AdrpLdr-addend-v128.exe
+ ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'ldr q1, _16foo16' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+AdrpLdrGotLdr: AdrpLdrGotLdr-extern AdrpLdrGotLdr-externfargot AdrpLdrGotLdr-near AdrpLdrGotLdr-far AdrpLdrGotLdr-nearunaligned AdrpLdrGotLdr-farunaligned
+ true
+
+AdrpLdrGotLdr-extern: main.o
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g8.o main.o -o AdrpLdrGotLdr-extern-g8.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g16.o main.o -o AdrpLdrGotLdr-extern-g16.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g32.o main.o -o AdrpLdrGotLdr-extern-g32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g64.o main.o -o AdrpLdrGotLdr-extern-g64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-f32.o main.o -o AdrpLdrGotLdr-extern-f32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-f64.o main.o -o AdrpLdrGotLdr-extern-f64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-v128.o main.o -o AdrpLdrGotLdr-extern-v128.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdr-externfargot: main.o
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g8.o main.o -o AdrpLdrGotLdr-externfargot-g8.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g16.o main.o -o AdrpLdrGotLdr-externfargot-g16.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot farfor 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g32.o main.o -o AdrpLdrGotLdr-externfargot-g32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g64.o main.o -o AdrpLdrGotLdr-externfargot-g64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-f32.o main.o -o AdrpLdrGotLdr-externfargot-f32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-f64.o main.o -o AdrpLdrGotLdr-externfargot-f64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-v128.o main.o -o AdrpLdrGotLdr-externfargot-v128.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdr-near: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g8.o -DFOO_AS_CONST -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g8.o main.o -o AdrpLdrGotLdr-near-g8.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g16.o -DFOO_AS_CONST -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g16.o main.o -o AdrpLdrGotLdr-near-g16.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g32.o -DFOO_AS_CONST -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g32.o main.o -o AdrpLdrGotLdr-near-g32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'ldr\tw2, _foo' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g64.o -DFOO_AS_CONST -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g64.o main.o -o AdrpLdrGotLdr-near-g64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'ldr\tx2, _foo' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-f32.o -DFOO_AS_CONST -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-f32.o main.o -o AdrpLdrGotLdr-near-f32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'ldr\ts2, _foo' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-f64.o -DFOO_AS_CONST -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-f64.o main.o -o AdrpLdrGotLdr-near-f64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'ldr\td2, _foo' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-v128.o -DFOO_AS_CONST -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-v128.o main.o -o AdrpLdrGotLdr-near-v128.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'ldr\tq2, _foo' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdr-nearunaligned: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g8.o main.o -o AdrpLdrGotLdr-nearunaligned-g8.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g16.o main.o -o AdrpLdrGotLdr-nearunaligned-g16.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g32.o main.o -o AdrpLdrGotLdr-nearunaligned-g32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g64.o main.o -o AdrpLdrGotLdr-nearunaligned-g64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-f32.o main.o -o AdrpLdrGotLdr-nearunaligned-f32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-f64.o main.o -o AdrpLdrGotLdr-nearunaligned-f64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-v128.o main.o -o AdrpLdrGotLdr-nearunaligned-v128.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdr-far: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g8.o main.o -o AdrpLdrGotLdr-far-g8.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'ldr\tb2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g16.o main.o -o AdrpLdrGotLdr-far-g16.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'ldr\th2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g32.o main.o -o AdrpLdrGotLdr-far-g32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'ldr\tw2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g64.o main.o -o AdrpLdrGotLdr-far-g64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'ldr\tx2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-f32.o main.o -o AdrpLdrGotLdr-far-f32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'ldr\ts2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-f64.o main.o -o AdrpLdrGotLdr-far-f64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'ldr\td2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-v128.o main.o -o AdrpLdrGotLdr-far-v128.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'ldr\tq2, \[x0,' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdr-farunaligned: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g8.o main.o -o AdrpLdrGotLdr-farunaligned-g8.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'ldr\tb2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g16.o main.o -o AdrpLdrGotLdr-farunaligned-g16.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g32.o main.o -o AdrpLdrGotLdr-farunaligned-g32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g64.o main.o -o AdrpLdrGotLdr-farunaligned-g64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-f32.o main.o -o AdrpLdrGotLdr-farunaligned-f32.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-f64.o main.o -o AdrpLdrGotLdr-farunaligned-f64.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-v128.o main.o -o AdrpLdrGotLdr-farunaligned-v128.exe
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpAddStr: AdrpAddStr-base AdrpAddStr-seg AdrpAddStr-align AdrpAddStr-addend AdrpAddStr-faraddend
+ true
+
+AdrpAddStr-base: main.o
+ # test ADRP/ADD/STR -> ADR/STR for 8-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g8.o -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpAddStr-base-g8.o main.o -o AdrpAddStr-base-g8.exe
+ ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR for 16-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g16.o -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpAddStr-base-g16.o main.o -o AdrpAddStr-base-g16.exe
+ ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'str\th1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR for 32-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g32.o -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpAddStr-base-g32.o main.o -o AdrpAddStr-base-g32.exe
+ ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'str\tw1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR for 64-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g64.o -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpAddStr-base-g64.o main.o -o AdrpAddStr-base-g64.exe
+ ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'str\tx1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR for 32-bit fp store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-f32.o -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpAddStr-base-f32.o main.o -o AdrpAddStr-base-f32.exe
+ ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'str\ts1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR for 64-bit fp store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-f64.o -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpAddStr-base-f64.o main.o -o AdrpAddStr-base-f64.exe
+ ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'str\td1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR for 128-bit vec store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-v128.o -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpAddStr-base-v128.o main.o -o AdrpAddStr-base-v128.exe
+ ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'str\tq1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+AdrpAddStr-seg: main.o
+ # test ADRP/ADD/STR -> ADRP/STR for 8-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g8.o -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-g8.o -o AdrpAddStr-seg-g8.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'str\tb1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/STR for 16-bit v
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g16.o -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-g16.o -o AdrpAddStr-seg-g16.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'str\th1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/STR for 32-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g32.o -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-g32.o -o AdrpAddStr-seg-g32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'str\tw1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/STR for 64-bit store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g64.o -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-g64.o -o AdrpAddStr-seg-g64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'str\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/STR for 32-bit fp store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-f32.o -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-f32.o -o AdrpAddStr-seg-f32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'str\ts1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/STR for 64-bit fp store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-f64.o -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-f64.o -o AdrpAddStr-seg-f64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'str\td1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/STR for 128-bit vec store
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-v128.o -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpAddStr-seg-v128.o -o AdrpAddStr-seg-v128.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'str\tq1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+AdrpAddStr-align: main.o
+ # test ADRP/ADD/STR -> ADR/STR when target is for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-g8.o main.o -o AdrpAddStr-align-g8.exe
+ ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-g16.o main.o -o AdrpAddStr-align-g16.exe
+ ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'str h1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-g32.o main.o -o AdrpAddStr-align-g32.exe
+ ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'str w1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-g64.o main.o -o AdrpAddStr-align-g64.exe
+ ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'str x1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-f32.o main.o -o AdrpAddStr-align-f32.exe
+ ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'str s1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-f64.o main.o -o AdrpAddStr-align-f64.exe
+ ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'str d1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpAddStr-align-v128.o main.o -o AdrpAddStr-align-v128.exe
+ ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'str q1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+
+AdrpAddStr-addend:
+ # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g8.o -DLOAD_GPR_8 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-g8.o main.o -o AdrpAddStr-addend-g8.exe
+ ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g16.o -DLOAD_GPR_16 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-g16.o main.o -o AdrpAddStr-addend-g16.exe
+ ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'str\th1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g32.o -DLOAD_GPR_32 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-g32.o main.o -o AdrpAddStr-addend-g32.exe
+ ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'str\tw1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g64.o -DLOAD_GPR_64 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-g64.o main.o -o AdrpAddStr-addend-g64.exe
+ ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'str\tx1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-f32.o -DLOAD_FPR_32 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-f32.o main.o -o AdrpAddStr-addend-f32.exe
+ ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'str\ts1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-f64.o -DLOAD_FPR_64 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-f64.o main.o -o AdrpAddStr-addend-f64.exe
+ ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'str\td1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-v128.o -DLOAD_VEC_128 -DADDEND=16
+ ${CC} ${CCFLAGS} AdrpAddStr-addend-v128.o main.o -o AdrpAddStr-addend-v128.exe
+ ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'str\tq1, \[x0\]' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN}
+
+
+AdrpAddStr-faraddend:
+ # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g8.o -DLOAD_GPR_8 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g8.o -o AdrpAddStr-faraddend-g8.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'str\tb1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 16-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g16.o -DLOAD_GPR_16 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g16.o -o AdrpAddStr-faraddend-g16.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'str\th1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g32.o -DLOAD_GPR_32 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g32.o -o AdrpAddStr-faraddend-g32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'str\tw1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g64.o -DLOAD_GPR_64 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g64.o -o AdrpAddStr-faraddend-g64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'str\tx1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-f32.o -DLOAD_FPR_32 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-f32.o -o AdrpAddStr-faraddend-f32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'str\ts1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-f64.o -DLOAD_FPR_64 -DADDEND=8
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-f64.o -o AdrpAddStr-faraddend-f64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'str\td1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/ADD/STR -> LDR when target is in __TEXT for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-v128.o -DLOAD_VEC_128 -DADDEND=16
+ ${CC} ${CCFLAGS} AdrpAddStr-faraddend-v128.o -o AdrpAddStr-faraddend-v128.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib
+ ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'add x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'str\tq1, \[x0' | ${FAIL_IF_EMPTY}
+
+
+
+AdrpLdrGotStr: AdrpLdrGotStr-extern AdrpLdrGotStr-externfargot AdrpLdrGotStr-near AdrpLdrGotStr-far AdrpLdrGotStr-nearunaligned AdrpLdrGotStr-farunaligned
+ true
+
+AdrpLdrGotStr-extern: main.o
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g8.o main.o -o AdrpLdrGotStr-extern-g8.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g16.o main.o -o AdrpLdrGotStr-extern-g16.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g32.o main.o -o AdrpLdrGotStr-extern-g32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g64.o main.o -o AdrpLdrGotStr-extern-g64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-f32.o main.o -o AdrpLdrGotStr-extern-f32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-f64.o main.o -o AdrpLdrGotStr-extern-f64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-v128.o main.o -o AdrpLdrGotStr-extern-v128.exe
+ ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotStr-externfargot: main.o
+ # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g8.o main.o -o AdrpLdrGotStr-externfargot-g8.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g16.o main.o -o AdrpLdrGotStr-externfargot-g16.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR left untouched when target is external and GOT slot farfor 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g32.o main.o -o AdrpLdrGotStr-externfargot-g32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g64.o main.o -o AdrpLdrGotStr-externfargot-g64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-f32.o main.o -o AdrpLdrGotStr-externfargot-f32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-f64.o main.o -o AdrpLdrGotStr-externfargot-f64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-v128.o main.o -o AdrpLdrGotStr-externfargot-v128.exe
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotStr-near: main.o
+ # test ADRP/LDR/STR -> ADR/STR when target close for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g8.o -DLOAD_GPR_8
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g8.o main.o -o AdrpLdrGotStr-near-g8.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -> ADR/STR when target close for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g16.o -DLOAD_GPR_16
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g16.o main.o -o AdrpLdrGotStr-near-g16.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -> ADR/STR when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g32.o -DLOAD_GPR_32
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g32.o main.o -o AdrpLdrGotStr-near-g32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -> ADR/STR when target close for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g64.o -DLOAD_GPR_64
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g64.o main.o -o AdrpLdrGotStr-near-g64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -> ADR/STR when target close for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-f32.o -DLOAD_FPR_32
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-f32.o main.o -o AdrpLdrGotStr-near-f32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -> ADR/STR when target close for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-f64.o -DLOAD_FPR_64
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-f64.o main.o -o AdrpLdrGotStr-near-f64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/STR -> ADR/STR when target close for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-v128.o -DLOAD_VEC_128
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-near-v128.o main.o -o AdrpLdrGotStr-near-v128.exe
+ ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotStr-nearunaligned: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g8.o main.o -o AdrpLdrGotStr-nearunaligned-g8.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g16.o main.o -o AdrpLdrGotStr-nearunaligned-g16.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g32.o main.o -o AdrpLdrGotStr-nearunaligned-g32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g64.o main.o -o AdrpLdrGotStr-nearunaligned-g64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-f32.o main.o -o AdrpLdrGotStr-nearunaligned-f32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-f64.o main.o -o AdrpLdrGotStr-nearunaligned-f64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-v128.o main.o -o AdrpLdrGotStr-nearunaligned-v128.exe
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotStr-far: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g8.o main.o -o AdrpLdrGotStr-far-g8.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'str\tb2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g16.o main.o -o AdrpLdrGotStr-far-g16.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'str\th2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g32.o main.o -o AdrpLdrGotStr-far-g32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'str\tw2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g64.o main.o -o AdrpLdrGotStr-far-g64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'str\tx2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-f32.o main.o -o AdrpLdrGotStr-far-f32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'str\ts2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-f64.o main.o -o AdrpLdrGotStr-far-f64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'str\td2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-far-v128.o main.o -o AdrpLdrGotStr-far-v128.exe
+ ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'str\tq2, \[x0,' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotStr-farunaligned: main.o
+ # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g8.o main.o -o AdrpLdrGotStr-farunaligned-g8.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'str\tb2, \[x0,' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g16.o main.o -o AdrpLdrGotStr-farunaligned-g16.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g32.o main.o -o AdrpLdrGotStr-farunaligned-g32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g64.o main.o -o AdrpLdrGotStr-farunaligned-g64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-f32.o main.o -o AdrpLdrGotStr-farunaligned-f32.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-f64.o main.o -o AdrpLdrGotStr-farunaligned-f64.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+ ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-v128.o main.o -o AdrpLdrGotStr-farunaligned-v128.exe
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGot: main.o
+ # test ADRP/LDR left untouched when target is extern and GOT is far
+ ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-externfar.o -DPADDING
+ ${CC} ${CCFLAGS} AdrpLdrGot-externfar.o main.o -o AdrpLdrGot-externfar.exe
+ ${OTOOL} -tV AdrpLdrGot-externfar.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGot-externfar.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> NOP/LDR literal when target is exern and GOT is near
+ ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-externnear.o
+ ${CC} ${CCFLAGS} AdrpLdrGot-externnear.o main.o -o AdrpLdrGot-externnear.exe
+ ${OTOOL} -tV AdrpLdrGot-externnear.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGot-externnear.exe | grep 'ldr\tx1' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> ADR when target is local, near code
+ ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localnear.o -DTARGET=_fooCode
+ ${CC} ${CCFLAGS} AdrpLdrGot-localnear.o main.o -o AdrpLdrGot-localnear.exe
+ ${OTOOL} -tV AdrpLdrGot-localnear.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGot-localnear.exe | grep 'adr\tx1' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> ADR when target is local, near data
+ ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localnear2.o -DTARGET=_fooData
+ ${CC} ${CCFLAGS} AdrpLdrGot-localnear2.o main.o -o AdrpLdrGot-localnear2.exe
+ ${OTOOL} -tV AdrpLdrGot-localnear2.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+ ${OTOOL} -tV AdrpLdrGot-localnear2.exe | grep 'adr\tx1' | ${FAIL_IF_EMPTY}
+
+ # test ADRP/LDR -> ADR when target is local, far code
+ ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localfar.o -DTARGET=_fooCode -DPADDING=1
+ ${CC} ${CCFLAGS} AdrpLdrGot-localfar.o main.o -o AdrpLdrGot-localfar.exe
+ ${OTOOL} -tV AdrpLdrGot-localfar.exe | grep 'adrp\tx1' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -tV AdrpLdrGot-localfar.exe | grep 'add\tx1' | ${FAIL_IF_EMPTY}
+
+
+
+clean:
+ rm -f *.o *.exe *.dylib
--- /dev/null
+
+ .text
+ .align 2
+ .globl _main
+_main: ret lr
+
run: ${ALL}
all:
- ${PASS_IFF_GOOD_MACHO} /usr/bin/true
+ ${PASS_IFF} /usr/bin/true
all-i386:
- ${CC} ${CCFLAGS} test.m -framework Foundation -o test1
+ ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 -Wno-objc-root-class
size -l test1 | grep __image_info | ${FAIL_IF_EMPTY}
- ${CC} ${CCFLAGS} test.m -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2
+ ${CC} ${CCFLAGS} test.m -Wno-objc-root-class -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2
size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} test2
-@interface Foo
+@interface Foo
@end
@implementation Foo
OPTIONS =
ifeq ($(ARCH),i386)
- OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch
+ OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/
endif
all: all-${ARCH}
OPTIONS =
ifeq ($(ARCH),i386)
- OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch
+ OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/
endif
ifeq ($(ARCH),arm)
OPTIONS =
ifeq ($(ARCH),i386)
- OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch
+ OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/
endif
all: all-${FILEARCH}
${FAIL_IF_BAD_OBJ} bar-gc-only.o
# check RR + RR -> RR
- ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
${FAIL_IF_BAD_MACHO} libfoobar.dylib
# check GC/RR + GC/RR -> GC/RR
- ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
${FAIL_IF_BAD_MACHO} libfoobar.dylib
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY}
# check GC + GC -> GC
- ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
${FAIL_IF_BAD_MACHO} libfoobar.dylib
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY}
# check RR + GC/RR -> RR
- ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
${FAIL_IF_BAD_MACHO} libfoobar.dylib
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN}
# check GC/RR + RR -> RR
- ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib
+ ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
${FAIL_IF_BAD_MACHO} libfoobar.dylib
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN}
# check GC + GC/RR -> GC
- ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
${FAIL_IF_BAD_MACHO} libfoobar.dylib
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY}
# check RR + GC -> error
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log
# check cmd line GC/RR, GC/RR + RR -> error
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc 2> fail.log
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc -framework Foundation 2> fail.log
# check GC/RR + compaction
- ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12 | ${FAIL_IF_EMPTY}
# check GC + compaction
- ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib
+ ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation
otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16 | ${FAIL_IF_EMPTY}
# none + GC/RR-dylib -> none
- ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib
- ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib
+ ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
+ ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
# none + GC-dylib -> none
- ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib
- ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib
+ ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
+ ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
# none + RR-dylib -> none
- ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib
- ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib
+ ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
+ ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
# check RR + GC-dylib -> error
- ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log
+ ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log
# check GC + RR-dylib -> error
- ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log
+ ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log
${PASS_IFF} true
+#include <Foundation/Foundation.h>
-@interface Bar {
+@interface Bar : NSObject {
int f;
}
- (void) doit;
+#include <Foundation/Foundation.h>
-@interface Foo {
+@interface Foo : NSObject {
int f;
}
- (void) doit;
#include <stdio.h>
#include <stdlib.h>
+#include <Foundation/Foundation.h>
-@interface Foo @end
+@interface Foo : NSObject
+@end
@implementation Foo
+(void)initialize { }
+(void)foo {
#include <Foundation/Foundation.h>
-@interface Test
+@interface Test : NSObject
{
BOOL one;
NSString* two;
};
void Foo::print() {
- printf("%d\n", a);
+ printf("%d %d\n", a, b);
}
run: all
all:
- ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o
+ ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o -Wno-array-bounds
${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump
${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o
// rdar://problem/4732996
const char* stringFutz(int x) {
- return "hello" + 0x1000 + x;
+ return &"hello"[0x1000 + x];
}
-const char* usesAddend = "teststr" + 0x2000;
+const char* usesAddend = &"teststr"[0x2000];
run: all
all:
- ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o
+ ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o -Wno-array-bounds
${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump
${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o
// rdar://problem/4732996
const char* stringFutz(int x) {
- return "hello" + 0x1000 + x;
+ return &"hello"[0x1000 + x];
}
-const char* usesAddend = "teststr" + 0x2000;
+const char* usesAddend = &"teststr"[0x2000];
-@interface Base
+@interface Base : NSObject
@end