From: Apple Date: Fri, 4 Apr 2014 22:42:29 +0000 (+0000) Subject: ld64-236.3.tar.gz X-Git-Tag: developer-tools-51^0 X-Git-Url: https://git.saurik.com/apple/ld64.git/commitdiff_plain/9543cb2f21e50a417dc8cf37eb7173f353536979 ld64-236.3.tar.gz --- diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index aef8eb9..964d099 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -297,6 +297,10 @@ them to use no space on disk. This option suppresses that optimization, so zero 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 @@ -380,6 +384,8 @@ Don't turn private external (aka visibility=hidden) symbols into static symbols, 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 diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 4ffd5d8..250fa5e 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -1058,6 +1058,7 @@ "$(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 = ( @@ -1066,7 +1067,7 @@ ); 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; @@ -1124,13 +1125,14 @@ "$(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; @@ -1173,7 +1175,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1208,7 +1210,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1326,13 +1328,14 @@ "$(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; @@ -1415,7 +1418,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index 60b4f10..9279734 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -394,6 +394,18 @@ }; #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 @@ -415,6 +427,9 @@ #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; @@ -428,7 +443,10 @@ struct ArchInfo { 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 }, diff --git a/src/abstraction/MachOTrie.hpp b/src/abstraction/MachOTrie.hpp index 1885e48..b7c55c1 100644 --- a/src/abstraction/MachOTrie.hpp +++ b/src/abstraction/MachOTrie.hpp @@ -329,7 +329,7 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* { 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; diff --git a/src/create_configure b/src/create_configure index aed4fa3..8ca92be 100755 --- a/src/create_configure +++ b/src/create_configure @@ -11,12 +11,12 @@ else 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 @@ -28,18 +28,5 @@ done echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h -# 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 - diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index 35daef1..acbdf4f 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -110,6 +110,7 @@ private: 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; + uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const; uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const; @@ -137,6 +138,7 @@ private: bool _hasDataInCodeLoadCommand; bool _hasSourceVersionLoadCommand; bool _hasDependentDRInfo; + bool _hasOptimizationHints; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; uint32_t _dyldEnvironExrasCount; @@ -176,6 +178,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _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: @@ -426,6 +429,9 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasDependentDRInfo ) sz += sizeof(macho_linkedit_data_command

); + + if ( _hasOptimizationHints ) + sz += sizeof(macho_linkedit_data_command

); return sz; } @@ -503,6 +509,9 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasDependentDRInfo ) ++count; + + if ( _hasOptimizationHints ) + ++count; return count; } @@ -561,9 +570,9 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const 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; @@ -578,7 +587,7 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const 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; @@ -610,10 +619,10 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const template <> uint32_t HeaderAndLoadCommandsAtom::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 <> @@ -1409,6 +1418,19 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) co } + +template +uint8_t* HeaderAndLoadCommandsAtom::copyOptimizationHintsLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)p; + cmd->set_cmd(LC_LINKER_OPTIMIZATION_HINTS); + cmd->set_cmdsize(sizeof(macho_linkedit_data_command

)); + cmd->set_dataoff(_writer.optimizationHintsSection->fileOffset); + cmd->set_datasize(_writer.optimizationHintsSection->size); + return p + sizeof(macho_linkedit_data_command

); +} + + template void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { @@ -1522,6 +1544,9 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasDependentDRInfo ) p = this->copyDependentDRLoadCommand(p); + if ( _hasOptimizationHints ) + p = this->copyOptimizationHintsLoadCommand(p); + } diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index a4b256f..f49f2e3 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -282,6 +282,8 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib 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 ) { @@ -291,7 +293,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } // 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); @@ -561,7 +563,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) } } catch (const char* msg) { - throwf("in '%s', %s", info.path, msg); + warning("Auto-Linking supplied '%s', %s", info.path, msg); } } } @@ -589,7 +591,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) } } catch (const char* msg) { - throwf("in '%s', %s", info.path, msg); + warning("Auto-Linking supplied '%s', %s", info.path, msg); } } } diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h index 3c55d45..b1e85cb 100644 --- a/src/ld/InputFiles.h +++ b/src/ld/InputFiles.h @@ -74,6 +74,9 @@ public: bool inferredArch() const { return _inferredArch; } + void addLinkerOptionLibraries(ld::Internal& state); + void createIndirectDylibs(); + // for -print_statistics volatile int64_t _totalObjectSize; volatile int64_t _totalArchiveSize; @@ -91,10 +94,8 @@ private: 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 diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index 1b41fb2..3420626 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -1555,6 +1555,68 @@ void DependentDRAtom::encode() const +template +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 +ld::Section OptimizationHintsAtom::_s_section("__LINKEDIT", "__opt_hints", ld::Section::typeLinkEdit, true); + +template +void OptimizationHintsAtom::encode() const +{ + if ( _state.someObjectHasOptimizationHints ) { + for (std::vector::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::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 diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index d916c53..057fad9 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -175,7 +175,8 @@ Options::Options(int argc, const char* argv[]) 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), @@ -631,7 +632,21 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co } } else { - bool lookForDylibs = ( fOutputKind != Options::kDyld); + bool lookForDylibs = false; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // + 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 @@ -1717,6 +1732,27 @@ void Options::addSection(const char* segment, const char* section, const char* p 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 ) @@ -2571,18 +2607,6 @@ void Options::parse(int argc, const char* argv[]) 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; } @@ -2923,9 +2947,36 @@ void Options::parse(int argc, const char* argv[]) 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

"; + 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); } @@ -3388,6 +3439,11 @@ void Options::reconfigureDefaults() fFunctionStartsLoadCommand = true; break; case Options::kObjectFile: + if ( !fDataInCodeInfoLoadCommandForcedOff ) + fDataInCodeInfoLoadCommand = true; + if ( fFunctionStartsForcedOn ) + fFunctionStartsLoadCommand = true; + break; case Options::kDynamicExecutable: case Options::kDyld: case Options::kDynamicLibrary: @@ -3624,7 +3680,11 @@ void Options::reconfigureDefaults() // -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) fDebugInfoStripping = Options::kDebugInfoNone; - + + // -r implies -no_uuid + if ( fOutputKind == Options::kObjectFile ) + fUUIDMode = kUUIDNone; + // choose how to process unwind info switch ( fArchitecture ) { case CPU_TYPE_I386: @@ -3709,9 +3769,17 @@ void Options::reconfigureDefaults() 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 ) @@ -3794,7 +3862,10 @@ void Options::reconfigureDefaults() 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: @@ -4466,6 +4537,10 @@ void Options::checkIllegalOptionCombinations() // -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"; } diff --git a/src/ld/Options.h b/src/ld/Options.h index d3e8d3f..0195815 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -172,6 +172,14 @@ public: 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, @@ -335,7 +343,10 @@ public: 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; @@ -362,6 +373,7 @@ public: linkerOptions() const { return fLinkerOptions; } FileInfo findFramework(const char* frameworkName) const; FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const; + const std::vector& sectionRenames() const { return fSectionRenames; } private: typedef std::unordered_map NameToOrder; @@ -424,6 +436,7 @@ private: 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); @@ -591,7 +604,10 @@ private: bool fKeepDwarfUnwind; bool fKeepDwarfUnwindForcedOn; bool fKeepDwarfUnwindForcedOff; + bool fVerboseOptimizationHints; + bool fIgnoreOptimizationHints; bool fGenerateDtraceDOF; + bool fAllowBranchIslands; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; @@ -613,6 +629,7 @@ private: std::vector fSDKPaths; std::vector fDyldEnvironExtras; std::vector< std::vector > fLinkerOptions; + std::vector fSectionRenames; bool fSaveTempFiles; mutable Snapshot fLinkSnapshot; bool fSnapshotRequested; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index 8b0be0a..c7a0918 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -69,16 +69,20 @@ 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), @@ -94,6 +98,7 @@ OutputFile::OutputFile(const Options& opts) _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), + _hasOptimizationHints(opts.outputKind() == Options::kObjectFile), _encryptedTEXTstartOffset(0), _encryptedTEXTendOffset(0), _localSymbolsStartIndex(0), @@ -115,7 +120,8 @@ OutputFile::OutputFile(const Options& opts) _splitSegInfoAtom(NULL), _functionStartsAtom(NULL), _dataInCodeAtom(NULL), - _dependentDRInfoAtom(NULL) + _dependentDRInfoAtom(NULL), + _optimizationHintsAtom(NULL) { } @@ -143,9 +149,9 @@ void OutputFile::write(ld::Internal& state) 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); @@ -192,7 +198,6 @@ void OutputFile::assignAtomAddresses(ld::Internal& state) if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName()); for (std::vector::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 @@ -207,6 +212,7 @@ void OutputFile::assignAtomAddresses(ld::Internal& state) break; default: (const_cast(atom))->setSectionStartAddress(sect->address); + if ( log ) fprintf(stderr, " atom=%p, addr=0x%08llX, name=%s\n", atom, atom->finalAddress(), atom->name()); break; } } @@ -255,6 +261,12 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _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); @@ -326,94 +338,6 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _fileSize = state.sections.back()->fileOffset + state.sections.back()->size; } -void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) -{ - for (std::vector::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::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - (const_cast(atom))->setSectionOffset(atom->objectAddress()); - } - } - else { - uint16_t maxAlignment = 0; - uint64_t offset = 0; - for (std::vector::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(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) { @@ -438,13 +362,14 @@ 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::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; @@ -495,192 +420,6 @@ uint64_t OutputFile::pageAlign(uint64_t addr, uint64_t pageSize) 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::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::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::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::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::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; - - // 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::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]; @@ -934,45 +673,61 @@ void OutputFile::rangeCheckARM12(int64_t displacement, ld::Internal& state, cons } } +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)); + } } @@ -1023,6 +778,517 @@ void OutputFile::set32BE(uint8_t* loc, uint32_t value) { BigEndian::set32(*( 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& usedByHints, + uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info) +{ + info->offsetInAtom = offsetInAtom + delta; + std::map::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()); @@ -1036,8 +1302,10 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: bool is_blx; bool is_b; bool thumbTarget = false; + std::map 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: @@ -1281,6 +1549,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } break; case ld::Fixup::kindLazyTarget: + case ld::Fixup::kindIslandTarget: break; case ld::Fixup::kindSetLazyOffset: assert(fit->binding == ld::Fixup::bindingDirectlyBound); @@ -1293,6 +1562,17 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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); @@ -1377,6 +1657,22 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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 ) @@ -1387,18 +1683,18 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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; } @@ -1421,6 +1717,23 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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 ) @@ -1572,11 +1885,14 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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 @@ -1594,6 +1910,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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: @@ -1664,6 +1981,21 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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; @@ -1677,6 +2009,450 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: #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::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) @@ -1779,6 +2555,12 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } } } + + 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); + } } @@ -2302,6 +3084,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2360,6 +3146,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2418,6 +3208,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2476,6 +3270,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2857,13 +3655,14 @@ uint64_t OutputFile::lookBackAddend(ld::Fixup::iterator fit) } - - - void OutputFile::generateLinkEditInfo(ld::Internal& state) { for (std::vector::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) ); @@ -3097,7 +3896,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s 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; diff --git a/src/ld/OutputFile.h b/src/ld/OutputFile.h index bfc8936..6bb793b 100644 --- a/src/ld/OutputFile.h +++ b/src/ld/OutputFile.h @@ -68,11 +68,9 @@ public: uint64_t fileSize() const { return _fileSize; } - bool hasWeakExternalSymbols; bool usesWeakExternalSymbols; bool overridesWeakExternalSymbols; bool _noReExportedDylibs; - bool hasThreadLocalVariableDefinitions; bool pieDisabled; bool hasDataInCode; ld::Internal::FinalSection* headerAndLoadCommandsSection; @@ -84,6 +82,7 @@ public: 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; @@ -150,8 +149,6 @@ private: 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, @@ -201,6 +198,10 @@ private: 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, @@ -231,6 +232,19 @@ private: 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& 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); @@ -261,6 +275,7 @@ private: bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; + bool _hasOptimizationHints; uint64_t _fileSize; std::map _lazyPointerAddressToInfoOffset; uint32_t _encryptedTEXTstartOffset; @@ -297,6 +312,7 @@ public: class LinkEditAtom* _functionStartsAtom; class LinkEditAtom* _dataInCodeAtom; class LinkEditAtom* _dependentDRInfoAtom; + class LinkEditAtom* _optimizationHintsAtom; }; } // namespace tool diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 25d0c66..573cad8 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -418,7 +418,16 @@ void Resolver::doFile(const ld::File& file) 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; } } @@ -466,7 +475,9 @@ void Resolver::doFile(const ld::File& file) 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); @@ -601,6 +612,8 @@ void Resolver::convertReferencesToIndirect(const ld::Atom& 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 ) ) { @@ -732,8 +745,14 @@ void Resolver::resolveUndefines() std::vector undefineNames; _symbolTable.undefines(undefineNames); for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - // make proxy - this->doAtom(*new UndefinedProxyAtom(*it)); + const char* undefName = *it; + // "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)); } } @@ -952,6 +971,7 @@ void Resolver::deadStripOptimize(bool force) if ( _haveLLVMObjs && !force ) { // 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()); @@ -1420,6 +1440,9 @@ void Resolver::linkTimeOptimize() if ( ! _haveLLVMObjs ) return; + // 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(); @@ -1443,12 +1466,12 @@ void Resolver::linkTimeOptimize() std::vector 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::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) this->doAtom(**it); - + // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); @@ -1463,6 +1486,10 @@ void Resolver::linkTimeOptimize() aliasAtom->setFinalAliasOf(); } + // 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::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; @@ -1500,6 +1527,7 @@ void Resolver::linkTimeOptimize() } else { // last chance to check for undefines + this->resolveUndefines(); this->checkUndefines(true); // check new code does not override some dylib @@ -1540,6 +1568,14 @@ void Resolver::tweakWeakness() } } +void Resolver::dumpAtoms() +{ + fprintf(stderr, "Resolver all atoms:\n"); + for (std::vector::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() { diff --git a/src/ld/Resolver.h b/src/ld/Resolver.h index bdb4e3e..4a3cd73 100644 --- a/src/ld/Resolver.h +++ b/src/ld/Resolver.h @@ -62,7 +62,8 @@ public: : _options(opts), _inputFiles(inputs), _internal(state), _symbolTable(opts, state.indirectBindingTable), _haveLLVMObjs(false), - _completedInitialObjectFiles(false) {} + _completedInitialObjectFiles(false), + _ltoCodeGenFinished(false) {} virtual void doAtom(const ld::Atom&); @@ -100,6 +101,7 @@ private: bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); void tweakWeakness(); void doLinkerOption(const std::vector& linkerOption, const char* fileName); + void dumpAtoms(); typedef std::unordered_set StringSet; @@ -127,6 +129,7 @@ private: SymbolTable _symbolTable; bool _haveLLVMObjs; bool _completedInitialObjectFiles; + bool _ltoCodeGenFinished; }; diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp index 309de8b..b271259 100644 --- a/src/ld/SymbolTable.cpp +++ b/src/ld/SymbolTable.cpp @@ -588,6 +588,19 @@ SymbolTable::IndirectBindingSlot SymbolTable::findSlotForName(const char* name) 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) diff --git a/src/ld/SymbolTable.h b/src/ld/SymbolTable.h index 5575f31..14c7a9e 100644 --- a/src/ld/SymbolTable.h +++ b/src/ld/SymbolTable.h @@ -120,6 +120,7 @@ public: unsigned int updateCount() { return _indirectBindingTable.size(); } void undefines(std::vector& undefines); void tentativeDefs(std::vector& undefines); + void removeDeadAtoms(); bool hasName(const char* name); bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; } byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index 7f7161d..844f614 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -100,9 +100,6 @@ struct PerformanceStatistics { }; - - - class InternalState : public ld::Internal { public: @@ -110,6 +107,8 @@ 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() {} @@ -121,7 +120,7 @@ private: 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); @@ -139,6 +138,9 @@ private: 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; @@ -243,10 +245,19 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& 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& renames = options.sectionRenames(); + for ( std::vector::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; } @@ -522,7 +533,7 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in } 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; @@ -569,6 +580,308 @@ void InternalState::sortSections() } + +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::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::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + (const_cast(atom))->setSectionOffset(atom->objectAddress()); + } + } + else { + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector::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(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::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::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::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::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::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; + + // 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::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; diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index 42d5aa6..95973aa 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -166,7 +166,8 @@ enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, 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 { // @@ -409,6 +410,7 @@ struct Fixup kindStoreARM64GOTLoadPage21, kindStoreARM64GOTLoadPageOff12, kindStoreARM64GOTLeaPage21, kindStoreARM64GOTLeaPageOff12, kindStoreARM64TLVPLoadPage21, kindStoreARM64TLVPLoadPageOff12, + kindStoreARM64TLVPLoadNowLeaPage21, kindStoreARM64TLVPLoadNowLeaPageOff12, kindStoreARM64PointerToGOT, kindStoreARM64PCRelToGOT, #endif // dtrace probes @@ -419,9 +421,13 @@ struct Fixup 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 @@ -451,6 +457,10 @@ struct Fixup kindStoreTargetAddressARM64GOTLoadPageOff12,// kindSetTargetAddress + kindStoreARM64GOTLoadPageOff12 kindStoreTargetAddressARM64GOTLeaPage21, // kindSetTargetAddress + kindStoreARM64GOTLeaPage21 kindStoreTargetAddressARM64GOTLeaPageOff12, // kindSetTargetAddress + kindStoreARM64GOTLeaPageOff12 + kindStoreTargetAddressARM64TLVPLoadPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadPage21 + kindStoreTargetAddressARM64TLVPLoadPageOff12,// kindSetTargetAddress + kindStoreARM64TLVPLoadPageOff12 + kindStoreTargetAddressARM64TLVPLoadNowLeaPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPage21 + kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPageOff12 #endif }; @@ -516,6 +526,23 @@ struct Fixup 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: @@ -544,6 +571,20 @@ struct Fixup 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 + }; // @@ -707,6 +748,7 @@ public: } return false; } + virtual void setFile(const File* f) { } virtual UnwindInfo::iterator beginUnwind() const { return NULL; } virtual UnwindInfo::iterator endUnwind() const { return NULL; } @@ -805,7 +847,8 @@ public: 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() {} @@ -816,7 +859,10 @@ public: objcDylibConstraint(ld::File::objcConstraintNone), cpuSubType(0), allObjectFilesScatterable(true), - someObjectFileHasDwarf(false), usingHugeSections(false) { } + someObjectFileHasDwarf(false), usingHugeSections(false), + hasThreadLocalVariableDefinitions(false), + hasWeakExternalSymbols(false), + someObjectHasOptimizationHints(false) { } std::vector sections; std::vector dylibs; @@ -835,6 +881,9 @@ public: bool allObjectFilesScatterable; bool someObjectFileHasDwarf; bool usingHugeSections; + bool hasThreadLocalVariableDefinitions; + bool hasWeakExternalSymbols; + bool someObjectHasOptimizationHints; }; diff --git a/src/ld/parsers/archive_file.cpp b/src/ld/parsers/archive_file.cpp index 49240b3..9004530 100644 --- a/src/ld/parsers/archive_file.cpp +++ b/src/ld/parsers/archive_file.cpp @@ -381,7 +381,7 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem // 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; diff --git a/src/ld/parsers/libunwind/DwarfInstructions.hpp b/src/ld/parsers/libunwind/DwarfInstructions.hpp index c67a237..a37c8a0 100644 --- a/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -95,7 +95,8 @@ public: 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* infos, uint32_t& infosCount, void* ref, WarnFunc warn); @@ -172,7 +173,8 @@ private: template const char* DwarfInstructions::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* infos, uint32_t& infosCount, void* ref, WarnFunc warn) { typename CFI_Parser::CIE_Info cieInfo; @@ -270,7 +272,7 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS ++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; diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 460dcf0..3fe21f6 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -47,7 +47,6 @@ #define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" - namespace lto { @@ -199,7 +198,8 @@ public: 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& allAtoms, ld::Internal& state, @@ -213,6 +213,9 @@ public: 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 CStringSet; typedef std::unordered_map CStringToAtom; @@ -276,7 +279,7 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) } 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); @@ -295,6 +298,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons 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 @@ -461,6 +466,20 @@ struct CommandLineOrderFileSorter }; +#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& allAtoms, ld::Internal& state, const OptimizeOptions& options, @@ -483,6 +502,10 @@ bool Parser::optimize( const std::vector& allAtoms, // 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 + // The order that files are merged must match command line order std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); ld::File::Ordinal lastOrdinal; @@ -529,10 +552,7 @@ bool Parser::optimize( const std::vector& allAtoms, 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; @@ -742,6 +762,8 @@ bool Parser::optimize( const std::vector& allAtoms, 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 ) { @@ -749,6 +771,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) 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 @@ -774,6 +798,11 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) else { // ld only knew about non-static atoms, so this one must be new _newAtoms.push_back(&machoAtom); + // 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(&machoAtom); + ma->setFile(lastProxiedFile); + } } // adjust fixups to go through proxy atoms @@ -844,11 +873,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // 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; } diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h index 4140a3a..d75aab3 100644 --- a/src/ld/parsers/lto_file.h +++ b/src/ld/parsers/lto_file.h @@ -39,7 +39,8 @@ extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_ty 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; @@ -55,6 +56,7 @@ struct OptimizeOptions { bool linkerDeadStripping; bool needsUnwindInfoSection; bool keepDwarfUnwind; + bool verboseOptimizationHints; cpu_type_t arch; const char* mcpu; const std::vector* llvmOptions; diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index b30e727..d37098b 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -204,6 +204,7 @@ private: void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, const macho_nlist

* symbolTable, const char* strings, const uint8_t* fileContent); + static uint32_t parseVersionNumber32(const char* versionString); static const char* objCInfoSegmentName(); static const char* objCInfoSectionName(); @@ -231,6 +232,7 @@ private: bool _explictReExportFound; bool _wrongOS; bool _installPathOverride; + bool _indirectDylibsProcessed; static bool _s_logHashtable; }; @@ -260,7 +262,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _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

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -514,6 +516,28 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +template +uint32_t File::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 void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, @@ -621,6 +645,13 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address else if ( strncmp(symAction, "install_name$", 13) == 0 ) { _dylibInstallPath = symName; _installPathOverride = true; + // 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 { @@ -793,6 +824,9 @@ bool File::isPublicLocation(const char* pth) template void File::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 ) { @@ -850,6 +884,8 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b chain.prev = NULL; chain.file = this; this->assertNoReExportCycles(&chain); + + _indirectDylibsProcessed = true; } template @@ -1018,7 +1054,8 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* { if ( Parser::validFile(fileContent, false) ) { *result = CPU_TYPE_X86_64; - *subResult = CPU_SUBTYPE_X86_64_ALL; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); return true; } if ( Parser::validFile(fileContent, false) ) { diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index c22af18..ad5720e 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -170,6 +170,7 @@ protected: Atom* findContentAtomByAddress(pint_t addr, class Atom* start, class Atom* end); uint32_t x86_64PcRelOffset(uint8_t r_type); + void addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]); static const char* makeSegmentName(const macho_section* s); static bool readable(const macho_section* s); static bool writable(const macho_section* s); @@ -676,7 +677,7 @@ class Atom : public ld::Atom { 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; } @@ -694,6 +695,7 @@ public: 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: @@ -719,7 +721,7 @@ public: 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&) const; typedef typename A::P P; typedef typename A::P::E E; @@ -753,7 +755,7 @@ public: 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: @@ -773,10 +775,27 @@ private: _fixupsCount : kFixupCountBits, _lineInfoCount : kLineInfoCountBits, _unwindInfoCount : kUnwindInfoCountBits; - + + static std::map _s_fileOverride; }; +template +std::map Atom::_s_fileOverride; + +template +void Atom::setFile(const ld::File* f) { + _s_fileOverride[this] = f; +} +template +const ld::File* Atom::file() const +{ + std::map::iterator pos = _s_fileOverride.find(this); + if ( pos != _s_fileOverride.end() ) + return pos->second; + + return §().file(); +} template void Atom::setFixupsRange(uint32_t startIndex, uint32_t count) @@ -843,7 +862,7 @@ void Atom::copyRawContent(uint8_t buffer[]) const } template <> -void Atom::verifyAlignment() const +void Atom::verifyAlignment(const macho_section

&) const { if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) { if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) ) @@ -851,8 +870,19 @@ void Atom::verifyAlignment() const } } +#if SUPPORT_ARCH_arm64 +template <> +void Atom::verifyAlignment(const macho_section

& 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 -void Atom::verifyAlignment() const +void Atom::verifyAlignment(const macho_section

&) const { } @@ -871,7 +901,8 @@ public: 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); } @@ -981,9 +1012,15 @@ public: 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

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* 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); @@ -1059,7 +1096,8 @@ private: 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(); @@ -1103,6 +1141,8 @@ private: bool _hasUUID; macho_data_in_code_entry

* _dataInCodeStart; macho_data_in_code_entry

* _dataInCodeEnd; + const uint8_t* _lohStart; + const uint8_t* _lohEnd; // filled in by parse() CFISection* _EHFrameSection; @@ -1118,6 +1158,8 @@ private: bool _hasDataInCodeLabels; bool _keepDwarfUnwind; bool _forceDwarfConversion; + bool _neverConvertDwarf; + bool _verboseOptimizationHints; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1128,7 +1170,8 @@ private: template Parser::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), @@ -1136,11 +1179,14 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _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) { } @@ -1843,6 +1889,15 @@ bool Parser::parseLoadCommands() throw "malformed LC_LINKER_OPTION"; } break; + case LC_LINKER_OPTIMIZATION_HINTS: + { + const macho_linkedit_data_command

* loh = (macho_linkedit_data_command

*)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

::CMD ) { if ( segment != NULL ) @@ -2648,6 +2703,12 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co 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; @@ -3552,7 +3613,7 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, 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; @@ -3845,7 +3906,10 @@ template ld::Atom::Alignment Section::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 @@ -3959,7 +4023,8 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::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); } @@ -3976,7 +4041,8 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::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); } @@ -4052,7 +4118,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::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); @@ -5190,7 +5256,11 @@ const uint8_t* CFStringSection::targetContent(const class Atom* atom, cons *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); @@ -5220,7 +5290,8 @@ unsigned long CFStringSection::contentHash(const class Atom* atom, const l } return hash; case contentUnknown: - return 0; + // For malformed CFStrings, hash to address of atom so they have unique hashes + return ULONG_MAX - (unsigned long)(atom); } return 0; } @@ -5249,6 +5320,12 @@ bool CFStringSection::canCoalesceWith(const class Atom* atom, const ld::At 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; @@ -6814,6 +6891,72 @@ bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const m return PointerToCStringSection::addRelocFixup(parser, reloc); } +#if SUPPORT_ARCH_arm64 +template <> +void Section::addLOH(class Parser& 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* 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::SourceLocation src(inAtom, lowestAddress- inAtom->objectAddress()); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindLinkerOptimizationHint, extra.addend); +} +#endif + +template +void Section::addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]) { + +} template void Section::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -6929,6 +7072,42 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } + // 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 ) { @@ -7006,7 +7185,8 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* { if ( mach_o::relocatable::Parser::validFile(fileContent) ) { *result = CPU_TYPE_X86_64; - *subResult = CPU_SUBTYPE_X86_64_ALL; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent) ) { diff --git a/src/ld/parsers/macho_relocatable_file.h b/src/ld/parsers/macho_relocatable_file.h index a576f52..6d20847 100644 --- a/src/ld/parsers/macho_relocatable_file.h +++ b/src/ld/parsers/macho_relocatable_file.h @@ -38,6 +38,8 @@ struct ParserOptions { bool warnUnwindConversionProblems; bool keepDwarfUnwind; bool forceDwarfConversion; + bool neverConvertDwarf; + bool verboseOptimizationHints; uint32_t subType; }; diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp index 96b6d35..5f5612c 100644 --- a/src/ld/passes/branch_island.cpp +++ b/src/ld/passes/branch_island.cpp @@ -41,6 +41,7 @@ namespace passes { namespace branch_island { +static std::map sAtomToAddress; struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; }; @@ -68,43 +69,27 @@ public: 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; }; @@ -154,62 +139,68 @@ public: 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) @@ -245,7 +236,8 @@ private: }; -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 ) { @@ -263,7 +255,10 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int 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); } @@ -343,47 +338,51 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra // 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::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::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) ) { + // haveCrossSectionBranches only applies to -preload builds + if ( preload && (atom->section() != target->section()) ) + haveCrossSectionBranches = true; } } // align atom @@ -400,9 +399,9 @@ void doPass(const Options& opts, ld::Internal& state) 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, @@ -410,12 +409,12 @@ void doPass(const Options& opts, ld::Internal& state) const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section std::vector 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::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."); @@ -427,28 +426,24 @@ void doPass(const Options& opts, ld::Internal& state) 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::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 AtomToIsland; AtomToIsland* regionsMap[kIslandRegionsCount]; + uint64_t regionAddresses[kIslandRegionsCount]; std::vector* regionsIslands[kIslandRegionsCount]; for(int i=0; i < kIslandRegionsCount; ++i) { regionsMap[i] = new AtomToIsland(); regionsIslands[i] = new std::vector(); + 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; @@ -493,25 +488,51 @@ void doPass(const Options& opts, ld::Internal& state) 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; @@ -521,7 +542,7 @@ void doPass(const Options& opts, ld::Internal& state) } } } - 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; } @@ -530,13 +551,14 @@ void doPass(const Options& opts, ld::Internal& state) 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; @@ -546,7 +568,7 @@ void doPass(const Options& opts, ld::Internal& state) } } } - 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; } @@ -561,24 +583,15 @@ void doPass(const Options& opts, ld::Internal& state) std::vector newAtomList; newAtomList.reserve(textSection->atoms.size()+islandCount); - uint64_t regionIndex = 0; + int regionIndex = 0; for (std::vector::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* islands = regionsIslands[regionIndex]; + newAtomList.insert(newAtomList.end(), islands->begin(), islands->end()); + ++regionIndex; } - - // insert the branch island atoms after the insertion point atom - std::vector* regionIslands = regionsIslands[regionIndex]; - for (std::vector::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(); @@ -588,6 +601,77 @@ void doPass(const Options& opts, ld::Internal& state) } +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::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::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::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 diff --git a/src/ld/passes/compact_unwind.cpp b/src/ld/passes/compact_unwind.cpp index 8b4a6bf..ad8a504 100644 --- a/src/ld/passes/compact_unwind.cpp +++ b/src/ld/passes/compact_unwind.cpp @@ -159,7 +159,7 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 // 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"); @@ -186,7 +186,11 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 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; diff --git a/src/ld/passes/order.cpp b/src/ld/passes/order.cpp index af73d66..684cb79 100644 --- a/src/ld/passes/order.cpp +++ b/src/ld/passes/order.cpp @@ -531,22 +531,32 @@ void Layout::buildOrdinalOverrideMap() warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() ); } - // When order file used on data, turn ordered zero fill symbols into zeroed data if ( ! moveToData.empty() ) { + // only move zero fill symbols to __data if there is a __data section + ld::Internal::FinalSection* dataSect = NULL; for (std::vector::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::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; + } } } } diff --git a/src/ld/passes/stubs/stub_arm64.hpp b/src/ld/passes/stubs/stub_arm64.hpp index cb1ceb3..63382e5 100644 --- a/src/ld/passes/stubs/stub_arm64.hpp +++ b/src/ld/passes/stubs/stub_arm64.hpp @@ -95,7 +95,9 @@ public: _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; } @@ -112,7 +114,7 @@ public: } 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) { @@ -130,6 +132,8 @@ private: ld::Fixup _fixup2; ld::Fixup _fixup3; ld::Fixup _fixup4; + ld::Fixup _fixup5; + ld::Fixup _fixup6; static ld::Section _s_section; }; @@ -287,8 +291,9 @@ public: _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(); } @@ -301,13 +306,14 @@ public: } 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; }; diff --git a/src/ld/passes/tlvp.cpp b/src/ld/passes/tlvp.cpp index 6a58fdf..e84fc25 100644 --- a/src/ld/passes/tlvp.cpp +++ b/src/ld/passes/tlvp.cpp @@ -142,6 +142,10 @@ void doPass(const Options& opts, ld::Internal& internal) 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: @@ -230,6 +234,14 @@ void doPass(const Options& opts, ld::Internal& internal) 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"); } diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index 36fcce9..c9eb46c 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -870,6 +870,9 @@ void dumper::dumpFixup(const ld::Fixup* ref) 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; @@ -888,6 +891,46 @@ void dumper::dumpFixup(const ld::Fixup* ref) 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; @@ -947,6 +990,12 @@ void dumper::dumpFixup(const ld::Fixup* ref) 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; @@ -1180,6 +1229,7 @@ static ld::relocatable::File* createReader(const char* path) objOpts.warnUnwindConversionProblems = true; objOpts.keepDwarfUnwind = false; objOpts.forceDwarfConversion = false; + objOpts.verboseOptimizationHints = true; objOpts.subType = sPreferredSubArch; #if 1 if ( ! foundFatSlice ) { @@ -1196,7 +1246,7 @@ static ld::relocatable::File* createReader(const char* path) 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; diff --git a/src/other/PruneTrie.cpp b/src/other/PruneTrie.cpp index e47d482..766ae94 100644 --- a/src/other/PruneTrie.cpp +++ b/src/other/PruneTrie.cpp @@ -98,16 +98,3 @@ prune_trie( // success return NULL; } - - -// 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"; -} - - diff --git a/src/other/dyldinfo.cpp b/src/other/dyldinfo.cpp index b43ff3f..6ac3311 100644 --- a/src/other/dyldinfo.cpp +++ b/src/other/dyldinfo.cpp @@ -1339,7 +1339,7 @@ void DyldInfoPrinter::processExportGraphNode(const uint8_t* const start, cons 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); diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index fdb6212..3f4647d 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -13,7 +13,7 @@ MYDIR=$(shell cd ../../bin;pwd) 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 @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 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) @@ -127,12 +127,16 @@ endif 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 diff --git a/unit-tests/test-cases/branch-islands/Makefile b/unit-tests/test-cases/branch-islands/Makefile index 8e1870b..9d30491 100644 --- a/unit-tests/test-cases/branch-islands/Makefile +++ b/unit-tests/test-cases/branch-islands/Makefile @@ -33,9 +33,9 @@ run: all 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 diff --git a/unit-tests/test-cases/branch-islands/extra.c b/unit-tests/test-cases/branch-islands/extra.c index a1991fe..3f43392 100644 --- a/unit-tests/test-cases/branch-islands/extra.c +++ b/unit-tests/test-cases/branch-islands/extra.c @@ -1,8 +1,10 @@ #include +extern void back(); void foo() { fprintf(stdout, "foo\n"); + back(); } diff --git a/unit-tests/test-cases/branch-islands/hello.c b/unit-tests/test-cases/branch-islands/hello.c index 3037663..0738f0e 100644 --- a/unit-tests/test-cases/branch-islands/hello.c +++ b/unit-tests/test-cases/branch-islands/hello.c @@ -5,7 +5,10 @@ extern void foo(); int main() { fprintf(stdout, "hello\n"); - foo(); + foo(); return 0; } +void back() +{ +} \ No newline at end of file diff --git a/unit-tests/test-cases/branch-islands/space.s b/unit-tests/test-cases/branch-islands/space.s index 0218bea..a15a2c2 100644 --- a/unit-tests/test-cases/branch-islands/space.s +++ b/unit-tests/test-cases/branch-islands/space.s @@ -35,11 +35,11 @@ _prejunk: #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__ diff --git a/unit-tests/test-cases/branch-long/Makefile b/unit-tests/test-cases/branch-long/Makefile new file mode 100644 index 0000000..9b41703 --- /dev/null +++ b/unit-tests/test-cases/branch-long/Makefile @@ -0,0 +1,59 @@ +## +# 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* diff --git a/unit-tests/test-cases/branch-long/bar.s b/unit-tests/test-cases/branch-long/bar.s new file mode 100644 index 0000000..528512a --- /dev/null +++ b/unit-tests/test-cases/branch-long/bar.s @@ -0,0 +1,24 @@ + +#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 diff --git a/unit-tests/test-cases/branch-long/foo.c b/unit-tests/test-cases/branch-long/foo.c new file mode 100644 index 0000000..60e96d2 --- /dev/null +++ b/unit-tests/test-cases/branch-long/foo.c @@ -0,0 +1,25 @@ + +int x = 1; +int y = 2; + +__attribute__((weak)) +void myweak1() +{ +} + +int foo() +{ + myweak1(); + return 1; +} + +int foo1() +{ + return x; +} + +int foo2() +{ + return y; +} + diff --git a/unit-tests/test-cases/cpu-sub-types/Makefile b/unit-tests/test-cases/cpu-sub-types/Makefile index f773c11..78a2484 100644 --- a/unit-tests/test-cases/cpu-sub-types/Makefile +++ b/unit-tests/test-cases/cpu-sub-types/Makefile @@ -27,6 +27,8 @@ include ${TESTROOT}/include/common.makefile # Validate cpu subtypes processing # +CC_ARM = $(shell xcrun -find clang) -miphoneos-version-min=5.0 -isysroot ${IOS_SDK} + test: test-${ARCH} test-ppc64: @@ -42,31 +44,31 @@ test-armv6: test-arm 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 @@ -84,9 +86,9 @@ test-arm: #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 diff --git a/unit-tests/test-cases/dso_handle/Makefile b/unit-tests/test-cases/dso_handle/Makefile index f6c40e4..d91d17f 100644 --- a/unit-tests/test-cases/dso_handle/Makefile +++ b/unit-tests/test-cases/dso_handle/Makefile @@ -15,7 +15,7 @@ all: ${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 diff --git a/unit-tests/test-cases/eh-coalescing-r/Makefile b/unit-tests/test-cases/eh-coalescing-r/Makefile index 3fb8a25..e40cc53 100644 --- a/unit-tests/test-cases/eh-coalescing-r/Makefile +++ b/unit-tests/test-cases/eh-coalescing-r/Makefile @@ -31,13 +31,12 @@ SHELL = bash # use bash shell so we can redirect just stderr # # 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} diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s b/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s new file mode 100644 index 0000000..b99221b --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s @@ -0,0 +1,27 @@ + + + .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 + diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s b/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s new file mode 100644 index 0000000..cdcaba9 --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s @@ -0,0 +1,55 @@ + +#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 + + diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s b/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s new file mode 100644 index 0000000..3e3252a --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s @@ -0,0 +1,44 @@ + +#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 + + diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s b/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s new file mode 100644 index 0000000..c91fa06 --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s @@ -0,0 +1,64 @@ + +#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 + + diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s new file mode 100644 index 0000000..40bc180 --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s @@ -0,0 +1,30 @@ + +#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 + + diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s new file mode 100644 index 0000000..0f90ff8 --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s @@ -0,0 +1,56 @@ + +#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 + + diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s new file mode 100644 index 0000000..d3fed03 --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s @@ -0,0 +1,49 @@ + +#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 + + diff --git a/unit-tests/test-cases/linker-optimization-hints/Makefile b/unit-tests/test-cases/linker-optimization-hints/Makefile new file mode 100644 index 0000000..de5da0d --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/Makefile @@ -0,0 +1,1418 @@ +## +# 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 diff --git a/unit-tests/test-cases/linker-optimization-hints/main.s b/unit-tests/test-cases/linker-optimization-hints/main.s new file mode 100644 index 0000000..762b4e4 --- /dev/null +++ b/unit-tests/test-cases/linker-optimization-hints/main.s @@ -0,0 +1,6 @@ + + .text + .align 2 + .globl _main +_main: ret lr + diff --git a/unit-tests/test-cases/objc-abi/Makefile b/unit-tests/test-cases/objc-abi/Makefile index d7eb660..d563cd9 100644 --- a/unit-tests/test-cases/objc-abi/Makefile +++ b/unit-tests/test-cases/objc-abi/Makefile @@ -35,12 +35,12 @@ endif 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 diff --git a/unit-tests/test-cases/objc-abi/test.m b/unit-tests/test-cases/objc-abi/test.m index 8b4a295..292c456 100644 --- a/unit-tests/test-cases/objc-abi/test.m +++ b/unit-tests/test-cases/objc-abi/test.m @@ -1,5 +1,5 @@ -@interface Foo +@interface Foo @end @implementation Foo diff --git a/unit-tests/test-cases/objc-category-optimize-load/Makefile b/unit-tests/test-cases/objc-category-optimize-load/Makefile index 2bbc5dd..7dcafac 100644 --- a/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile 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} diff --git a/unit-tests/test-cases/objc-category-optimize/Makefile b/unit-tests/test-cases/objc-category-optimize/Makefile index c395988..de4ea0c 100644 --- a/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/unit-tests/test-cases/objc-category-optimize/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile 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) diff --git a/unit-tests/test-cases/objc-category-warning/Makefile b/unit-tests/test-cases/objc-category-warning/Makefile index 5481ba0..31d841b 100644 --- a/unit-tests/test-cases/objc-category-warning/Makefile +++ b/unit-tests/test-cases/objc-category-warning/Makefile @@ -30,7 +30,7 @@ SHELL = bash # use bash shell so we can redirect just stderr 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} diff --git a/unit-tests/test-cases/objc-gc-checks/Makefile b/unit-tests/test-cases/objc-gc-checks/Makefile index 67e04a3..13d1743 100644 --- a/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/unit-tests/test-cases/objc-gc-checks/Makefile @@ -63,70 +63,70 @@ test-macosx: ${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 diff --git a/unit-tests/test-cases/objc-gc-checks/bar.m b/unit-tests/test-cases/objc-gc-checks/bar.m index 5c98709..f66df50 100644 --- a/unit-tests/test-cases/objc-gc-checks/bar.m +++ b/unit-tests/test-cases/objc-gc-checks/bar.m @@ -1,5 +1,6 @@ +#include -@interface Bar { +@interface Bar : NSObject { int f; } - (void) doit; diff --git a/unit-tests/test-cases/objc-gc-checks/foo.m b/unit-tests/test-cases/objc-gc-checks/foo.m index e13367e..819049d 100644 --- a/unit-tests/test-cases/objc-gc-checks/foo.m +++ b/unit-tests/test-cases/objc-gc-checks/foo.m @@ -1,5 +1,6 @@ +#include -@interface Foo { +@interface Foo : NSObject { int f; } - (void) doit; diff --git a/unit-tests/test-cases/objc-literal-pointers-strip/test.m b/unit-tests/test-cases/objc-literal-pointers-strip/test.m index 4837911..3ead0e6 100644 --- a/unit-tests/test-cases/objc-literal-pointers-strip/test.m +++ b/unit-tests/test-cases/objc-literal-pointers-strip/test.m @@ -25,8 +25,10 @@ #include #include +#include -@interface Foo @end +@interface Foo : NSObject +@end @implementation Foo +(void)initialize { } +(void)foo { diff --git a/unit-tests/test-cases/objc-properties/test.m b/unit-tests/test-cases/objc-properties/test.m index 25ca8c8..4a568ae 100644 --- a/unit-tests/test-cases/objc-properties/test.m +++ b/unit-tests/test-cases/objc-properties/test.m @@ -24,7 +24,7 @@ #include -@interface Test +@interface Test : NSObject { BOOL one; NSString* two; diff --git a/unit-tests/test-cases/operator-new/main.cxx b/unit-tests/test-cases/operator-new/main.cxx index 7d1ef81..862e46e 100644 --- a/unit-tests/test-cases/operator-new/main.cxx +++ b/unit-tests/test-cases/operator-new/main.cxx @@ -48,7 +48,7 @@ public: }; void Foo::print() { - printf("%d\n", a); + printf("%d %d\n", a, b); } diff --git a/unit-tests/test-cases/relocs-literals/Makefile b/unit-tests/test-cases/relocs-literals/Makefile index a9ca5ef..d62de34 100644 --- a/unit-tests/test-cases/relocs-literals/Makefile +++ b/unit-tests/test-cases/relocs-literals/Makefile @@ -35,7 +35,7 @@ include ${TESTROOT}/include/common.makefile 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 diff --git a/unit-tests/test-cases/relocs-literals/test.c b/unit-tests/test-cases/relocs-literals/test.c index 2d199d0..5939ef4 100644 --- a/unit-tests/test-cases/relocs-literals/test.c +++ b/unit-tests/test-cases/relocs-literals/test.c @@ -48,7 +48,7 @@ long double getLongDouble() { return 3.0; } // 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]; diff --git a/unit-tests/test-cases/relocs-literals2/Makefile b/unit-tests/test-cases/relocs-literals2/Makefile index 23e4a82..b1820f0 100644 --- a/unit-tests/test-cases/relocs-literals2/Makefile +++ b/unit-tests/test-cases/relocs-literals2/Makefile @@ -38,7 +38,7 @@ endif 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 diff --git a/unit-tests/test-cases/relocs-literals2/test.c b/unit-tests/test-cases/relocs-literals2/test.c index 2d199d0..5939ef4 100644 --- a/unit-tests/test-cases/relocs-literals2/test.c +++ b/unit-tests/test-cases/relocs-literals2/test.c @@ -48,7 +48,7 @@ long double getLongDouble() { return 3.0; } // 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]; diff --git a/unit-tests/test-cases/relocs-objc/test.m b/unit-tests/test-cases/relocs-objc/test.m index 1ca2157..2b2bf9c 100644 --- a/unit-tests/test-cases/relocs-objc/test.m +++ b/unit-tests/test-cases/relocs-objc/test.m @@ -48,7 +48,7 @@ -@interface Base +@interface Base : NSObject @end