#include <sys/types.h>
#include <sys/stat.h>
#include <mach/vm_prot.h>
#include <sys/sysctl.h>
#include <mach-o/dyld.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <spawn.h>
#include <cxxabi.h>
#include <Availability.h>
#include <tapi/tapi.h>
#include <vector>
#include <map>
#include <sstream>
#include "ld.hpp"
#include "Options.h"
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
#include "Snapshot.h"
#include "macho_relocatable_file.h"
extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length);
#define VAL(x) #x
#define STRINGIFY(x) VAL(x)
namespace lto {
extern const char* version();
extern unsigned static_api_version();
extern unsigned runtime_api_version();
}
const int crashreporterBufferSize = 2000;
static char crashreporterBuffer[crashreporterBufferSize];
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
#include <CrashReporterClient.h>
struct crashreporter_annotations_t gCRAnnotations
__attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION)))
= { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 };
#else
extern "C" char* __crashreporter_info__;
__attribute__((used))
char* __crashreporter_info__ = crashreporterBuffer;
#endif
static bool sEmitWarnings = true;
static bool sFatalWarnings = false;
static const char* sWarningsSideFilePath = NULL;
static FILE* sWarningsSideFile = NULL;
static int sWarningsCount = 0;
void warning(const char* format, ...)
{
++sWarningsCount;
if ( sEmitWarnings ) {
va_list list;
if ( sWarningsSideFilePath != NULL ) {
if ( sWarningsSideFile == NULL )
sWarningsSideFile = fopen(sWarningsSideFilePath, "a");
}
va_start(list, format);
fprintf(stderr, "ld: warning: ");
vfprintf(stderr, format, list);
fprintf(stderr, "\n");
if ( sWarningsSideFile != NULL ) {
fprintf(sWarningsSideFile, "ld: warning: ");
vfprintf(sWarningsSideFile, format, list);
fprintf(sWarningsSideFile, "\n");
fflush(sWarningsSideFile);
}
va_end(list);
}
}
void throwf(const char* format, ...)
{
va_list list;
char* p;
va_start(list, format);
vasprintf(&p, format, list);
va_end(list);
const char* t = p;
throw t;
}
bool Options::FileInfo::checkFileExists(const Options& options, const char *p)
{
if (isInlined) {
modTime = 0;
return true;
}
struct stat statBuffer;
if (p == NULL)
p = path;
if ( stat(p, &statBuffer) == 0 ) {
if (p != path) path = strdup(p);
modTime = statBuffer.st_mtime;
return true;
}
options.addDependency(Options::depNotFound, p);
return false;
}
Options::Options(int argc, const char* argv[])
: fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0),
fFallbackArchitecture(0), fFallbackSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable),
fHasPreferredSubType(false), fArchSupportsThumb2(false), fBindAtLoad(false), fKeepPrivateExterns(false),
fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false),
fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace),
fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL),
fBaseAddress(0), fMaxAddress(0xFFFFFFFFFFFFFFFFULL),
fBaseWritableAddress(0),
fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir),
fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(true),
fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak),
fClientName(NULL),
fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
fBundleLoader(NULL), fDtraceScriptName(NULL), fMapPath(NULL),
fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL),
fKextObjectsEnable(-1),fKextObjectsDirPath(NULL),fToolchainPath(NULL),fOrderFilePath(NULL),
fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false),
fNonExecutableHeap(false), fDisableNonExecutableHeap(false),
fMinimumHeaderPad(32), fSegmentAlignment(LD_PAGE_SIZE),
fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false),
fVerbose(false), fKeepRelocations(false), fWarnStabs(false),
fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false),
fSharedRegionEligible(false), fSharedRegionEligibleForceOff(false), fPrintOrderFileStatistics(false),
fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false),
fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false),
fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false),
fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false),
fOrderData(true), fMarkDeadStrippableDylib(false),
fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false),
fMakeThreadedStartsSection(false), fNoEHLabels(false),
fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false),
fWarnOnSwiftABIVersionMismatches(false), fUseSimplifiedDylibReExports(false),
fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false),
fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false),
fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false),
fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false),
fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true),
fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false),
fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false),
fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false),
fOutputSlidable(false), fWarnWeakExports(false), fNoWeakExports(false),
fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false),
fDemangle(false), fTLVSupport(false),
fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false),
fVersionLoadCommandForcedOff(false), fForceLegacyVersionLoadCommands(false), fFunctionStartsLoadCommand(false),
fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false),
fDataInCodeInfoLoadCommand(false), fDataInCodeInfoLoadCommandForcedOn(false), fDataInCodeInfoLoadCommandForcedOff(false),
fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false),
fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false),
fSourceVersionLoadCommand(false),
fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false),
fExportDynamic(false), fAbsoluteSymbols(false),
fAllowSimulatorToLinkWithMacOSX(false), fSimulatorSupportDylib(false), fKeepDwarfUnwind(true),
fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false),
fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false),
fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false),
fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false),
fSharedRegionEncodingV2(false), fUseDataConstSegment(false),
fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false),
fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false),
fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), fMakeInitializersIntoOffsets(false),
fUseLinkedListBinding(false), fMakeChainedFixups(false), fMakeChainedFixupsSection(false), fNoLazyBinding(false), fDebugVariant(false),
fReverseMapPath(NULL), fLTOCodegenOnly(false),
fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fInitializersTreatment(Options::kInvalid),
fZeroModTimeInDebugMap(false), fBitcodeKind(kBitcodeProcess),
fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fPlatfromVersionCmdFound(false), fInternalSDK(false),
fSaveTempFiles(false), fLinkSnapshot(this), fSnapshotRequested(false), fPipelineFifo(NULL),
fDependencyInfoPath(NULL), fBuildContextName(NULL), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0),
fUnalignedPointerTreatment(kUnalignedPointerIgnore), fPreferTAPIFile(false), fOSOPrefixPath(NULL)
{
this->checkForClassic(argc, argv);
this->parsePreCommandLineEnvironmentSettings();
this->parse(argc, argv);
this->parsePostCommandLineEnvironmentSettings();
this->reconfigureDefaults();
this->checkIllegalOptionCombinations();
this->addDependency(depOutputFile, fOutputFile);
if ( fMapPath != NULL )
this->addDependency(depOutputFile, fMapPath);
}
Options::~Options()
{
if ( fTraceFileDescriptor != -1 )
::close(fTraceFileDescriptor);
}
bool Options::errorBecauseOfWarnings() const
{
return (sFatalWarnings && (sWarningsCount > 0));
}
const char* Options::installPath() const
{
if ( fDylibInstallName != NULL )
return fDylibInstallName;
else if ( fFinalName != NULL )
return fFinalName;
else
return fOutputFile;
}
bool Options::interposable(const char* name) const
{
switch ( fInterposeMode ) {
case kInterposeNone:
return false;
case kInterposeAllExternal:
return true;
case kInterposeSome:
return fInterposeList.contains(name);
}
throw "internal error";
}
bool Options::printWhyLive(const char* symbolName) const
{
return fWhyLive.contains(symbolName);
}
const char* Options::dotOutputFile()
{
return fDotOutputFile;
}
bool Options::hasWildCardExportRestrictList() const
{
return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards());
}
bool Options::hasWeakBitTweaks() const
{
return (!fForceWeakSymbols.empty() || !fForceNotWeakSymbols.empty());
}
bool Options::allGlobalsAreDeadStripRoots() const
{
if ( fExportMode == kExportSome )
return false;
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
return fExportDynamic;
case Options::kStaticExecutable:
return fExportDynamic;
case Options::kPreload:
return false;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kKextBundle:
return true;
}
return false;
}
bool Options::dyldLoadsOutput() const
{
switch ( fOutputKind ) {
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kObjectFile:
case Options::kKextBundle:
return false;
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
return true;
}
}
bool Options::keepRelocations()
{
return fKeepRelocations;
}
bool Options::warnStabs()
{
return fWarnStabs;
}
const char* Options::executablePath()
{
return fExecutablePath;
}
uint32_t Options::initialSegProtection(const char* segName) const
{
for(std::vector<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) {
if ( strcmp(it->name, segName) == 0 ) {
return it->init;
}
}
if ( strcmp(segName, "__TEXT") == 0 ) {
return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) );
}
else if ( strcmp(segName, "__TEXT_EXEC") == 0 ) {
return VM_PROT_READ | VM_PROT_EXECUTE;
}
else if ( strcmp(segName, "__PAGEZERO") == 0 ) {
return 0;
}
else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
return VM_PROT_READ;
}
return VM_PROT_READ | VM_PROT_WRITE;
}
bool Options::readOnlyDataSegment(const char* name) const
{
return ( fUseDataConstSegment && (strcmp(name, "__DATA_CONST") == 0) && (!fSharedRegionEligible || (fOutputKind == Options::kDyld)) );
}
uint32_t Options::maxSegProtection(const char* segName) const
{
if ( fArchitecture != CPU_TYPE_I386 )
return initialSegProtection(segName);
for(std::vector<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) {
if ( strcmp(it->name, segName) == 0 ) {
return it->max;
}
}
if ( strcmp(segName, "__PAGEZERO") == 0 ) {
return 0;
}
return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
}
uint64_t Options::segPageSize(const char* segName) const
{
for(std::vector<SegmentSize>::const_iterator it=fCustomSegmentSizes.begin(); it != fCustomSegmentSizes.end(); ++it) {
if ( strcmp(it->name, segName) == 0 )
return it->size;
}
return fSegmentAlignment;
}
uint64_t Options::customSegmentAddress(const char* segName) const
{
for(std::vector<SegmentStart>::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
if ( strcmp(it->name, segName) == 0 )
return it->address;
}
if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) )
return fStackAddr - fStackSize;
return 0;
}
bool Options::hasCustomSegmentAddress(const char* segName) const
{
for(std::vector<SegmentStart>::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
if ( strcmp(it->name, segName) == 0 )
return true;
}
if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) )
return true;
return false;
}
bool Options::hasCustomSectionAlignment(const char* segName, const char* sectName) const
{
for (std::vector<SectionAlignment>::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) {
if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) )
return true;
}
if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) )
return true;
return false;
}
uint8_t Options::customSectionAlignment(const char* segName, const char* sectName) const
{
for (std::vector<SectionAlignment>::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) {
if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) )
return it->alignment;
}
if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) )
return __builtin_ctz(fSegmentAlignment);
return 0;
}
bool Options::segmentOrderAfterFixedAddressSegment(const char* segName) const
{
bool nowPinned = false;
for (std::vector<const char*>::const_iterator it=fSegmentOrder.begin(); it != fSegmentOrder.end(); ++it) {
if ( strcmp(*it, segName) == 0 )
return nowPinned;
if ( hasCustomSegmentAddress(*it) )
nowPinned = true;
}
return false;
}
bool Options::hasExportedSymbolOrder()
{
return (fExportSymbolsOrder.size() > 0);
}
bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) const
{
NameToOrder::const_iterator pos = fExportSymbolsOrder.find(sym);
if ( pos != fExportSymbolsOrder.end() ) {
*order = pos->second;
return true;
}
else {
*order = 0xFFFFFFFF;
return false;
}
}
void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping)
{
int fd = ::open(fileOfExports, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open -exported_symbols_order file: %s", fileOfExports);
struct stat stat_buf;
::fstat(fd, &stat_buf);
char* p = (char*)malloc(stat_buf.st_size);
if ( p == NULL )
throwf("can't process -exported_symbols_order file: %s", fileOfExports);
if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
throwf("can't read -exported_symbols_order file: %s", fileOfExports);
::close(fd);
unsigned int count = 0;
char * const end = &p[stat_buf.st_size];
enum { lineStart, inSymbol, inComment } state = lineStart;
char* symbolStart = NULL;
for (char* s = p; s < end; ++s ) {
switch ( state ) {
case lineStart:
if ( *s =='#' ) {
state = inComment;
}
else if ( !isspace(*s) ) {
state = inSymbol;
symbolStart = s;
}
break;
case inSymbol:
if ( (*s == '\n') || (*s == '\r') ) {
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
orderMapping[symbolStart] = ++count;
symbolStart = NULL;
state = lineStart;
}
break;
case inComment:
if ( (*s == '\n') || (*s == '\r') )
state = lineStart;
break;
}
}
if ( state == inSymbol ) {
warning("missing line-end at end of file \"%s\"", fileOfExports);
int len = end-symbolStart+1;
char* temp = new char[len];
strlcpy(temp, symbolStart, len);
char* last = &temp[len-2];
while ( isspace(*last) ) {
*last = '\0';
--last;
}
orderMapping[temp] = ++count;
}
}
void Options::loadImplictZipperFile(const char *path, std::vector<const char*>& paths)
{
int fd = ::open(path, O_RDONLY, 0);
if ( fd == -1 ) return; struct stat stat_buf;
::fstat(fd, &stat_buf);
char* p = (char*)malloc(stat_buf.st_size);
if ( p == NULL )
throwf("can't process auto zipper file: %s", path);
if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
throwf("can't process auto zipper file: %s", path);
::close(fd);
char * const end = &p[stat_buf.st_size];
enum { lineStart, inInstallname, inComment } state = lineStart;
char* installnameStart = NULL;
for (char* s = p; s < end; ++s ) {
switch ( state ) {
case lineStart:
if ( *s =='#' ) {
state = inComment;
}
else if ( !isspace(*s) ) {
state = inInstallname;
installnameStart = s;
}
break;
case inInstallname:
if ( (*s == '\n') || (*s == '\r') ) {
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
paths.push_back(installnameStart);
state = lineStart;
}
break;
case inComment:
if ( (*s == '\n') || (*s == '\r') )
state = lineStart;
break;
}
}
if ( state == inInstallname ) {
warning("missing line-end at end of file \"%s\"", path);
int len = end-installnameStart+1;
char* temp = new char[len];
strlcpy(temp, installnameStart, len);
char* last = &temp[len-2];
while ( isspace(*last) ) {
*last = '\0';
--last;
}
paths.push_back(installnameStart);
}
}
bool Options::forceWeak(const char* symbolName) const
{
return fForceWeakSymbols.contains(symbolName);
}
bool Options::forceNotWeak(const char* symbolName) const
{
return fForceNotWeakSymbols.contains(symbolName);
}
bool Options::forceWeakNonWildCard(const char* symbolName) const
{
return fForceWeakSymbols.containsNonWildcard(symbolName);
}
bool Options::forceNotWeakNonWildcard(const char* symbolName) const
{
return fForceNotWeakSymbols.containsNonWildcard(symbolName);
}
bool Options::forceCoalesce(const char* symbolName) const
{
return fForceCoalesceSymbols.contains(symbolName);
}
bool Options::shouldExport(const char* symbolName) const
{
switch (fExportMode) {
case kExportSome:
return fExportSymbols.contains(symbolName);
case kDontExportSome:
return ! fDontExportSymbols.contains(symbolName);
case kExportDefault:
return true;
}
throw "internal error";
}
bool Options::shouldReExport(const char* symbolName) const
{
return fReExportSymbols.contains(symbolName);
}
bool Options::keepLocalSymbol(const char* symbolName) const
{
switch (fLocalSymbolHandling) {
case kLocalSymbolsAll:
return true;
case kLocalSymbolsNone:
return false;
case kLocalSymbolsSelectiveInclude:
return fLocalSymbolsIncluded.contains(symbolName);
case kLocalSymbolsSelectiveExclude:
return ! fLocalSymbolsExcluded.contains(symbolName);
}
throw "internal error";
}
const std::vector<const char*>* Options::sectionOrder(const char* segName) const
{
for (std::vector<SectionOrderList>::const_iterator it=fSectionOrder.begin(); it != fSectionOrder.end(); ++it) {
if ( strcmp(it->segmentName, segName) == 0 )
return &it->sectionOrder;
}
return NULL;
}
void Options::setInferredArch(cpu_type_t type, cpu_subtype_t subtype)
{
assert(fArchitecture == 0);
for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) {
fArchitecture = type;
fSubArchitecture = subtype;
fArchitectureName = t->archName;
fHasPreferredSubType = t->isSubType;
fArchSupportsThumb2 = t->supportsThumb2;
#if SUPPORT_ARCH_arm64e
if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64E) ) {
fSupportsAuthenticatedPointers = true;
}
#endif
break;
}
}
if ( fArchitecture == 0 ) {
fArchitecture = type;
fSubArchitecture = subtype;
fArchitectureName = "unknown";
}
fLinkSnapshot.recordArch(fArchitectureName);
}
void Options::setInferredPlatform(ld::Platform platform, uint32_t minOsVers)
{
assert(platforms().empty());
fPlatforms.insert(ld::PlatformVersion(platform, minOsVers));
if ( !fMakeCompressedDyldInfo && platforms().minOS(ld::version2009) && !fMakeCompressedDyldInfoForceOff )
fMakeCompressedDyldInfo = true;
if ( platforms().minOS(ld::version2008) )
fUseSimplifiedDylibReExports = true;
}
bool Options::armUsesZeroCostExceptions() const
{
return ( (fArchitecture == CPU_TYPE_ARM) && (fSubArchitecture == CPU_SUBTYPE_ARM_V7K) );
}
void Options::selectFallbackArch(const char *arch)
{
if (const char* fallbackEnv = getenv("LD_DYLIB_ARCH_FALLBACK") ) {
std::string fallback(fallbackEnv);
auto delimPos = fallback.find(':');
if ( delimPos == std::string::npos || fallback.substr(0, delimPos) != arch )
return;
std::string fallbackTo = fallback.substr(delimPos + 1);
for (const ArchInfo *t = archInfoArray; t->archName != nullptr; ++t) {
if ( fallbackTo == t->archName ) {
fFallbackArchitecture = t->cpuType;
fFallbackSubArchitecture = t->cpuSubType;
}
}
}
else {
if ( (fArchitecture == CPU_TYPE_X86_64) && (fSubArchitecture == CPU_SUBTYPE_X86_64_H) ) {
fFallbackArchitecture = CPU_TYPE_X86_64;
fFallbackSubArchitecture = CPU_SUBTYPE_X86_ALL;
}
}
}
void Options::parseArch(const char* arch)
{
if ( arch == NULL )
throw "-arch must be followed by an architecture string";
for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
if ( strcmp(t->archName,arch) == 0 ) {
fArchitectureName = arch;
fArchitecture = t->cpuType;
fSubArchitecture = t->cpuSubType;
fHasPreferredSubType = t->isSubType;
fArchSupportsThumb2 = t->supportsThumb2;
selectFallbackArch(arch);
return;
}
}
throwf("unknown/unsupported architecture name for: -arch %s", arch);
}
bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const
{
char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8];
sprintf(possiblePath, format, dir, rootName);
bool found = result.checkFileExists(*this, possiblePath);
if ( fTraceDylibSearching )
printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath);
return found;
}
Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const
{
FileInfo result;
const int rootNameLen = strlen(rootName);
if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) {
for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( checkForFile("%s/%s", dir, rootName, result) )
return result;
}
}
else {
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:
if ( lookForDylibs ) {
for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
auto path = std::string(dir) + "/lib" + rootName + ".dylib";
if ( findFile(path, {".tbd"}, result) )
return result;
}
for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( checkForFile("%s/lib%s.so", dir, rootName, result) )
return result;
}
}
if ( !dylibsOnly ) {
for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( checkForFile("%s/lib%s.a", dir, rootName, result) )
return result;
}
}
break;
case kSearchDylibAndArchiveInEachDir:
for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
auto path = std::string(dir) + "/lib" + rootName + ".dylib";
if ( lookForDylibs && findFile(path, {".tbd"}, result) )
return result;
if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) )
return result;
if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) )
return result;
}
break;
}
}
throwf("library not found for -l%s", rootName);
}
Options::FileInfo Options::findFramework(const char* frameworkName) const
{
if ( frameworkName == NULL )
throw "-framework missing next argument";
char temp[strlen(frameworkName)+1];
strcpy(temp, frameworkName);
const char* name = temp;
const char* suffix = NULL;
char* comma = strchr(temp, ',');
if ( comma != NULL ) {
*comma = '\0';
suffix = &comma[1];
}
return findFramework(name, suffix);
}
Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const
{
for (const auto* path : fFrameworkSearchPaths) {
auto possiblePath = std::string(path).append("/").append(rootName).append(".framework/").append(rootName);
if ( suffix != nullptr ) {
char realPath[PATH_MAX];
if ( realpath(possiblePath.c_str(), realPath) != nullptr )
possiblePath = std::string(realPath).append(suffix);
}
FileInfo result;
if ( findFile(possiblePath, {".tbd"}, result) )
return result;
}
if ( suffix != NULL )
return findFramework(rootName, NULL);
else
throwf("framework not found %s", rootName);
}
static std::string replace_extension(const std::string &path, const std::string &ext)
{
auto result = path;
auto lastSlashIdx = result.find_last_of('/');
auto lastDotIdx = result.find_last_of('.');
if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx)
result.erase(lastDotIdx, std::string::npos);
if ( ext.size() > 0 && ext[0] == '.' )
result.append(ext);
else
result.append('.' + ext);
return result;
}
void Options::addTAPIInterface(tapi::LinkerInterfaceFile* interface, const char *path) const {
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
if (tapi::APIVersion::isAtLeast(1, 3)) {
for (auto &name : interface->inlinedFrameworkNames()) {
fTAPIFiles.emplace_back(interface, path, name.c_str());
}
}
#endif
}
bool Options::findFile(const std::string &path, const std::vector<std::string> &tbdExtensions, FileInfo& result) const
{
FileInfo tbdInfo;
for ( const auto &ext : tbdExtensions ) {
auto newPath = replace_extension(path, ext);
bool found = tbdInfo.checkFileExists(*this, newPath.c_str());
if ( fTraceDylibSearching )
printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str());
if ( found )
break;
}
FileInfo dylibInfo;
{
bool found = dylibInfo.checkFileExists(*this, path.c_str());
if ( fTraceDylibSearching )
printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str());
}
if ( tbdInfo.missing() != dylibInfo.missing() ) {
result = tbdInfo.missing() ? dylibInfo : tbdInfo;
}
else if ( !tbdInfo.missing() && !dylibInfo.missing() ) {
if (fPreferTAPIFile) {
result = tbdInfo;
}
else if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) {
result = tbdInfo;
}
else if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) {
result = tbdInfo;
}
else {
if ( strstr(tbdInfo.path, "/SDKs/iOSHostAdditions") == NULL )
warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path);
result = dylibInfo;
}
} else {
return false;
}
return true;
}
static bool startsWith(const std::string& str, const std::string& prefix)
{
return (str.compare(0, prefix.length(), prefix) == 0);
}
static std::string getDirPath(const std::string& path)
{
std::string::size_type lastSlashPos = path.find_last_of('/');
if ( lastSlashPos == std::string::npos )
return "./";
else
return path.substr(0, lastSlashPos+1);
}
Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::File* fromDylib) const
{
FileInfo result;
if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) {
for (const auto* sdkPathDir : fSDKPaths) {
auto possiblePath = std::string(sdkPathDir) + path;
if ( findFile(possiblePath, {".tbd"}, result) )
return result;
}
}
if ( path[0] == '@' ) {
if ( startsWith(path, "@executable_path/") && (fExecutablePath != nullptr) ) {
std::string exeBasedPath = getDirPath(fExecutablePath) + &path[17];
if ( findFile(exeBasedPath, {".tbd"}, result) )
return result;
}
else if ( startsWith(path, "@loader_path/") && (fromDylib != nullptr) ) {
char absPath[PATH_MAX];
if ( realpath(fromDylib->path(), absPath) != NULL ) {
std::string loaderBasedPath = getDirPath(fromDylib->path()) + &path[13];
if ( findFile(loaderBasedPath, {".tbd"}, result) )
return result;
}
}
else if ( startsWith(path, "@rpath/") ) {
if ( fromDylib != nullptr ) {
for (const char* rp : fromDylib->rpaths() ) {
std::string rpath = rp;
if ( startsWith(rpath, "@loader_path/") ) {
char absPath[PATH_MAX];
if ( realpath(fromDylib->path(), absPath) != NULL )
rpath = getDirPath(absPath) + &rpath[13];
else
rpath = getDirPath(fromDylib->path()) + &rpath[13];
}
std::string rpathBasedPath = rpath + "/" + &path[6];
if ( findFile(rpathBasedPath, {".tbd"}, result) )
return result;
}
}
}
}
if (hasInlinedTAPIFile(path)) {
FileInfo inlinedFile(path.c_str());
inlinedFile.isInlined = true;
return inlinedFile;
}
if ( findFile(path, {".tbd"}, result) )
return result;
throwf("file not found: %s", path.c_str());
}
bool Options::hasInlinedTAPIFile(const std::string &path) const {
for (const auto &dylib : fTAPIFiles) {
if (dylib.getInstallName() == path)
return true;
}
return false;
}
tapi::LinkerInterfaceFile* Options::findTAPIFile(const std::string &path) const
{
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
tapi::LinkerInterfaceFile* interface = nullptr;
std::string TBDPath;
tapi::ParsingFlags flags = tapi::ParsingFlags::None;
if (enforceDylibSubtypesMatch())
flags |= tapi::ParsingFlags::ExactCpuSubType;
if (!allowWeakImports())
flags |= tapi::ParsingFlags::DisallowWeakImports;
__block uint32_t linkMinOSVersion = 0;
platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
if (linkMinOSVersion == 0)
linkMinOSVersion = minVersion;
if (platform == ld::Platform::macOS)
linkMinOSVersion = minVersion;
});
for (const auto &dylib : fTAPIFiles) {
if (dylib.getInstallName() == path) {
std::string errorMessage;
auto file = dylib.getInterfaceFile()->getInlinedFramework(path.c_str(), architecture(), subArchitecture(),
flags, tapi::PackedVersion32(linkMinOSVersion), errorMessage);
if (!file)
throw strdup(errorMessage.c_str());
if (!interface) {
interface = file;
TBDPath = dylib.getTAPIFilePath();
} else {
if (interface->getCurrentVersion() == file->getCurrentVersion())
continue;
warning("Inlined framework/dylib mismatch: %s (%s and %s)", path.c_str(),
TBDPath.c_str(), dylib.getTAPIFilePath().c_str());
if (interface->getCurrentVersion() < file->getCurrentVersion()) {
interface = file;
TBDPath = dylib.getTAPIFilePath();
}
}
}
}
return interface;
#else
return nullptr;
#endif
}
Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const
{
FileInfo result;
auto lastSlashPos = installName.find_last_of('/');
auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0;
auto leafName = installName.substr(pos);
bool isFramework = false;
if ( lastSlashPos != std::string::npos ) {
auto frameworkDir = std::string("/").append(leafName).append(".framework/");
if ( installName.rfind(frameworkDir) != std::string::npos )
isFramework = true;
}
if ( isFramework ) {
auto endPos = installName.rfind(".framework");
auto beginPos = installName.find_last_of('/', endPos);
auto leafPath = installName.substr(beginPos);
for (const auto* dir : fFrameworkSearchPaths) {
auto possiblePath = dir + leafPath;
if ( findFile(possiblePath, {".tbd"}, result) )
return result;
}
} else {
bool embeddedDylib = ( (leafName.size() > 6)
&& (leafName.find(".dylib", leafName.size()-6) != std::string::npos)
&& (installName.find(".framework/") != std::string::npos) );
if ( !embeddedDylib ) {
for (const auto* dir : fLibrarySearchPaths) {
std::string possiblePath = dir + std::string("/") + leafName;
if ( findFile(possiblePath, {".tbd"}, result) )
return result;
}
}
}
return findFile(installName, fromDylib);
}
void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal)
{
FILE* file;
const char* comma = strrchr(fileOfPaths, ',');
const char* prefix = NULL;
if ( comma != NULL ) {
file = fopen(fileOfPaths, "r");
if ( file == NULL ) {
prefix = comma+1;
int realFileOfPathsLen = comma-fileOfPaths;
char realFileOfPaths[realFileOfPathsLen+1];
strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen);
realFileOfPaths[realFileOfPathsLen] = '\0';
file = fopen(realFileOfPaths, "r");
if ( file == NULL )
throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno));
this->addDependency(Options::depFileList, realFileOfPaths);
}
}
else {
file = fopen(fileOfPaths, "r");
if ( file == NULL )
throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno));
this->addDependency(Options::depFileList, fileOfPaths);
}
char path[PATH_MAX];
ld::File::Ordinal previousOrdinal = baseOrdinal;
while ( fgets(path, PATH_MAX, file) != NULL ) {
path[PATH_MAX-1] = '\0';
char* eol = strchr(path, '\n');
if ( eol != NULL )
*eol = '\0';
if ( prefix != NULL ) {
char builtPath[strlen(prefix)+strlen(path)+2];
strcpy(builtPath, prefix);
strcat(builtPath, "/");
strcat(builtPath, path);
if (fPipelineFifo != NULL) {
FileInfo info = FileInfo(builtPath);
info.ordinal = previousOrdinal.nextFileListOrdinal();
previousOrdinal = info.ordinal;
info.fromFileList = true;
fInputFiles.push_back(info);
} else {
FileInfo info = findFile(builtPath);
info.ordinal = previousOrdinal.nextFileListOrdinal();
previousOrdinal = info.ordinal;
info.fromFileList = true;
fInputFiles.push_back(info);
}
}
else {
if (fPipelineFifo != NULL) {
FileInfo info = FileInfo(path);
info.ordinal = previousOrdinal.nextFileListOrdinal();
previousOrdinal = info.ordinal;
info.fromFileList = true;
fInputFiles.push_back(info);
} else {
FileInfo info = findFile(path);
info.ordinal = previousOrdinal.nextFileListOrdinal();
previousOrdinal = info.ordinal;
info.fromFileList = true;
fInputFiles.push_back(info);
}
}
}
fclose(file);
}
void Options::SetWithWildcards::remove(const NameSet& toBeRemoved)
{
for(NameSet::const_iterator it=toBeRemoved.begin(); it != toBeRemoved.end(); ++it) {
const char* symbolName = *it;
NameSet::iterator pos = fRegular.find(symbolName);
if ( pos != fRegular.end() )
fRegular.erase(pos);
}
}
bool Options::SetWithWildcards::hasWildCards(const char* symbol)
{
return ( strpbrk(symbol, "*?[") != NULL );
}
void Options::SetWithWildcards::insert(const char* symbol)
{
if ( hasWildCards(symbol) )
fWildCard.push_back(symbol);
else
fRegular.insert(symbol);
}
bool Options::SetWithWildcards::contains(const char* symbol, bool* matchBecauseOfWildcard) const
{
if ( matchBecauseOfWildcard != NULL )
*matchBecauseOfWildcard = false;
if ( fRegular.find(symbol) != fRegular.end() )
return true;
for(std::vector<const char*>::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) {
if ( wildCardMatch(*it, symbol) ) {
if ( matchBecauseOfWildcard != NULL )
*matchBecauseOfWildcard = true;
return true;
}
}
return false;
}
bool Options::SetWithWildcards::containsWithPrefix(const char* symbol, const char* file, bool& wildCardMatch) const
{
wildCardMatch = false;
if ( contains(symbol, &wildCardMatch) )
return true;
if ( file == NULL )
return false;
const char* s = strrchr(file, '/');
if ( s != NULL )
file = s+1;
char buff[strlen(file)+strlen(symbol)+2];
sprintf(buff, "%s:%s", file, symbol);
return contains(buff, &wildCardMatch);
}
bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const
{
return ( fRegular.find(symbol) != fRegular.end() );
}
std::vector<const char*> Options::exportsData() const
{
return fExportSymbols.data();
}
std::vector<const char*> Options::SetWithWildcards::data() const
{
std::vector<const char*> data;
for (NameSet::iterator it=regularBegin(); it != regularEnd(); ++it) {
data.push_back(*it);
}
for (std::vector<const char*>::const_iterator it=fWildCard.begin(); it != fWildCard.end(); ++it) {
data.push_back(*it);
}
return data;
}
bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const
{
++p; const char* b = p;
while ( *p != '\0' ) {
if ( *p == ']') {
const char* e = p;
unsigned char last = '\0';
for ( const char* s = b; s < e; ++s ) {
if ( *s == '-' ) {
unsigned char next = *(++s);
if ( (last <= c) && (c <= next) )
return true;
++s;
}
else {
if ( *s == c )
return true;
last = *s;
}
}
return false;
}
++p;
}
return false;
}
bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) const
{
const char* s = symbol;
for (const char* p = pattern; *p != '\0'; ++p) {
switch ( *p ) {
case '*':
if ( p[1] == '\0' )
return true;
for (const char* t = s; *t != '\0'; ++t) {
if ( wildCardMatch(&p[1], t) )
return true;
}
return false;
case '?':
if ( *s == '\0' )
return false;
++s;
break;
case '[':
if ( ! inCharRange(p, *s) )
return false;
++s;
break;
default:
if ( *s != *p )
return false;
++s;
}
}
return (*s == '\0');
}
void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set)
{
if ( fileOfExports == NULL )
throwf("missing file after %s", option);
int fd = ::open(fileOfExports, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open %s file: %s", option, fileOfExports);
struct stat stat_buf;
::fstat(fd, &stat_buf);
char* p = (char*)malloc(stat_buf.st_size);
if ( p == NULL )
throwf("can't process %s file: %s", option, fileOfExports);
if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
throwf("can't read %s file: %s", option, fileOfExports);
this->addDependency(Options::depMisc, fileOfExports);
::close(fd);
char * const end = &p[stat_buf.st_size];
enum { lineStart, inSymbol, inComment } state = lineStart;
char* symbolStart = NULL;
for (char* s = p; s < end; ++s ) {
switch ( state ) {
case lineStart:
if ( *s =='#' ) {
state = inComment;
}
else if ( !isspace(*s) ) {
state = inSymbol;
symbolStart = s;
}
break;
case inSymbol:
if ( (*s == '\n') || (*s == '\r') ) {
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
set.insert(symbolStart);
symbolStart = NULL;
state = lineStart;
}
break;
case inComment:
if ( (*s == '\n') || (*s == '\r') )
state = lineStart;
break;
}
}
if ( state == inSymbol ) {
warning("missing line-end at end of file \"%s\"", fileOfExports);
int len = end-symbolStart+1;
char* temp = new char[len];
strlcpy(temp, symbolStart, len);
char* last = &temp[len-2];
while ( isspace(*last) ) {
*last = '\0';
--last;
}
set.insert(temp);
}
}
void Options::parseAliasFile(const char* fileOfAliases)
{
int fd = ::open(fileOfAliases, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open alias file: %s", fileOfAliases);
struct stat stat_buf;
::fstat(fd, &stat_buf);
char* p = (char*)malloc(stat_buf.st_size+1);
if ( p == NULL )
throwf("can't process alias file: %s", fileOfAliases);
if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
throwf("can't read alias file: %s", fileOfAliases);
p[stat_buf.st_size] = '\n';
::close(fd);
this->addDependency(Options::depMisc, fileOfAliases);
AliasPair pair;
char * const end = &p[stat_buf.st_size+1];
enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart;
int lineNumber = 1;
for (char* s = p; s < end; ++s ) {
switch ( state ) {
case lineStart:
if ( *s =='#' ) {
state = inComment;
}
else if ( !isspace(*s) ) {
state = inRealName;
pair.realName = s;
}
break;
case inRealName:
if ( *s == '\n' ) {
warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases);
++lineNumber;
state = lineStart;
}
else if ( isspace(*s) ) {
*s = '\0';
state = inBetween;
}
break;
case inBetween:
if ( *s == '\n' ) {
warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases);
++lineNumber;
state = lineStart;
}
else if ( ! isspace(*s) ) {
state = inAliasName;
pair.alias = s;
}
break;
case inAliasName:
if ( *s =='#' ) {
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
fAliases.push_back(pair);
state = inComment;
}
else if ( *s == '\n' ) {
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
fAliases.push_back(pair);
state = lineStart;
}
break;
case inComment:
if ( *s == '\n' )
state = lineStart;
break;
}
}
}
void Options::setUndefinedTreatment(const char* treatment)
{
if ( treatment == NULL )
throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]";
if ( strcmp(treatment, "warning") == 0 )
fUndefinedTreatment = kUndefinedWarning;
else if ( strcmp(treatment, "error") == 0 )
fUndefinedTreatment = kUndefinedError;
else if ( strcmp(treatment, "suppress") == 0 )
fUndefinedTreatment = kUndefinedSuppress;
else if ( strcmp(treatment, "dynamic_lookup") == 0 )
fUndefinedTreatment = kUndefinedDynamicLookup;
else
throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]";
}
Options::Treatment Options::parseTreatment(const char* treatment)
{
if ( treatment == NULL )
return kNULL;
if ( strcmp(treatment, "warning") == 0 )
return kWarning;
else if ( strcmp(treatment, "error") == 0 )
return kError;
else if ( strcmp(treatment, "suppress") == 0 )
return kSuppress;
else
return kInvalid;
}
void Options::setWeakReferenceMismatchTreatment(const char* treatment)
{
if ( treatment == NULL )
throw "-weak_reference_mismatches missing [ error | weak | non-weak ]";
if ( strcmp(treatment, "error") == 0 )
fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError;
else if ( strcmp(treatment, "weak") == 0 )
fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak;
else if ( strcmp(treatment, "non-weak") == 0 )
fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak;
else
throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]";
}
Options::CommonsMode Options::parseCommonsTreatment(const char* mode)
{
if ( mode == NULL )
throw "-commons missing [ ignore_dylibs | use_dylibs | error ]";
if ( strcmp(mode, "ignore_dylibs") == 0 )
return kCommonsIgnoreDylibs;
else if ( strcmp(mode, "use_dylibs") == 0 )
return kCommonsOverriddenByDylibs;
else if ( strcmp(mode, "error") == 0 )
return kCommonsConflictsDylibsError;
else
throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]";
}
void Options::addDylibOverride(const char* paths)
{
if ( paths == NULL )
throw "-dylib_file must followed by two colon separated paths";
const char* colon = strchr(paths, ':');
if ( colon == NULL )
throw "-dylib_file must followed by two colon separated paths";
int len = colon-paths;
char* target = new char[len+2];
strncpy(target, paths, len);
target[len] = '\0';
DylibOverride entry;
entry.installName = target;
entry.useInstead = &colon[1];
fDylibOverrides.push_back(entry);
}
uint64_t Options::parseAddress(const char* addr)
{
char* endptr;
uint64_t result = strtoull(addr, &endptr, 16);
return result;
}
uint32_t Options::parseProtection(const char* prot)
{
uint32_t result = 0;
for(const char* p = prot; *p != '\0'; ++p) {
switch(tolower(*p)) {
case 'r':
result |= VM_PROT_READ;
break;
case 'w':
result |= VM_PROT_WRITE;
break;
case 'x':
result |= VM_PROT_EXECUTE;
break;
case '-':
break;
default:
throwf("unknown -segprot lettter in %s", prot);
}
}
return result;
}
uint64_t Options::parseVersionNumber64(const char* versionString)
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
uint64_t e = 0;
char* end;
a = strtoul(versionString, &end, 10);
if ( *end == '.' ) {
b = strtoul(&end[1], &end, 10);
if ( *end == '.' ) {
c = strtoul(&end[1], &end, 10);
if ( *end == '.' ) {
d = strtoul(&end[1], &end, 10);
if ( *end == '.' ) {
e = strtoul(&end[1], &end, 10);
}
}
}
}
if ( (*end != '\0') || (a > 0xFFFFFF) || (b > 0x3FF) || (c > 0x3FF) || (d > 0x3FF) || (e > 0x3FF) )
throwf("malformed 64-bit a.b.c.d.e version number: %s", versionString);
return (a << 40) | ( b << 30 ) | ( c << 20 ) | ( d << 10 ) | e;
}
uint32_t Options::currentVersion32() const
{
uint32_t a = (fDylibCurrentVersion >> 40) & 0xFFFF;
uint32_t b = (fDylibCurrentVersion >> 30) & 0xFF;
uint32_t c = (fDylibCurrentVersion >> 20) & 0xFF;
uint64_t rep32 = ((uint64_t)a << 40) | ((uint64_t)b << 30) | ((uint64_t)c << 20);
if ( rep32 != fDylibCurrentVersion ) {
warning("truncating -current_version to fit in 32-bit space used by old mach-o format");
a = (fDylibCurrentVersion >> 40) & 0xFFFFFF;
if ( a > 0xFFFF )
a = 0xFFFF;
b = (fDylibCurrentVersion >> 30) & 0x3FF;
if ( b > 0xFF )
b = 0xFF;
c = (fDylibCurrentVersion >> 20) & 0x3FF;
if ( c > 0xFF )
c = 0xFF;
}
return (a << 16) | ( b << 8 ) | c;
}
uint32_t Options::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;
}
static const char* cstringSymbolName(const char* orderFileString)
{
char* result;
asprintf(&result, "cstring=%s", orderFileString);
char* d = result;
for(const char* s=result; *s != '\0'; ++s, ++d) {
if ( *s == '\\' ) {
++s;
switch ( *s ) {
case 'n':
*d = '\n';
break;
case 't':
*d = '\t';
break;
case 'v':
*d = '\v';
break;
case 'b':
*d = '\b';
break;
case 'r':
*d = '\r';
break;
case 'f':
*d = '\f';
break;
case 'a':
*d = '\a';
break;
case '\\':
*d = '\\';
break;
case '?':
*d = '\?';
break;
case '\'':
*d = '\r';
break;
case '\"':
*d = '\"';
break;
case 'x':
{
++s;
char value = 0;
while ( isxdigit(*s) ) {
value *= 16;
if ( isdigit(*s) )
value += (*s-'0');
else
value += ((toupper(*s)-'A') + 10);
++s;
}
*d = value;
}
break;
default:
if ( isdigit(*s) ) {
char value = 0;
while ( isdigit(*s) ) {
value = (value << 3) + (*s-'0');
++s;
}
*d = value;
}
}
}
else {
*d = *s;
}
}
*d = '\0';
return result;
}
void Options::parseOrderFile(const char* path, bool cstring)
{
fAutoOrderInitializers = false;
for (const char* sdkPath : fSDKPaths) {
char fullPath[PATH_MAX];
strlcpy(fullPath, sdkPath, PATH_MAX);
strlcat(fullPath, "/", PATH_MAX);
strlcat(fullPath, path, PATH_MAX);
struct stat statBuffer;
if ( stat(fullPath, &statBuffer) == 0 ) {
path = strdup(fullPath);
break;
}
}
int fd = ::open(path, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open order file: %s", path);
struct stat stat_buf;
::fstat(fd, &stat_buf);
char* p = (char*)malloc(stat_buf.st_size+1);
if ( p == NULL )
throwf("can't process order file: %s", path);
if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
throwf("can't read order file: %s", path);
::close(fd);
p[stat_buf.st_size] = '\n';
this->addDependency(Options::depMisc, path);
fOrderFilePath = strdup(path);
char * const end = &p[stat_buf.st_size+1];
enum { lineStart, inSymbol, inComment } state = lineStart;
char* symbolStart = NULL;
for (char* s = p; s < end; ++s ) {
switch ( state ) {
case lineStart:
if ( *s =='#' ) {
state = inComment;
}
else if ( !isspace(*s) || cstring ) {
state = inSymbol;
symbolStart = s;
}
break;
case inSymbol:
if ( (*s == '\n') || (!cstring && (*s == '#')) ) {
bool wasComment = (*s == '#');
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
if ( strncmp(symbolStart, "ppc:", 4) == 0 ) {
symbolStart = NULL;
}
else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) {
symbolStart = NULL;
}
else if ( strncmp(symbolStart, "i386:", 5) == 0 ) {
if ( fArchitecture == CPU_TYPE_I386 )
symbolStart = &symbolStart[5];
else
symbolStart = NULL;
}
else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) {
if ( fArchitecture == CPU_TYPE_X86_64 )
symbolStart = &symbolStart[7];
else
symbolStart = NULL;
}
else if ( strncmp(symbolStart, "arm:", 4) == 0 ) {
if ( fArchitecture == CPU_TYPE_ARM )
symbolStart = &symbolStart[4];
else
symbolStart = NULL;
}
else if ( strncmp(symbolStart, "arm64:", 6) == 0 ) {
if ( fArchitecture == CPU_TYPE_ARM64 )
symbolStart = &symbolStart[6];
else
symbolStart = NULL;
}
if ( symbolStart != NULL ) {
char* objFileName = NULL;
char* colon = strstr(symbolStart, ".o:");
if ( colon != NULL ) {
colon[2] = '\0';
objFileName = symbolStart;
symbolStart = &colon[3];
}
else {
colon = strstr(symbolStart, ".o):");
if ( colon != NULL ) {
colon[3] = '\0';
objFileName = symbolStart;
symbolStart = &colon[4];
}
}
while ( isspace(*symbolStart) )
++symbolStart;
Options::OrderedSymbol pair;
if ( cstring )
pair.symbolName = cstringSymbolName(symbolStart);
else
pair.symbolName = symbolStart;
pair.objectFileName = objFileName;
fOrderedSymbols.push_back(pair);
}
symbolStart = NULL;
if ( wasComment )
state = inComment;
else
state = lineStart;
}
break;
case inComment:
if ( *s == '\n' )
state = lineStart;
break;
}
}
}
void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path)
{
if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) {
parseOrderFile(path, true);
}
else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) {
warning("sorting of __literal[4,8,16] sections not supported");
}
else {
parseOrderFile(path, false);
}
}
void Options::addSection(const char* segment, const char* section, const char* path)
{
if ( strlen(segment) > 16 )
throw "-seccreate segment name max 16 chars";
if ( strlen(section) > 16 ) {
char* tmp = strdup(section);
tmp[16] = '\0';
warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp);
section = tmp;
}
int fd = ::open(path, O_RDONLY, 0);
if ( fd == -1 )
throwf("can't open -sectcreate file: %s", path);
struct stat stat_buf;
::fstat(fd, &stat_buf);
char* p = (char*)malloc(stat_buf.st_size);
if ( p == NULL )
throwf("can't process -sectcreate file: %s", path);
if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
throwf("can't read -sectcreate file: %s", path);
::close(fd);
ExtraSection info = { segment, section, path, (uint8_t*)p, (uint64_t)stat_buf.st_size };
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::addSegmentRename(const char* srcSegment, const char* dstSegment)
{
if ( strlen(srcSegment) > 16 )
throw "-rename_segment segment name max 16 chars";
if ( strlen(dstSegment) > 16 )
throw "-rename_segment segment name max 16 chars";
SegmentRename info;
info.fromSegment = srcSegment;
info.toSegment = dstSegment;
fSegmentRenames.push_back(info);
}
void Options::addSymbolMove(const char* dstSegment, const char* symbolList,
std::vector<SymbolsMove>& list, const char* optionName)
{
if ( strlen(dstSegment) > 16 )
throwf("%s segment name max 16 chars", optionName);
SymbolsMove tmp;
list.push_back(tmp);
SymbolsMove& info = list.back();
info.toSegment = dstSegment;
loadExportFile(symbolList, optionName, info.symbols);
}
bool Options::moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const
{
for (std::vector<SymbolsMove>::const_iterator it=fSymbolsMovesData.begin(); it != fSymbolsMovesData.end(); ++it) {
const SymbolsMove& info = *it;
if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) {
seg = info.toSegment;
return true;
}
}
return false;
}
bool Options::moveAXMethodList(const char* className) const
{
for (const SymbolsMove& sm : fSymbolsMovesAXMethodLists) {
bool wildcard;
if ( sm.symbols.containsWithPrefix(className, NULL, wildcard) )
return true;
}
return false;
}
bool Options::moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const
{
for (std::vector<SymbolsMove>::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) {
const SymbolsMove& info = *it;
if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) {
seg = info.toSegment;
return true;
}
}
return false;
}
void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr)
{
if ( strlen(segment) > 16 )
throw "-sectalign segment name max 16 chars";
if ( strlen(section) > 16 )
throw "-sectalign section name max 16 chars";
char* endptr;
unsigned long value = strtoul(alignmentStr, &endptr, 16);
if ( *endptr != '\0')
throw "argument for -sectalign is not a hexadecimal number";
if ( value > 0x8000 )
throw "argument for -sectalign must be less than or equal to 0x8000";
if ( value == 0 ) {
warning("zero is not a valid -sectalign");
value = 1;
}
uint8_t alignment = (uint8_t)__builtin_ctz(value);
if ( (unsigned long)(1 << alignment) != value ) {
warning("alignment for -sectalign %s %s is not a power of two, using 0x%X",
segment, section, 1 << alignment);
}
SectionAlignment info = { segment, section, alignment };
fSectionAlignments.push_back(info);
}
void Options::addLibrary(const FileInfo& info)
{
for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
if ( strcmp(info.path, fit->path) == 0 ) {
if ( info.options.fWeakImport )
fit->options.fWeakImport = true;
return;
}
}
fInputFiles.push_back(info);
}
void Options::warnObsolete(const char* arg)
{
warning("option %s is obsolete and being ignored", arg);
}
void Options::cannotBeUsedWithBitcode(const char* arg)
{
if ( fBundleBitcode )
throwf("%s and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together", arg);
}
std::string Options::getVersionString32(uint32_t ver) const
{
if (ver == 0 || ver >= 0x10000000)
return "0.0.0";
unsigned microVersion = ver & 0xFF;
unsigned minorVersion = (ver >> 8) & 0xFF;
unsigned majorVersion = (ver >> 16) & 0xFF;
std::stringstream versionString;
versionString << majorVersion << "." << minorVersion << "." << microVersion;
return versionString.str();
}
std::string Options::getVersionString64(uint64_t ver) const
{
uint64_t a = (ver >> 40) & 0xFFFFFF;
uint64_t b = (ver >> 30) & 0x3FF;
uint64_t c = (ver >> 20) & 0x3FF;
uint64_t d = (ver >> 10) & 0x3FF;
uint64_t e = ver & 0x3FF;
std::stringstream versionString;
versionString << a << "." << b << "." << c << "." << d << "." << e;
return versionString.str();
}
bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &result)
{
result = 0;
if ( versionStr.empty() )
return false;
size_t pos = versionStr.find('.');
if ( pos == std::string::npos )
return false;
std::string majorStr = versionStr.substr(0, pos);
std::string rest = versionStr.substr(pos+1);
try {
size_t majorEnd;
int majorValue = std::stoi(majorStr, &majorEnd);
if ( majorEnd != majorStr.size() )
return false;
if ( majorValue < 0 )
return false;
if ( majorValue > 65535 )
return false;
std::string minorStr;
std::string microStr;
pos = rest.find('.');
if ( pos == std::string::npos ) {
minorStr = rest;
}
else {
minorStr = rest.substr(0, pos);
microStr = rest.substr(pos+1);
}
size_t minorEnd;
int minorValue = std::stoi(minorStr, &minorEnd);
if ( minorEnd != minorStr.size() )
return false;
if ( minorValue < 0 )
return false;
if ( minorValue > 255 )
return false;
int microValue = 0;
if ( !microStr.empty() ) {
size_t microEnd;
microValue = std::stoi(microStr, µEnd);
if ( microEnd != microStr.size() )
return false;
if ( microValue < 0 )
return false;
if ( microValue > 255 )
return false;
}
result = (uint32_t)((majorValue << 16) | (minorValue << 8) | microValue);
return true;
}
catch (...) {
return false;
}
}
std::string Options::getSDKVersionStr() const
{
std::string retval;
auto appendString = [&](const std::string& string) {
if (retval.empty()) {
retval = string;
} else {
retval += "/";
retval += string;
}
};
platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
appendString(getVersionString32(sdkVersion));
});
return retval;
}
std::vector<std::string> Options::writeBitcodeLinkOptions() const
{
__block std::vector<std::string> linkCommand;
switch ( fOutputKind ) {
case Options::kDynamicLibrary:
linkCommand.push_back("-dylib");
linkCommand.push_back("-compatibility_version");
if ( fDylibCompatVersion != 0 ) {
linkCommand.push_back(getVersionString32(fDylibCompatVersion));
} else {
linkCommand.push_back(getVersionString32(currentVersion32()));
}
if ( fDylibCurrentVersion != 0 ) {
linkCommand.push_back("-current_version");
linkCommand.push_back(getVersionString64(fDylibCurrentVersion));
}
linkCommand.push_back("-install_name");
linkCommand.push_back(installPath());
break;
case Options::kDynamicExecutable:
linkCommand.push_back("-execute");
break;
case Options::kObjectFile:
linkCommand.push_back("-r");
break;
default:
throwf("could not write bitcode options file output kind\n");
}
if (!fImplicitlyLinkPublicDylibs)
linkCommand.push_back("-no_implicit_dylibs");
platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
std::string printName = platformInfo(platform).printName;
std::transform(printName.begin(), printName.end(), printName.begin(), [](unsigned char c) -> unsigned char {
if (c == ' ') return '-';
return std::tolower(c);
});
linkCommand.push_back("-platform_version");
linkCommand.push_back(platformInfo(platform).printName);
linkCommand.push_back(getVersionString32(minVersion));
linkCommand.push_back(getVersionString32(sdkVersion));
});
if ( fEntryName ) {
linkCommand.push_back("-e");
linkCommand.push_back(fEntryName);
}
if (!fRPaths.empty()) {
for (std::vector<const char*>::const_iterator it=fRPaths.begin(); it != fRPaths.end(); ++it) {
linkCommand.push_back("-rpath");
linkCommand.push_back(*it);
}
}
if ( fObjCABIVersion1Override ) {
linkCommand.push_back("-objc_abi_version");
linkCommand.push_back("1");
} else if ( fObjCABIVersion2Override ) {
linkCommand.push_back("-objc_abi_version");
linkCommand.push_back("2");
}
if ( fExecutablePath ) {
linkCommand.push_back("-executable_path");
linkCommand.push_back(fExecutablePath);
}
if ( fDeadStrip )
linkCommand.push_back("-dead_strip");
if ( fExportDynamic )
linkCommand.push_back("-export_dynamic");
if ( fMarkAppExtensionSafe && fCheckAppExtensionSafe )
linkCommand.push_back("-application_extension");
if ( fSourceVersionLoadCommandForceOn )
linkCommand.push_back("-add_source_version");
if ( fSourceVersion != 0 ) {
linkCommand.push_back("-source_version");
linkCommand.push_back(getVersionString64(fSourceVersion));
}
if ( !fObjcCategoryMerging )
linkCommand.push_back("-no_objc_category_merging");
return linkCommand;
}
const char* Options::checkForNullArgument(const char* argument_name, const char* arg) const
{
if ( arg == NULL )
throwf("missing argument for %s", argument_name);
return arg;
}
const char* Options::checkForNullVersionArgument(const char* argument_name, const char* arg) const
{
if ( arg == NULL )
throwf("%s missing version argument", argument_name);
return arg;
}
static const ld::PlatformInfo* isPlatformOption(const char* arg)
{
__block const ld::PlatformInfo* result = nullptr;
forEachSupportedPlatform(^(const ld::PlatformInfo& info, bool& stop) {
if ( strcmp(arg, info.commandLineArg) == 0 ) {
result = &info;
stop = true;
}
});
return result;
}
static const ld::PlatformInfo* isPlatformName(const char* arg)
{
__block const ld::PlatformInfo* result = nullptr;
char *end = nullptr;
ld::Platform platformNumber = (ld::Platform)strtol(arg, &end, 10);
forEachSupportedPlatform(^(const ld::PlatformInfo& info, bool& stop) {
if (*end == 0 && platformNumber == info.platform) {
result = &info;
stop = true;
return;
}
auto normalizedStringComp = [](char c1, char c2){
return (c1 == c2 || std::toupper(c1) == std::toupper(c2)
|| (c1 == ' ' && c2 == '-') || (c1 == '-' && c2 == ' '));
};
if (strlen(arg) != strlen(info.printName)) return;
if (!std::equal(&arg[0], &arg[strlen(arg)], &info.printName[0], normalizedStringComp)) return;
result = &info;
stop = true;
});
return result;
}
void Options::parse(int argc, const char* argv[])
{
fLinkSnapshot.recordRawArgs(argc, argv);
this->buildSearchPaths(argc, argv);
fInputFiles.reserve(32);
for(int i=1; i < argc; ++i) {
const char* arg = argv[i];
if ( arg[0] == '-' ) {
int snapshotArgIndex = i;
int snapshotArgCount = -1; int snapshotFileArgIndex = -1;
if (fPrintOptions)
fprintf (stderr, "[Logging ld64 options]\t%s\n", arg);
if ( strcmp(arg, "-iphoneos_version_min") == 0 )
arg = "-ios_version_min";
if ( strcmp(arg, "-macosx_version_min") == 0 )
arg = "-macos_version_min";
if ( (strcmp(arg, "-iosmac_version_min") == 0) || (strcmp(arg, "-uikitformac_version_min") == 0) )
arg = "-maccatalyst_version_min";
if ( (arg[1] == 'L') || (arg[1] == 'F') ) {
snapshotArgCount = 0; if (arg[2] == '\0')
++i;
}
else if ( (strcmp(arg, "--help") == 0)
|| (strcmp(arg, "-help") == 0)) {
fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n");
exit (0);
}
else if ( strcmp(arg, "-arch") == 0 ) {
const char* arch = argv[++i];
parseArch(arch);
fLinkSnapshot.recordArch(arch);
}
else if ( strcmp(arg, "-dynamic") == 0 ) {
}
else if ( strcmp(arg, "-static") == 0 ) {
fForStatic = true;
if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) {
fOutputKind = kStaticExecutable;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dylib") == 0 ) {
fOutputKind = kDynamicLibrary;
}
else if ( strcmp(arg, "-bundle") == 0 ) {
fOutputKind = kDynamicBundle;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dylinker") == 0 ) {
fOutputKind = kDyld;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-execute") == 0 ) {
if ( fOutputKind != kStaticExecutable )
fOutputKind = kDynamicExecutable;
}
else if ( strcmp(arg, "-preload") == 0 ) {
fOutputKind = kPreload;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-r") == 0 ) {
fOutputKind = kObjectFile;
}
else if ( strcmp(arg, "-kext") == 0 ) {
fOutputKind = kKextBundle;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-kext_objects_dir") == 0 ) {
fKextObjectsDirPath = argv[++i];
if ( fKextObjectsDirPath == NULL )
throw "missing argument to -kext_objects_dir";
fKextObjectsEnable = 1;
}
else if ( strcmp(arg, "-no_kext_objects") == 0 ) {
fKextObjectsEnable = 0;
}
else if ( strcmp(arg, "-o") == 0 ) {
snapshotArgCount = 0;
fOutputFile = checkForNullArgument(arg, argv[++i]);
fLinkSnapshot.setOutputPath(fOutputFile);
}
else if ( strncmp(arg, "-lazy-l", 7) == 0 ) {
snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[7], true);
info.options.fLazyLoad = false;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = false;
cannotBeUsedWithBitcode(arg);
warning("-lazy-l is deprecated, changing to regular link");
}
else if ( strcmp(arg, "-lto_library") == 0 ) {
snapshotFileArgIndex = 1;
fOverridePathlibLTO = argv[++i];
if ( fOverridePathlibLTO == NULL )
throw "missing argument to -lto_library";
}
else if ( strcmp(arg, "-cache_path_lto") == 0 ) {
fLtoCachePath = argv[++i];
if ( fLtoCachePath == NULL )
throw "missing argument to -cache_path_lto";
}
else if ( strcmp(arg, "-prune_interval_lto") == 0 ) {
const char* value = argv[++i];
if ( value == NULL )
throw "missing argument to -prune_interval_lto";
char* endptr;
fLtoPruneIntervalOverwrite = true;
fLtoPruneInterval = strtoul(value, &endptr, 10);
if ( *endptr != '\0')
throw "invalid argument for -prune_interval_lto";
}
else if ( strcmp(arg, "-prune_after_lto") == 0 ) {
const char* value = argv[++i];
if ( value == NULL )
throw "missing argument to -prune_after_lto";
char* endptr;
fLtoPruneAfter = strtoul(value, &endptr, 10);
if ( *endptr != '\0')
throw "invalid argument for -prune_after_lto";
}
else if ( strcmp(arg, "-max_relative_cache_size_lto") == 0 ) {
const char* value = argv[++i];
if ( value == NULL )
throw "missing argument to -max_relative_cache_size_lto";
char* endptr;
fLtoMaxCacheSize = strtoul(value, &endptr, 10);
if ( *endptr != '\0')
throw "invalid argument for -max_relative_cache_size_lto";
if (fLtoMaxCacheSize > 100)
throw "Expect a value between 0 and 100 for -max_relative_cache_size_lto";
}
else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) {
snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[2]);
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strncmp(arg, "-weak-l", 7) == 0 ) {
snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[7]);
info.options.fWeakImport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-bind_at_load") == 0 ) {
fBindAtLoad = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-twolevel_namespace") == 0 ) {
fNameSpace = kTwoLevelNameSpace;
}
else if ( strcmp(arg, "-flat_namespace") == 0 ) {
fNameSpace = kFlatNameSpace;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-force_flat_namespace") == 0 ) {
fNameSpace = kForceFlatNameSpace;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-all_load") == 0 ) {
fFullyLoadArchives = true;
}
else if ( strcmp(arg, "-noall_load") == 0) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-ObjC") == 0 ) {
fLoadAllObjcObjectsFromArchives = true;
}
else if ( strcmp(arg, "-force_load") == 0 ) {
const char* path = checkForNullArgument(arg, argv[++i]);
FileInfo info = findFile(path);
info.options.fForceLoad = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( (strcmp(arg, "-dylib_compatibility_version") == 0)
|| (strcmp(arg, "-compatibility_version") == 0)) {
const char* vers = argv[++i];
if ( vers == NULL )
throw "-dylib_compatibility_version missing <version>";
fDylibCompatVersion = parseVersionNumber32(vers);
}
else if ( (strcmp(arg, "-dylib_current_version") == 0)
|| (strcmp(arg, "-current_version") == 0)) {
const char* vers = argv[++i];
if ( vers == NULL )
throw "-dylib_current_version missing <version>";
fDylibCurrentVersion = parseVersionNumber64(vers);
}
else if ( strcmp(arg, "-sectorder") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
throw "-sectorder missing <segment> <section> <file-path>";
snapshotFileArgIndex = 3;
parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-order_file") == 0 ) {
snapshotFileArgIndex = 1;
parseOrderFile(argv[++i], false);
}
else if ( strcmp(arg, "-order_file_statistics") == 0 ) {
fPrintOrderFileStatistics = true;
cannotBeUsedWithBitcode(arg);
}
else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
throw "-sectcreate missing <segment> <section> <file-path>";
snapshotFileArgIndex = 3;
addSection(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
}
else if ( (strcmp(arg, "-dylib_install_name") == 0)
|| (strcmp(arg, "-dylinker_install_name") == 0)
|| (strcmp(arg, "-install_name") == 0)) {
fDylibInstallName = argv[++i];
if ( fDylibInstallName == NULL )
throw "-install_name missing <path>";
}
else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) {
const char* address = argv[++i];
if ( address == NULL )
throwf("%s missing <address>", arg);
fBaseAddress = parseAddress(address);
uint64_t temp = ((fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment));
if ( fBaseAddress != temp ) {
warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment);
fBaseAddress = temp;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-e") == 0 ) {
fEntryName = argv[++i];
}
else if ( strcmp(arg, "-filelist") == 0 ) {
snapshotArgCount = 0;
const char* path = argv[++i];
if ( (path == NULL) || (path[0] == '-') )
throw "-filelist missing <path>";
ld::File::Ordinal baseOrdinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
loadFileList(path, baseOrdinal);
}
else if ( strcmp(arg, "-keep_private_externs") == 0 ) {
cannotBeUsedWithBitcode(arg);
fKeepPrivateExterns = true;
}
else if ( strcmp(arg, "-final_output") == 0 ) {
fFinalName = argv[++i];
if ( strstr(fFinalName, "//") != NULL ) {
char fixedPath[strlen(fFinalName)+1];
char* t = fixedPath;
bool lastWasSlash = false;
for (const char* s=fFinalName; *s != '\0'; ++s) {
if ( *s == '/' ) {
if ( !lastWasSlash )
*t++ = *s;
lastWasSlash = true;
}
else {
*t++ = *s;
lastWasSlash = false;
}
}
*t = '\0';
fFinalName = strdup(fixedPath);
}
}
else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) {
switch ( fInterposeMode ) {
case kInterposeNone:
case kInterposeAllExternal:
fInterposeMode = kInterposeAllExternal;
break;
case kInterposeSome:
break;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-interposable_list") == 0 ) {
snapshotFileArgIndex = 1;
fInterposeMode = kInterposeSome;
loadExportFile(argv[++i], "-interposable_list", fInterposeList);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-single_module") == 0 ) {
fInterposeMode = kInterposeNone;
}
else if ( strcmp(arg, "-exported_symbols_list") == 0 ) {
snapshotFileArgIndex = 1;
if ( fExportMode == kDontExportSome )
throw "can't use -exported_symbols_list and -unexported_symbols_list";
fExportMode = kExportSome;
loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols);
}
else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) {
snapshotFileArgIndex = 1;
if ( fExportMode == kExportSome )
throw "can't use -unexported_symbols_list and -exported_symbols_list";
fExportMode = kDontExportSome;
loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols);
}
else if ( strcmp(arg, "-exported_symbol") == 0 ) {
if ( fExportMode == kDontExportSome )
throw "can't use -exported_symbol and -unexported_symbols";
fExportMode = kExportSome;
const char* symbol = checkForNullArgument(arg, argv[++i]);
fExportSymbols.insert(symbol);
}
else if ( strcmp(arg, "-unexported_symbol") == 0 ) {
if ( fExportMode == kExportSome )
throw "can't use -unexported_symbol and -exported_symbol";
fExportMode = kDontExportSome;
const char* symbol = checkForNullArgument(arg, argv[++i]);
fDontExportSymbols.insert(symbol);
}
else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) {
snapshotFileArgIndex = 1;
if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude )
throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
fLocalSymbolHandling = kLocalSymbolsSelectiveInclude;
loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) {
snapshotFileArgIndex = 1;
if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude )
throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list";
fLocalSymbolHandling = kLocalSymbolsSelectiveExclude;
loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_arch_warnings") == 0 ) {
fIgnoreOtherArchFiles = true;
}
else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) {
fForceSubtypeAll = true;
fAllowCpuSubtypeMismatches = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-weak_library") == 0 ) {
snapshotArgCount = 0;
const char* path = checkForNullArgument(arg, argv[++i]);
FileInfo info = findFile(path);
info.options.fWeakImport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-lazy_library") == 0 ) {
snapshotArgCount = 0;
const char* path = checkForNullArgument(arg, argv[++i]);
FileInfo info = findFile(path);
info.options.fLazyLoad = false;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = false;
cannotBeUsedWithBitcode(arg);
warning("-lazy_library is deprecated, changing to regular link");
}
else if ( strcmp(arg, "-framework") == 0 ) {
snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-weak_framework") == 0 ) {
snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fWeakImport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
}
else if ( strcmp(arg, "-lazy_framework") == 0 ) {
snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fLazyLoad = false;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = false;
cannotBeUsedWithBitcode(arg);
warning("-lazy_framework is deprecated, changing to regular link");
}
else if ( strcmp(arg, "-search_paths_first") == 0 ) {
}
else if ( strcmp(arg, "-search_dylibs_first") == 0 ) {
}
else if ( strcmp(arg, "-undefined") == 0 ) {
setUndefinedTreatment(argv[++i]);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-arch_multiple") == 0 ) {
fMessagesPrefixedWithArchitecture = true;
}
else if ( strcmp(arg, "-read_only_relocs") == 0 ) {
switch ( parseTreatment(argv[++i]) ) {
case kNULL:
case kInvalid:
throw "-read_only_relocs missing [ warning | error | suppress ]";
case kWarning:
fWarnTextRelocs = true;
fAllowTextRelocs = true;
cannotBeUsedWithBitcode(arg);
break;
case kSuppress:
fWarnTextRelocs = false;
fAllowTextRelocs = true;
cannotBeUsedWithBitcode(arg);
break;
case kError:
fWarnTextRelocs = false;
fAllowTextRelocs = false;
break;
}
}
else if ( strcmp(arg, "-unaligned_pointers") == 0 ) {
switch ( parseTreatment(argv[++i]) ) {
case kNULL:
case kInvalid:
throw "-unaligned_pointers missing [ warning | error | suppress ]";
case kWarning:
fUnalignedPointerTreatment = Options::kUnalignedPointerWarning;
cannotBeUsedWithBitcode(arg);
break;
case kSuppress:
fUnalignedPointerTreatment = Options::kUnalignedPointerIgnore;
cannotBeUsedWithBitcode(arg);
break;
case kError:
fUnalignedPointerTreatment = Options::kUnalignedPointerError;
break;
}
}
else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) {
warnObsolete(arg);
++i;
}
else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) {
setWeakReferenceMismatchTreatment(argv[++i]);
}
else if ( strcmp(arg, "-prebind") == 0 ) {
warnObsolete(arg);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-noprebind") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-nofixprebinding") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-dylib_file") == 0 ) {
snapshotArgCount = 0;
addDylibOverride(argv[++i]);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-executable_path") == 0 ) {
fExecutablePath = argv[++i];
if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') )
throw "-executable_path missing <path>";
struct stat statBuffer;
if ( stat(fExecutablePath, &statBuffer) == 0 ) {
if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) {
char* pathWithSlash = new char[strlen(fExecutablePath)+2];
strcpy(pathWithSlash, fExecutablePath);
strcat(pathWithSlash, "/");
fExecutablePath = pathWithSlash;
}
}
}
else if ( strcmp(arg, "-segalign") == 0 ) {
const char* size = argv[++i];
if ( size == NULL )
throw "-segalign missing <size>";
fSegmentAlignment = parseAddress(size);
uint8_t alignment = (uint8_t)__builtin_ctz(fSegmentAlignment);
uint32_t p2aligned = (1 << alignment);
if ( p2aligned != fSegmentAlignment ) {
warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned);
fSegmentAlignment = p2aligned;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-segaddr") == 0 ) {
SegmentStart seg;
seg.name = argv[++i];
if ( (seg.name == NULL) || (argv[i+1] == NULL) )
throw "-segaddr missing segName Adddress";
seg.address = parseAddress(argv[++i]);
uint64_t temp = ((seg.address+fSegmentAlignment-1) & (-fSegmentAlignment));
if ( seg.address != temp )
warning("-segaddr %s not %lld byte aligned", seg.name, fSegmentAlignment);
fCustomSegmentAddresses.push_back(seg);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) {
warnObsolete(arg);
++i;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) {
warnObsolete(arg);
++i;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
warnObsolete(arg);
snapshotFileArgIndex = 1;
const char* name = argv[++i];
if ( name == NULL )
throw "-seg_addr_table missing argument";
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) {
warnObsolete(arg);
++i;
}
else if ( strcmp(arg, "-segprot") == 0 ) {
SegmentProtect seg;
seg.name = argv[++i];
if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) )
throw "-segprot missing segName max-prot init-prot";
seg.max = parseProtection(argv[++i]);
seg.init = parseProtection(argv[++i]);
if ( strcmp(seg.name, "__LINKEDIT") == 0 )
warning("-segprot cannot be used to modify __LINKEDIT protections");
else
fCustomSegmentProtections.push_back(seg);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-pagezero_size") == 0 ) {
const char* size = argv[++i];
if ( size == NULL )
throw "-pagezero_size missing <size>";
fZeroPageSize = parseAddress(size);
uint64_t temp = fZeroPageSize & (-4096); if ( (fZeroPageSize != temp) )
warning("-pagezero_size not page aligned, rounding down");
fZeroPageSize = temp;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-stack_addr") == 0 ) {
const char* address = argv[++i];
if ( address == NULL )
throw "-stack_addr missing <address>";
fStackAddr = parseAddress(address);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-stack_size") == 0 ) {
const char* size = argv[++i];
if ( size == NULL )
throw "-stack_size missing <address>";
fStackSize = parseAddress(size);
}
else if ( strcmp(arg, "-allow_stack_execute") == 0 ) {
fExecutableStack = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-allow_heap_execute") == 0 ) {
fDisableNonExecutableHeap = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sectalign") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) )
throw "-sectalign missing <segment> <section> <file-path>";
addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sectorder_detail") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) {
warnObsolete(arg);
i += 2;
}
else if ( strcmp(arg, "-bundle_loader") == 0 ) {
snapshotFileArgIndex = 1;
fBundleLoader = argv[++i];
if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') )
throw "-bundle_loader missing <path>";
FileInfo info = findFile(fBundleLoader);
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
info.options.fBundleLoader = true;
fInputFiles.push_back(info);
}
else if ( strcmp(arg, "-private_bundle") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-platform_version") == 0 ) {
const char* platform = argv[++i];
const ld::PlatformInfo* info = isPlatformName(platform);
if (info == nullptr) {
throwf("unknown platform string \"%s\"", arg);
}
const char* versStr = checkForNullVersionArgument(arg, argv[++i]);
if ( versStr == NULL )
throwf("%s missing min version argument", arg);
uint32_t minVersValue;
if ( !parsePackedVersion32(versStr, minVersValue) ) {
throwf("%s min version malformed: '%s'", arg, versStr);
}
const char* SDKVersStr = checkForNullVersionArgument(arg, argv[++i]);
if ( SDKVersStr == NULL )
throwf("%s missing sdk version argument", arg);
uint32_t SDKValue;
if ( !parsePackedVersion32(SDKVersStr, SDKValue) ) {
throwf("%s sdk version malformed: '%s'", arg, SDKVersStr);
}
if (fPlatforms.contains(info->platform)) {
std::string existingVersionStr = getVersionString32(fPlatforms.minOS(info->platform));
warning("passed two min versions (%s, %s) for platform %s. Using %s.",
existingVersionStr.c_str(), versStr, info->printName, versStr);
fPlatforms.updateMinVersion(info->platform, minVersValue);
fPlatforms.updateSDKVersion(info->platform, SDKValue);
} else {
fPlatforms.insert(ld::PlatformVersion(info->platform, minVersValue, SDKValue));
}
fPlatfromVersionCmdFound = true;
}
else if ( const ld::PlatformInfo* info = isPlatformOption(arg) ) {
const char* versStr = checkForNullVersionArgument(arg, argv[++i]);
if ( versStr == NULL )
throwf("%s missing version argument", arg);
uint32_t value;
if ( !parsePackedVersion32(versStr, value) ) {
throwf("%s value malformed: '%s'", arg, versStr);
}
if ( value < info->minimumOsVersion ) {
value = info->minimumOsVersion;
warning("OS version (%s) too small, changing to %s", versStr, getVersionString32(info->minimumOsVersion).c_str());
}
if (fPlatforms.contains(info->platform)) {
std::string existingVersionStr = getVersionString32(fPlatforms.minOS(info->platform));
warning("passed two min versions (%s, %s) for platform %s. Using %s.",
existingVersionStr.c_str(), versStr, info->printName, versStr);
fPlatforms.updateMinVersion(info->platform, value);
} else {
fPlatforms.insert(ld::PlatformVersion(info->platform, value));
}
}
else if ( strcmp(arg, "-multiply_defined") == 0 ) {
++i;
}
else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) {
warnObsolete(arg);
++i;
}
else if ( strcmp(arg, "-nomultidefs") == 0 ) {
warnObsolete(arg);
}
else if ( strncmp(arg, "-y", 2) == 0 ) {
warnObsolete("-y");
}
else if ( strcmp(arg, "-Y") == 0 ) {
++i;
}
else if ( strcmp(arg, "-m") == 0 ) {
warnObsolete(arg);
}
else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) {
fWhyLoad = true;
}
else if ( strcmp(arg, "-why_live") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-why_live missing symbol name argument";
fWhyLive.insert(name);
}
else if ( strcmp(arg, "-u") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-u missing argument";
fInitialUndefines.push_back(name);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-U") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-U missing argument";
fAllowedUndefined.insert(name);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-s") == 0 ) {
warnObsolete(arg);
fLocalSymbolHandling = kLocalSymbolsNone;
fDebugInfoStripping = Options::kDebugInfoNone;
}
else if ( strcmp(arg, "-x") == 0 ) {
fLocalSymbolHandling = kLocalSymbolsNone;
}
else if ( strcmp(arg, "-S") == 0 ) {
fDebugInfoStripping = Options::kDebugInfoNone;
}
else if ( strcmp(arg, "-X") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-Si") == 0 ) {
warnObsolete(arg);
fDebugInfoStripping = Options::kDebugInfoFull;
}
else if ( strcmp(arg, "-b") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-Sn") == 0 ) {
warnObsolete(arg);
fDebugInfoStripping = Options::kDebugInfoFull;
}
else if ( strcmp(arg, "-Sp") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-dead_strip") == 0 ) {
fDeadStrip = true;
}
else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) {
fDeadStrip = true;
}
else if ( strcmp(arg, "-w") == 0 ) {
}
else if ( strcmp(arg, "-fatal_warnings") == 0 ) {
}
else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) {
fErrorOnOtherArchFiles = true;
}
else if ( strcmp(arg, "-M") == 0 ) {
}
else if ( strcmp(arg, "-headerpad") == 0 ) {
const char* size = argv[++i];
if ( size == NULL )
throw "-headerpad missing argument";
fMinimumHeaderPad = parseAddress(size);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) {
if ( fBundleBitcode )
warning("-headerpad_max_install_names is ignored when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES)");
else
fMaxMinimumHeaderPad = true;
}
else if ( strcmp(arg, "-t") == 0 ) {
fLogAllFiles = true;
}
else if ( strcmp(arg, "-whatsloaded") == 0 ) {
fLogObjectFiles = true;
}
else if ( strcmp(arg, "-A") == 0 ) {
warnObsolete(arg);
++i;
}
else if ( strcmp(arg, "-umbrella") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-umbrella missing argument";
fUmbrellaName = name;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-allowable_client") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-allowable_client missing argument";
fAllowableClients.push_back(name);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-client_name") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-client_name missing argument";
fClientName = name;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sub_umbrella") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-sub_umbrella missing argument";
fSubUmbellas.push_back(name);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sub_library") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-sub_library missing argument";
fSubLibraries.push_back(name);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-init") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-init missing argument";
fInitFunctionName = name;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dot") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-dot missing argument";
fDotOutputFile = name;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-warn_commons") == 0 ) {
fWarnCommons = true;
}
else if ( strcmp(arg, "-commons") == 0 ) {
fCommonsMode = parseCommonsTreatment(argv[++i]);
}
else if ( strcmp(arg, "-keep_relocs") == 0 ) {
fKeepRelocations = true;
}
else if ( strcmp(arg, "-warn_stabs") == 0 ) {
fWarnStabs = true;
}
else if ( strcmp(arg, "-pause") == 0 ) {
fPause = true;
}
else if ( strcmp(arg, "-print_statistics") == 0 ) {
fStatistics = true;
}
else if ( strcmp(arg, "-d") == 0 ) {
fMakeTentativeDefinitionsReal = true;
}
else if ( strcmp(arg, "-v") == 0 ) {
}
else if ( strcmp(arg, "-Z") == 0 ) {
}
else if ( strcmp(arg, "-syslibroot") == 0 ) {
snapshotArgCount = 0;
++i;
}
else if ( strcmp(arg, "-bitcode_bundle") == 0 ) {
snapshotArgCount = 0;
}
else if ( strcmp(arg, "-no_uuid") == 0 ) {
fUUIDMode = kUUIDNone;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-random_uuid") == 0 ) {
fUUIDMode = kUUIDRandom;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dtrace") == 0 ) {
snapshotFileArgIndex = 1;
const char* name = argv[++i];
if ( name == NULL )
throw "-dtrace missing argument";
fDtraceScriptName = name;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-root_safe") == 0 ) {
fRootSafe = true;
}
else if ( strcmp(arg, "-setuid_safe") == 0 ) {
fSetuidSafe = true;
}
else if ( strcmp(arg, "-alias") == 0 ) {
Options::AliasPair pair;
pair.realName = checkForNullArgument(arg, argv[++i]);
pair.alias = checkForNullArgument(arg, argv[++i]);
fAliases.push_back(pair);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-alias_list") == 0 ) {
snapshotFileArgIndex = 1;
parseAliasFile(argv[++i]);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-save-temps") == 0 ) {
fSaveTempFiles = true;
}
else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) {
fHideSymbols = true;
}
else if ( strcmp(arg, "-bitcode_verify") == 0 ) {
fVerifyBitcode = true;
}
else if ( strcmp(arg, "-bitcode_symbol_map") == 0) {
fReverseMapPath = checkForNullArgument(arg, argv[++i]);
struct stat statbuf;
int ret = ::stat(fReverseMapPath, &statbuf);
if ( ret == 0 && S_ISDIR(statbuf.st_mode)) {
char tempPath[PATH_MAX];
sprintf(tempPath, "%s/XXXXXX", fReverseMapPath);
int tempFile = ::mkstemp(tempPath);
if (tempFile == -1)
throwf("could not write file to symbol map directory: %s", fReverseMapPath);
::close(tempFile);
fReverseMapTempPath = std::string(tempPath);
fReverseMapUUIDRename = true;
} else
fReverseMapTempPath = std::string(fReverseMapPath);
}
else if ( strcmp(arg, "-flto-codegen-only") == 0) {
fLTOCodegenOnly = true;
}
else if ( strcmp(arg, "-ignore_auto_link") == 0) {
fIgnoreAutoLink = true;
}
else if ( strcmp(arg, "-allow_dead_duplicates") == 0) {
fAllowDeadDups = true;
}
else if ( strcmp(arg, "-bitcode_process_mode") == 0 ) {
const char* bitcode_type = checkForNullArgument(arg, argv[++i]);
if ( strcmp(bitcode_type, "strip") == 0 )
fBitcodeKind = kBitcodeStrip;
else if ( strcmp(bitcode_type, "marker") == 0 )
fBitcodeKind = kBitcodeMarker;
else if ( strcmp(bitcode_type, "data") == 0 )
fBitcodeKind = kBitcodeAsData;
else if ( strcmp(bitcode_type, "bitcode") == 0 )
fBitcodeKind = kBitcodeProcess;
else
throw "unknown argument to -bitcode_process_mode {strip,marker,data,bitcode}";
}
else if ( strcmp(arg, "-rpath") == 0 ) {
const char* path = checkForNullArgument(arg, argv[++i]);
fRPaths.push_back(path);
}
else if ( strcmp(arg, "-read_only_stubs") == 0 ) {
fReadOnlyx86Stubs = true;
}
else if ( strcmp(arg, "-slow_stubs") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-map") == 0 ) {
fMapPath = checkForNullArgument(arg, argv[++i]);
}
else if ( strcmp(arg, "-pie") == 0 ) {
fPositionIndependentExecutable = true;
fPIEOnCommandLine = true;
}
else if ( strcmp(arg, "-no_pie") == 0 ) {
fDisablePositionIndependentExecutable = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strncmp(arg, "-reexport-l", 11) == 0 ) {
snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[11], true);
info.options.fReExport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-reexport_library") == 0 ) {
snapshotArgCount = 0;
const char* path = checkForNullArgument(arg, argv[++i]);
FileInfo info = findFile(path);
info.options.fReExport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-reexport_framework") == 0 ) {
snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fReExport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strncmp(arg, "-upward-l", 9) == 0 ) {
snapshotArgCount = 0;
FileInfo info = findLibrary(&arg[9], true);
info.options.fUpward = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-upward_library") == 0 ) {
snapshotArgCount = 0;
const char* path = checkForNullArgument(arg, argv[++i]);
FileInfo info = findFile(path);
info.options.fUpward = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-upward_framework") == 0 ) {
snapshotArgCount = 0;
FileInfo info = findFramework(argv[++i]);
info.options.fUpward = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) {
fDeadStripDylibs = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) {
fImplicitlyLinkPublicDylibs = false;
}
else if ( strcmp(arg, "-new_linker") == 0 ) {
}
else if ( strcmp(arg, "-no_encryption") == 0 ) {
fEncryptableForceOff = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-encryptable") == 0 ) {
fEncryptableForceOn = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_compact_unwind") == 0 ) {
fAddCompactUnwindEncoding = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-mllvm") == 0 ) {
const char* opts = checkForNullArgument(arg, argv[++i]);
fLLVMOptions.push_back(opts);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-mcpu") == 0 ) {
const char* cpu = checkForNullArgument(arg, argv[++i]);
fLtoCpu = cpu;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_order_inits") == 0 ) {
fAutoOrderInitializers = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_order_data") == 0 ) {
fOrderData = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-seg_page_size") == 0 ) {
SegmentSize seg;
seg.name = argv[++i];
if ( (seg.name == NULL) || (argv[i+1] == NULL) )
throw "-seg_page_size missing segName Adddress";
seg.size = parseAddress(argv[++i]);
uint64_t temp = seg.size & (-4096); if ( (seg.size != temp) )
warning("-seg_page_size %s not 4K aligned, rounding down", seg.name);
fCustomSegmentSizes.push_back(seg);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) {
fMarkDeadStrippableDylib = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-exported_symbols_order") == 0 ) {
snapshotFileArgIndex = 1;
loadSymbolOrderFile(argv[++i], fExportSymbolsOrder);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) {
warnObsolete("-no_compact_linkedit");
}
else if ( strcmp(arg, "-no_eh_labels") == 0 ) {
fNoEHLabels = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) {
fWarnCompactUnwind = true;
}
else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) {
fAllowCpuSubtypeMismatches = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) {
fOptimizeZeroFill = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) {
fMergeZeroFill = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_abi_version") == 0 ) {
const char* version = checkForNullVersionArgument(arg, argv[++i]);
if ( strcmp(version, "2") == 0 ) {
fObjCABIVersion1Override = false;
fObjCABIVersion2Override = true;
}
else if ( strcmp(version, "1") == 0 ) {
fObjCABIVersion1Override = true;
fObjCABIVersion2Override = false;
}
else
warning("ignoring unrecognized argument (%s) to -objc_abi_version", version);
}
else if ( strcmp(arg, "-warn_weak_exports") == 0 ) {
fWarnWeakExports = true;
}
else if ( strcmp(arg, "-no_weak_exports") == 0 ) {
fNoWeakExports = true;
}
else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) {
fObjcGcCompaction = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_gc") == 0 ) {
fObjCGc = true;
if ( fObjCGcOnly ) {
warning("-objc_gc overriding -objc_gc_only");
fObjCGcOnly = false;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_gc_only") == 0 ) {
fObjCGcOnly = true;
if ( fObjCGc ) {
warning("-objc_gc_only overriding -objc_gc");
fObjCGc = false;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-demangle") == 0 ) {
fDemangle = true;
}
else if ( strcmp(arg, "-version_load_command") == 0 ) {
fVersionLoadCommandForcedOn = true;
fVersionLoadCommandForcedOff = false;
}
else if ( strcmp(arg, "-function_starts") == 0 ) {
fFunctionStartsForcedOn = true;
fFunctionStartsForcedOff = false;
}
else if ( strcmp(arg, "-no_function_starts") == 0 ) {
fFunctionStartsForcedOff = true;
fFunctionStartsForcedOn = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) {
fDataInCodeInfoLoadCommandForcedOff = true;
fDataInCodeInfoLoadCommandForcedOn = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-data_in_code_info") == 0 ) {
fDataInCodeInfoLoadCommandForcedOn = true;
fDataInCodeInfoLoadCommandForcedOff = false;
}
else if ( strcmp(arg, "-object_path_lto") == 0 ) {
fTempLtoObjectPath = checkForNullArgument(arg, argv[++i]);
}
else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) {
fObjcCategoryMerging = false;
}
else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) {
snapshotFileArgIndex = 1;
loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) {
snapshotFileArgIndex = 1;
loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-force_symbol_weak") == 0 ) {
const char* symbol = argv[++i];
if ( symbol == NULL )
throw "-force_symbol_weak missing <symbol>";
fForceWeakSymbols.insert(symbol);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) {
const char* symbol = argv[++i];
if ( symbol == NULL )
throw "-force_symbol_not_weak missing <symbol>";
fForceNotWeakSymbols.insert(symbol);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) {
snapshotFileArgIndex = 1;
if ( fExportMode == kExportSome )
throw "can't use -exported_symbols_list and -reexported_symbols_list";
loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols);
}
else if ( strcmp(arg, "-dyld_env") == 0 ) {
const char* envarg = argv[++i];
if ( envarg == NULL )
throw "-dyld_env missing ENV=VALUE";
if ( strchr(envarg, '=') == NULL )
throw "-dyld_env missing ENV=VALUE";
fDyldEnvironExtras.push_back(envarg);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) {
fPageAlignDataAtoms = true;
cannotBeUsedWithBitcode(arg);
}
else if (strcmp(arg, "-debug_snapshot") == 0) {
fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG);
fSnapshotRequested = true;
cannotBeUsedWithBitcode(arg);
}
else if (strcmp(arg, "-snapshot_dir") == 0) {
const char* path = argv[++i];
if ( path == NULL )
throw "-snapshot_dir missing path";
fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG);
fLinkSnapshot.setSnapshotPath(path);
fSnapshotRequested = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-source_version") == 0 ) {
const char* vers = argv[++i];
if ( vers == NULL )
throw "-source_version missing <version>";
fSourceVersion = parseVersionNumber64(vers);
}
else if ( strcmp(arg, "-add_source_version") == 0 ) {
fSourceVersionLoadCommandForceOn = true;
}
else if ( strcmp(arg, "-no_source_version") == 0 ) {
fSourceVersionLoadCommandForceOff = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sdk_version") == 0 ) {
const char* vers = argv[++i];
if ( vers == NULL )
throw "-sdk_version missing <version>";
fSDKVersion = parseVersionNumber32(vers);
}
else if ( strcmp(arg, "-dependent_dr_info") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) {
fKextsUseStubs = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dependency_info") == 0 ) {
snapshotArgCount = 0;
++i;
}
else if ( strcmp(arg, "-export_dynamic") == 0 ) {
fExportDynamic = true;
}
else if ( strcmp(arg, "-force_symbols_coalesce_list") == 0 ) {
snapshotFileArgIndex = 1;
loadExportFile(argv[++i], "-force_symbols_coalesce_list", fForceCoalesceSymbols);
}
else if ( strcmp(arg, "-add_linker_option") == 0 ) {
const char* optString = argv[++i];
if ( optString == NULL )
throw "-add_linker_option missing <option>";
std::vector<const char*> opts;
char* buffer = strdup(optString);
char* start = buffer;
for (char* s = buffer; ; ++s) {
if ( isspace(*s) ) {
*s = '\0';
opts.push_back(start);
start = s+1;
}
else if ( *s == '\0' ) {
opts.push_back(start);
break;
}
}
fLinkerOptions.push_back(opts);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-allow_simulator_linking_to_macosx_dylibs") == 0 ) {
fAllowSimulatorToLinkWithMacOSX = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-simulator_support") == 0 ) {
fSimulatorSupportDylib = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-keep_dwarf_unwind") == 0 ) {
fKeepDwarfUnwindForcedOn = true;
fKeepDwarfUnwindForcedOff = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_keep_dwarf_unwind") == 0 ) {
fKeepDwarfUnwindForcedOn = false;
fKeepDwarfUnwindForcedOff = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-verbose_optimization_hints") == 0 ) {
fVerboseOptimizationHints = true;
}
else if ( strcmp(arg, "-ignore_optimization_hints") == 0 ) {
fIgnoreOptimizationHints = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_dtrace_dof") == 0 ) {
fGenerateDtraceDOF = false;
}
else if ( strcmp(arg, "-rename_section") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) || (argv[i+4]==NULL) )
throw "-rename_section missing <segment> <section> <segment> <section>";
addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]);
i += 4;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-rename_segment") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) )
throw "-rename_segment missing <existing-segment> <new-segment>";
addSegmentRename(argv[i+1], argv[i+2]);
i += 2;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-move_to_ro_segment") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) )
throw "-move_to_ro_segment missing <segment> <symbol-list-file>";
addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesCode, "-move_to_ro_segment");
i += 2;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-move_to_rw_segment") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) )
throw "-move_to_rw_segment missing <segment> <symbol-list-file>";
addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesData, "-move_to_rw_segment");
i += 2;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-trace_symbol_layout") == 0 ) {
fTraceSymbolLayout = true;
}
else if ( strcmp(arg, "-no_branch_islands") == 0 ) {
fAllowBranchIslands = false;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-segment_order") == 0 ) {
const char* optString = argv[++i];
if ( optString == NULL )
throw "-segment_order missing colon separated <segment-list>";
if ( !fSegmentOrder.empty() )
throw "-segment_order used more than once";
char* buffer = strdup(optString);
char* start = buffer;
for (char* s = buffer; ; ++s) {
if ( *s == ':' ) {
*s = '\0';
fSegmentOrder.push_back(start);
start = s+1;
}
else if ( *s == '\0' ) {
fSegmentOrder.push_back(start);
break;
}
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-section_order") == 0 ) {
if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) )
throw "-section_order missing <segment> <section-list>";
const char* segName = argv[++i];
const char* optString = argv[++i];
if ( sectionOrder(segName) != NULL )
throwf("-section_order %s ... used more than once", segName);
SectionOrderList dummy;
fSectionOrder.push_back(dummy);
SectionOrderList& entry = fSectionOrder.back();
entry.segmentName = segName;
char* buffer = strdup(optString);
char* start = buffer;
for (char* s = buffer; ; ++s) {
if ( *s == ':' ) {
*s = '\0';
entry.sectionOrder.push_back(start);
start = s+1;
}
else if ( *s == '\0' ) {
entry.sectionOrder.push_back(start);
break;
}
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-application_extension") == 0 ) {
fMarkAppExtensionSafe = true;
fCheckAppExtensionSafe = true;
}
else if ( strcmp(arg, "-no_application_extension") == 0 ) {
fMarkAppExtensionSafe = false;
fCheckAppExtensionSafe = false;
}
else if ( strcmp(arg, "-add_ast_path") == 0 ) {
const char* path = argv[++i];
if ( path == NULL )
throw "-add_ast_path missing <option>";
fASTFilePaths.push_back(path);
}
else if ( strcmp(arg, "-force_load_swift_libs") == 0 ) {
fForceLoadSwiftLibs = true;
}
else if ( strcmp(arg, "-not_for_dyld_shared_cache") == 0 ) {
fSharedRegionEligibleForceOff = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-dirty_data_list") == 0 ) {
if ( argv[i+1] == NULL )
throw "-dirty_data_list missing <symbol-list-file>";
addSymbolMove("__DATA_DIRTY", argv[i+1], fSymbolsMovesData, "-dirty_data_list");
++i;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-data_const") == 0 ) {
fUseDataConstSegmentForceOn = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_data_const") == 0 ) {
fUseDataConstSegmentForceOff = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-text_exec") == 0 ) {
fUseTextExecSegment = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-add_split_seg_info") == 0) {
fSharedRegionEligible = true;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-no_deduplicate") == 0 ) {
fDeDupe = false;
}
else if ( strcmp(arg, "-verbose_deduplicate") == 0 ) {
fVerboseDeDupe = true;
}
else if ( strcmp(arg, "-max_default_common_align") == 0 ) {
const char* alignStr = argv[++i];
if ( alignStr == NULL )
throw "-max_default_common_align missing <align-value>";
char* endptr;
unsigned long value = strtoul(alignStr, &endptr, 16);
if ( *endptr != '\0')
throw "argument for -max_default_common_align is not a hexadecimal number";
if ( value > 0x8000 )
throw "argument for -max_default_common_align must be less than or equal to 0x8000";
if ( value == 0 ) {
warning("zero is not a valid -max_default_common_align");
value = 1;
}
uint8_t alignment = (uint8_t)__builtin_ctz(value);
if ( (unsigned long)(1 << alignment) != value ) {
warning("alignment for -max_default_common_align is not a power of two, using 0x%X", 1 << alignment);
}
fMaxDefaultCommonAlign = alignment;
}
else if ( strcmp(arg, "-no_weak_imports") == 0 ) {
fAllowWeakImports = false;
}
else if ( strcmp(arg, "-no_inits") == 0 ) {
fInitializersTreatment = Options::kError;
}
else if ( strcmp(arg, "-no_warn_inits") == 0 ) {
fInitializersTreatment = Options::kSuppress;
}
else if ( strcmp(arg, "-threaded_starts_section") == 0 ) {
fMakeThreadedStartsSection = true;
}
else if ( strcmp(arg, "-fixup_chains_section") == 0 ) {
fMakeChainedFixups = true;
fMakeChainedFixupsSection = true;
}
else if ( strcmp(arg, "-fixup_chains") == 0 ) {
fMakeChainedFixups = true;
}
else if (strcmp(arg, "-debug_variant") == 0) {
fDebugVariant = true;
}
else if (strcmp(arg, "-no_new_main") == 0) {
}
else if (strcmp(arg, "-init_offsets") == 0) {
fMakeInitializersIntoOffsets = true;
}
else if (strcmp(arg, "-oso_prefix") == 0) {
const char* path = argv[++i];
if ( path == NULL )
throw "-oso_prefix missing <path>";
fOSOPrefixPath = path;
}
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);
}
if (snapshotArgCount == -1)
snapshotArgCount = i-snapshotArgIndex+1;
if (snapshotArgCount > 0)
fLinkSnapshot.addSnapshotLinkArg(snapshotArgIndex, snapshotArgCount, snapshotFileArgIndex);
}
else {
FileInfo info = findFile(arg);
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 )
addLibrary(info);
else
fInputFiles.push_back(info);
}
}
if ( fUsingLazyDylibLinking ) {
FileInfo info = findLibrary("lazydylib1.o");
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)argc);
addLibrary(info);
}
if (fSnapshotRequested)
fLinkSnapshot.createSnapshot();
if ( kKextBundle == fOutputKind ) {
if ( fKextObjectsEnable < 0 )
fKextObjectsEnable = ((fArchitecture == CPU_TYPE_ARM64) || (fArchitecture == CPU_TYPE_ARM));
if (fKextObjectsEnable > 0) {
if ( !fKextObjectsDirPath ) {
const char* dstroot;
const char* objdir = getenv("LD_KEXT_OBJECTS_DIR");
if ( objdir )
fKextObjectsDirPath = strdup(objdir);
else if ( (dstroot = getenv("DSTROOT")) )
asprintf((char **)&fKextObjectsDirPath, "%s/AppleInternal/KextObjects", dstroot);
}
fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_KEXT);
fLinkSnapshot.createSnapshot();
}
}
}
bool Options::shouldUseBuildVersion(ld::Platform plat, uint32_t minOSvers) const
{
if ( forceLegacyVersionLoadCommands() )
return false;
if ( isSimulatorSupportDylib() && (plat != ld::Platform::iOSMac) )
return false;
return (minOSvers >= platformInfo(plat).firstOsVersionUsingBuildVersionLC);
}
void Options::buildSearchPaths(int argc, const char* argv[])
{
bool addStandardLibraryDirectories = true;
std::vector<const char*> libraryPaths;
std::vector<const char*> frameworkPaths;
libraryPaths.reserve(10);
frameworkPaths.reserve(10);
for(int i=0; i < argc; ++i) {
if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) {
const char* libSearchDir = &argv[i][2];
if (argv[i][2] == '\0') {
const char* path = argv[++i];
if ( path == NULL )
throw "-L missing argument";
libSearchDir = path;
}
if ( libSearchDir[0] == '\0' )
throw "-L must be immediately followed by a directory path (no space)";
libraryPaths.push_back(libSearchDir);
}
else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) {
const char* frameworkSearchDir = &argv[i][2];
if (argv[i][2] == '\0') {
const char* path = argv[++i];
if ( path == NULL )
throw "-F missing argument";
frameworkSearchDir = path;
}
if ( frameworkSearchDir[0] == '\0' )
throw "-F must be immediately followed by a directory path (no space)";
frameworkPaths.push_back(frameworkSearchDir);
}
else if ( strcmp(argv[i], "-Z") == 0 )
addStandardLibraryDirectories = false;
else if ( strcmp(argv[i], "-v") == 0 ) {
fVerbose = true;
extern const char ldVersionString[];
fprintf(stderr, "%s", ldVersionString);
fprintf(stderr, "BUILD " __TIME__ " " __DATE__"\n");
fprintf(stderr, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS);
if ( argc == 2 ) {
const char* ltoVers = lto::version();
if ( ltoVers != NULL )
fprintf(stderr, "LTO support using: %s (static support for %d, runtime is %d)\n",
ltoVers, lto::static_api_version(), lto::runtime_api_version());
fprintf(stderr, "TAPI support using: %s\n", tapi::Version::getFullVersionAsString().c_str());
exit(0);
}
}
else if ( strcmp(argv[i], "-version_details") == 0 ) {
fVerbose = true;
extern const char ldVersionString[];
fprintf(stdout, "{\n");
fprintf(stdout, "\t\"version\": \"%s\",\n", STRINGIFY(LD64_VERSION_NUM));
fprintf(stdout, "\t\"architectures\": [\n");
char all_archs[] = ALL_SUPPORTED_ARCHS;
char* p = strtok(all_archs, " ");
int i = 0;
while (p) {
if (i++ > 0) {
fprintf(stdout, ",\n");
}
fprintf(stdout, "\t\t\"%s\"", p);
p = strtok(NULL, " ");
}
fprintf(stdout, "\n\t],\n");
const char* ltoVers = lto::version();
if ( ltoVers != NULL ) {
fprintf(stdout, "\t\"lto\": {\n");
fprintf(stdout, "\t\t\"runtime_api_version\": %d,\n", lto::runtime_api_version());
fprintf(stdout, "\t\t\"static_api_version\": %d,\n", lto::static_api_version());
fprintf(stdout, "\t\t\"version_string\": \"%s\"\n", ltoVers);
fprintf(stdout, "\t},\n");
}
fprintf(stdout, "\t\"tapi\": {\n");
fprintf(stdout, "\t\t\"version\": \"%s\",\n", tapi::Version::getAsString().c_str());
fprintf(stdout, "\t\t\"version_string\": \"%s\"\n", tapi::Version::getFullVersionAsString().c_str());
fprintf(stdout, "\t}\n");
fprintf(stdout, "}\n");
if ( argc == 2 ) {
exit(0);
}
}
else if ( strcmp(argv[i], "-syslibroot") == 0 ) {
const char* path = argv[++i];
if ( path == NULL )
throw "-syslibroot missing argument";
fSDKPaths.push_back(path);
}
else if ( strcmp(argv[i], "-search_paths_first") == 0 ) {
fLibrarySearchMode = kSearchDylibAndArchiveInEachDir;
}
else if ( strcmp(argv[i], "-search_dylibs_first") == 0 ) {
fLibrarySearchMode = kSearchAllDirsForDylibsThenAllDirsForArchives;
}
else if ( strcmp(argv[i], "-w") == 0 ) {
sEmitWarnings = false;
}
else if ( strcmp(argv[i], "-fatal_warnings") == 0 ) {
sFatalWarnings = true;
}
else if ( strcmp(argv[i], "-dependency_info") == 0 ) {
const char* path = argv[++i];
if ( path == NULL )
throw "-dependency_info missing <path>";
fDependencyInfoPath = path;
}
else if ( strcmp(argv[i], "-bitcode_bundle") == 0 ) {
fBundleBitcode = true;
}
}
int standardLibraryPathsStartIndex = libraryPaths.size();
int standardFrameworkPathsStartIndex = frameworkPaths.size();
if ( addStandardLibraryDirectories ) {
libraryPaths.push_back("/usr/lib");
libraryPaths.push_back("/usr/local/lib");
frameworkPaths.push_back("/Library/Frameworks/");
frameworkPaths.push_back("/System/Library/Frameworks/");
}
if ( fSDKPaths.size() > 0 ) {
if ( strcmp(fSDKPaths.back(), "/") == 0 ) {
fSDKPaths.clear();
}
}
fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1));
int libIndex = 0;
for (std::vector<const char*>::iterator it = libraryPaths.begin(); it != libraryPaths.end(); ++it, ++libIndex) {
const char* libDir = *it;
bool sdkOverride = false;
if ( libDir[0] == '/' ) {
char betterLibDir[PATH_MAX];
if ( strstr(libDir, "/..") != NULL ) {
if ( realpath(libDir, betterLibDir) != NULL )
libDir = strdup(betterLibDir);
}
const int libDirLen = strlen(libDir);
for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
const char* sdkDir = *sdkit;
const int sdkDirLen = strlen(sdkDir);
char newPath[libDirLen + sdkDirLen+4];
strcpy(newPath, sdkDir);
if ( newPath[sdkDirLen-1] == '/' )
newPath[sdkDirLen-1] = '\0';
strcat(newPath, libDir);
struct stat statBuffer;
if ( stat(newPath, &statBuffer) == 0 ) {
if ( (statBuffer.st_mode & S_IFDIR) == 0 ) {
warning("-syslibroot and -L combined path '%s' is not a directory", newPath);
}
else {
fLibrarySearchPaths.push_back(strdup(newPath));
sdkOverride = true;
}
}
}
}
if ( !sdkOverride ) {
if ( (libIndex >= standardLibraryPathsStartIndex) && (fSDKPaths.size() == 1) ) {
}
else {
struct stat statBuffer;
if ( stat(libDir, &statBuffer) == 0 ) {
if ( (statBuffer.st_mode & S_IFDIR) == 0 )
warning("-L path '%s' is not a directory", libDir);
else
fLibrarySearchPaths.push_back(libDir);
}
else if ( !addStandardLibraryDirectories || (strcmp(libDir, "/usr/local/lib") != 0) ) {
warning("directory not found for option '-L%s'", libDir);
}
}
}
}
fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1));
int frameIndex = 0;
for (std::vector<const char*>::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); ++it, ++frameIndex) {
const char* frameworkDir = *it;
bool sdkOverride = false;
if ( frameworkDir[0] == '/' ) {
char betterFrameworkDir[PATH_MAX];
if ( strstr(frameworkDir, "/..") != NULL ) {
if ( realpath(frameworkDir, betterFrameworkDir) != NULL )
frameworkDir = strdup(betterFrameworkDir);
}
const int frameworkDirLen = strlen(frameworkDir);
for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
const char* sdkDir = *sdkit;
const int sdkDirLen = strlen(sdkDir);
char newPath[frameworkDirLen + sdkDirLen+4];
strcpy(newPath, sdkDir);
if ( newPath[sdkDirLen-1] == '/' )
newPath[sdkDirLen-1] = '\0';
strcat(newPath, frameworkDir);
struct stat statBuffer;
if ( stat(newPath, &statBuffer) == 0 ) {
if ( (statBuffer.st_mode & S_IFDIR) == 0 ) {
warning("-syslibroot and -F combined path '%s' is not a directory", newPath);
}
else {
fFrameworkSearchPaths.push_back(strdup(newPath));
sdkOverride = true;
}
}
}
}
if ( !sdkOverride ) {
if ( (frameIndex >= standardFrameworkPathsStartIndex) && (fSDKPaths.size() == 1) ) {
}
else {
struct stat statBuffer;
if ( stat(frameworkDir, &statBuffer) == 0 ) {
if ( (statBuffer.st_mode & S_IFDIR) == 0 )
warning("-F path '%s' is not a directory", frameworkDir);
else
fFrameworkSearchPaths.push_back(frameworkDir);
}
else if ( !addStandardLibraryDirectories || (strcmp(frameworkDir, "/Library/Frameworks/") != 0) ) {
warning("directory not found for option '-F%s'", frameworkDir);
}
}
}
}
if ( fVerbose ) {
fprintf(stderr,"Library search paths:\n");
for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++)
fprintf(stderr,"\t%s\n", *it);
fprintf(stderr,"Framework search paths:\n");
for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
it != fFrameworkSearchPaths.end();
it++)
fprintf(stderr,"\t%s\n", *it);
}
}
void Options::parsePreCommandLineEnvironmentSettings()
{
if (getenv("LD_FORCE_LEGACY_VERSION_LOAD_CMDS") != NULL) {
fForceLegacyVersionLoadCommands = true;
}
if ((getenv("LD_TRACE_ARCHIVES") != NULL)
|| (getenv("RC_TRACE_ARCHIVES") != NULL))
fTraceArchives = true;
if ((getenv("LD_TRACE_DYLIBS") != NULL)
|| (getenv("RC_TRACE_DYLIBS") != NULL)) {
fTraceDylibs = true;
fTraceIndirectDylibs = true;
}
if ((getenv("LD_TRACE_DEPENDENTS") != NULL)) {
fTraceEmitJSON = true;
fTraceArchives = false;
fTraceDylibs = false;
fTraceIndirectDylibs = false;
}
if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) {
fTraceDylibSearching = true;
}
if (getenv("LD_PRINT_OPTIONS") != NULL)
fPrintOptions = true;
if (fTraceDylibs || fTraceArchives || fTraceEmitJSON)
fTraceOutputFile = getenv("LD_TRACE_FILE");
if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL)
fPrintOrderFileStatistics = true;
if (getenv("LD_NO_ENCRYPT") != NULL) {
fEncryptable = false;
fMarkAppExtensionSafe = true; fCheckAppExtensionSafe = false;
}
if (getenv("LD_APPLICATION_EXTENSION_SAFE") != NULL) {
fMarkAppExtensionSafe = true;
fCheckAppExtensionSafe = false;
}
if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL)
fAllowCpuSubtypeMismatches = true;
if (getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH") != NULL)
fEnforceDylibSubtypesMatch = true;
if (getenv("LD_WARN_ON_SWIFT_ABI_VERSION_MISMATCHES") != NULL)
fWarnOnSwiftABIVersionMismatches = true;
sWarningsSideFilePath = getenv("LD_WARN_FILE");
const char* customDyldPath = getenv("LD_DYLD_PATH");
if ( customDyldPath != NULL )
fDyldInstallPath = customDyldPath;
const char* debugArchivePath = getenv("LD_DEBUG_SNAPSHOT");
if (debugArchivePath != NULL) {
fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG);
if (strlen(debugArchivePath) > 0)
fLinkSnapshot.setSnapshotPath(debugArchivePath);
fSnapshotRequested = true;
}
const char* pipeFdString = getenv("LD_PIPELINE_FIFO");
if (pipeFdString != NULL) {
fPipelineFifo = pipeFdString;
}
if ( getenv("ZERO_AR_DATE") != NULL )
fZeroModTimeInDebugMap = true;
char rawPath[PATH_MAX];
char path[PATH_MAX];
char *base;
uint32_t bufSize = PATH_MAX;
if ( _NSGetExecutablePath(rawPath, &bufSize) != -1 ) {
if ( realpath(rawPath, path) != NULL ) {
#define TOOLCHAINBASEPATH "/Developer/Toolchains/"
if ( (base = strstr(path, TOOLCHAINBASEPATH)) )
fToolchainPath = strndup(path, base - path + strlen(TOOLCHAINBASEPATH));
}
}
fBuildContextName = getenv("RC_RELEASE");
if (getenv("LD_PREFER_TAPI_FILE") != NULL)
fPreferTAPIFile = true;
}
void Options::parsePostCommandLineEnvironmentSettings()
{
if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) {
fExecutablePath = fOutputFile;
}
if ( !fDeadStrip ) {
if ( getenv("LD_DEAD_STRIP") != NULL ) {
switch (fOutputKind) {
case Options::kDynamicLibrary:
case Options::kDynamicExecutable:
case Options::kDynamicBundle:
fDeadStrip = true;
break;
case Options::kPreload:
case Options::kObjectFile:
case Options::kDyld:
case Options::kStaticExecutable:
case Options::kKextBundle:
break;
}
}
}
if ( getenv("LD_WARN_COMMONS") != NULL )
fWarnCommons = true;
if ( fSourceVersion == 0 ) {
const char* vers = getenv("RC_ProjectSourceVersion");
if ( vers != NULL )
fSourceVersion = parseVersionNumber64(vers);
}
}
bool Options::sharedCacheEligiblePath(const char* path) const
{
if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) )
return true;
if ( platforms().contains(ld::Platform::macOS) ) {
if ( (strncmp(path, "/Library/Apple/usr/lib/", 23) == 0) || (strncmp(path, "/Library/Apple/System/Library/", 30) == 0) )
return true;
}
uint32_t iosMacVers = platforms().minOS(ld::Platform::iOS);
if ( iosMacVers >= 0x000D0000 ) {
if ( (strncmp(path, "/System/iOSSupport/System/Library/", 34) == 0) || (strncmp(path, "/System/iOSSupport/usr/lib/", 27) == 0) )
return true;
}
return false;
}
void Options::reconfigureDefaults()
{
if ( fOutputKind == Options::kPreload ) {
if ( !platforms().empty() )
fPlatforms.clear();
fPlatforms.insert(ld::PlatformVersion(ld::Platform::freestanding, 0));
}
else if ( (fOutputKind == Options::kStaticExecutable) && platforms().empty() ) {
fPlatforms.insert(ld::PlatformVersion(ld::Platform::freestanding, 0));
}
if ( platforms().empty() || (fArchitecture == 0) )
inferArchAndPlatform();
if ( platforms().empty() ) {
if ( fOutputKind != Options::kObjectFile )
warning("No platform min-version specified on command line");
ld::forEachSupportedPlatform(^(const ld::PlatformInfo& info, bool& stop) {
if ( info.fallbackEnvVarName != NULL ) {
if ( const char* versStr = getenv(info.fallbackEnvVarName) ) {
stop = true;
uint32_t value;
if ( parsePackedVersion32(versStr, value) ) {
if ( value < info.minimumOsVersion ) {
value = info.minimumOsVersion;
warning("OS version (%s) too small, changing to %s", versStr, getVersionString32(info.minimumOsVersion).c_str());
}
fPlatforms.insert(ld::PlatformVersion(info.platform, value));
}
else {
warning("%s env var malformed: '%s'", info.fallbackEnvVarName, versStr);
}
}
}
});
}
switch ( fOutputKind ) {
case Options::kObjectFile:
fForFinalLinkedImage = false;
break;
case Options::kDyld:
fForDyld = true;
fForFinalLinkedImage = true;
fNoEHLabels = true;
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kKextBundle:
fForFinalLinkedImage = true;
fNoEHLabels = true;
break;
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
case Options::kPreload:
fLinkingMainExecutable = true;
fForFinalLinkedImage = true;
fNoEHLabels = true;
break;
}
switch ( fOutputKind ) {
case Options::kPreload:
case Options::kStaticExecutable:
case Options::kKextBundle:
if ( fDataInCodeInfoLoadCommandForcedOn )
fDataInCodeInfoLoadCommand = true;
if ( fFunctionStartsForcedOn )
fFunctionStartsLoadCommand = true;
break;
case Options::kObjectFile:
if ( !fDataInCodeInfoLoadCommandForcedOff )
fDataInCodeInfoLoadCommand = true;
if ( fFunctionStartsForcedOn )
fFunctionStartsLoadCommand = true;
break;
case Options::kDynamicExecutable:
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
if ( !fDataInCodeInfoLoadCommandForcedOff )
fDataInCodeInfoLoadCommand = true;
if ( !fFunctionStartsForcedOff )
fFunctionStartsLoadCommand = true;
break;
}
if ( fOutputKind == kKextBundle ) {
switch ( fArchitecture ) {
case CPU_TYPE_X86_64:
fMakeCompressedDyldInfo = false;
fMakeCompressedDyldInfoForceOff = true;
fAllowTextRelocs = true;
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
case CPU_TYPE_ARM64:
fMakeCompressedDyldInfo = false;
fMakeCompressedDyldInfoForceOff = true;
fAllowTextRelocs = false;
fKextsUseStubs = true;
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
case CPU_TYPE_ARM:
fMakeCompressedDyldInfo = false;
fMakeCompressedDyldInfoForceOff = true;
fAllowTextRelocs = false;
fKextsUseStubs = !fAllowTextRelocs;
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
case CPU_TYPE_I386:
fOutputKind = kObjectFile;
break;
}
}
if ( !platforms().minOS(ld::version2007) )
fImplicitlyLinkPublicDylibs = false;
switch ( fArchitecture ) {
case CPU_TYPE_I386:
fMaxAddress = 0xFFFFFFFF;
break;
case CPU_TYPE_X86_64:
break;
case CPU_TYPE_ARM:
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
fMaxAddress = 0x2FFFFFFF;
break;
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
fMaxAddress = 0xFFFFFFFF;
break;
}
if ( fBaseAddress > fMaxAddress ) {
warning("ignoring -seg1addr 0x%08llX. Address out of range.", fBaseAddress);
fBaseAddress = 0;
}
break;
}
if ( fOutputKind == Options::kDynamicLibrary ) {
if ( platforms().minOS(ld::version2008Fall) )
if ( !fSharedRegionEligibleForceOff )
if ( sharedCacheEligiblePath(this->installPath()) )
fSharedRegionEligible = true;
}
else if ( fOutputKind == Options::kDyld ) {
fSharedRegionEligible = true;
}
if ( (fOutputKind == Options::kKextBundle) && (fArchitecture == CPU_TYPE_ARM64) ) {
fUseDataConstSegment = true;
fUseTextExecSegment = true;
fSharedRegionEligible = true;
}
if ( !fSharedRegionEligible && !fUseDataConstSegmentForceOff && platforms().minOS(ld::version2019Fall) ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
fUseDataConstSegment = !fDisablePositionIndependentExecutable;
break;
case Options::kDynamicBundle:
if ( (fFinalName != NULL) && (strncmp(fFinalName, "/System/Library/UserEventPlugins/", 33) == 0) )
fUseDataConstSegment = false;
else
fUseDataConstSegment = true;
break;
case Options::kDynamicLibrary:
case Options::kDyld:
fUseDataConstSegment = true;
break;
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kPreload:
case Options::kKextBundle:
break;
}
}
if ( fSharedRegionEligible && platforms().minOS(ld::supportsSplitSegV2) ) {
fSharedRegionEncodingV2 = true;
if ( platforms().contains(ld::Platform::macOS) && (fArchitecture == CPU_TYPE_X86_64) ) {
fSharedRegionEncodingV2 = false;
if ( strncmp(this->installPath(), "/usr/lib/swift/", 15) == 0 )
fSharedRegionEncodingV2 = true;
for (const char* searchPath : fLibrarySearchPaths ) {
if ( strstr(searchPath, "/usr/lib/swift") != NULL ) {
fSharedRegionEncodingV2 = true;
break;
}
}
}
if ( fSharedRegionEncodingV2 && (fArchitecture == CPU_TYPE_I386) ) {
fSharedRegionEncodingV2 = false;
}
fIgnoreOptimizationHints = true;
}
if ( fUseDataConstSegmentForceOn ) {
fUseDataConstSegment = true;
}
else if ( fUseDataConstSegmentForceOff ) {
fUseDataConstSegment = false;
}
else if ( fSharedRegionEncodingV2 ) {
fUseDataConstSegment = true;
}
if ( (fFinalName != NULL) && ((strcmp(fFinalName, "/usr/libexec/locationd") == 0) || (strcmp(fFinalName, "/usr/libexec/terminusd") == 0)) ) {
fUseDataConstSegment = false;
}
if ( fUseDataConstSegment ) {
addSectionRename("__DATA", "__got", "__DATA_CONST", "__got");
#if SUPPORT_ARCH_arm64e
addSectionRename("__DATA", "__auth_got", "__DATA_CONST", "__auth_got");
addSectionRename("__DATA", "__auth_ptr", "__DATA_CONST", "__auth_ptr");
#endif
addSectionRename("__DATA", "__nl_symbol_ptr", "__DATA_CONST", "__nl_symbol_ptr");
addSectionRename("__DATA", "__const", "__DATA_CONST", "__const");
addSectionRename("__DATA", "__cfstring", "__DATA_CONST", "__cfstring");
addSectionRename("__DATA", "__mod_init_func", "__DATA_CONST", "__mod_init_func");
addSectionRename("__DATA", "__mod_term_func", "__DATA_CONST", "__mod_term_func");
addSectionRename("__DATA", "__objc_classlist", "__DATA_CONST", "__objc_classlist");
addSectionRename("__DATA", "__objc_nlclslist", "__DATA_CONST", "__objc_nlclslist");
addSectionRename("__DATA", "__objc_catlist", "__DATA_CONST", "__objc_catlist");
addSectionRename("__DATA", "__objc_nlcatlist", "__DATA_CONST", "__objc_nlcatlist");
addSectionRename("__DATA", "__objc_protolist", "__DATA_CONST", "__objc_protolist");
addSectionRename("__DATA", "__objc_imageinfo", "__DATA_CONST", "__objc_imageinfo");
if ( fSharedRegionEligible ) {
addSectionRename("__DATA", "__la_symbol_ptr", "__DATA_CONST", "__la_symbol_ptr");
addSectionRename("__DATA", "__objc_const", "__DATA_CONST", "__objc_const");
}
}
if ( fUseTextExecSegment ) {
addSectionRename("__TEXT", "__text", "__TEXT_EXEC", "__text");
addSectionRename("__TEXT", "__stubs", "__TEXT_EXEC", "__stubs");
}
if ( fSharedRegionEncodingV2 && fUseDataConstSegment ) {
addSectionRename("__DATA", "__thread_vars", "__DATA_DIRTY", "__thread_vars");
}
if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) )
fDebugInfoStripping = Options::kDebugInfoNone;
if ( fOutputKind == Options::kObjectFile )
fUUIDMode = kUUIDNone;
switch ( fArchitecture ) {
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM64:
switch ( fOutputKind ) {
case Options::kObjectFile:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
fAddCompactUnwindEncoding = false;
break;
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDynamicExecutable:
break;
}
break;
case CPU_TYPE_ARM:
if ( armUsesZeroCostExceptions() ) {
switch ( fOutputKind ) {
case Options::kObjectFile:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
fAddCompactUnwindEncoding = false;
break;
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDynamicExecutable:
fAddCompactUnwindEncoding = true;
break;
}
}
else {
fAddCompactUnwindEncoding = false;
fRemoveDwarfUnwindIfCompactExists = false;
}
break;
case 0:
fAddCompactUnwindEncoding = false;
break;
}
switch ( fOutputKind ) {
case Options::kObjectFile:
case Options::kDyld:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
fEncryptable = false;
break;
case Options::kDynamicExecutable:
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
if ( !platforms().minOS(ld::version2013) )
fEncryptable = false;
break;
}
if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64)
)
fEncryptable = false;
if ( fEncryptableForceOn )
fEncryptable = true;
else if ( fEncryptableForceOff )
fEncryptable = false;
if ( fOutputKind == Options::kDyld )
fAutoOrderInitializers = false;
switch ( fOutputKind ) {
case Options::kObjectFile:
case Options::kDyld:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
fOrderData = false;
break;
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
}
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
case Options::kDyld:
if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64E) )
break;
case Options::kPreload:
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kKextBundle:
fMakeCompressedDyldInfoForceOff = true;
break;
}
if ( fMakeCompressedDyldInfoForceOff || !platforms().minOS(ld::version2009) )
fMakeCompressedDyldInfo = false;
switch ( fArchitecture ) {
case CPU_TYPE_ARM:
case CPU_TYPE_ARM64:
break;
case CPU_TYPE_X86_64:
fEnforceDylibSubtypesMatch = false;
break;
case CPU_TYPE_I386:
fEnforceDylibSubtypesMatch = false;
break;
}
if ( fOutputKind == Options::kObjectFile )
fOptimizeZeroFill = true;
if ( fWarnCommons ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
case Options::kPreload:
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kDyld:
case Options::kKextBundle:
fWarnCommons = false;
break;
}
}
if ( platforms().minOS(ld::version2008) )
fUseSimplifiedDylibReExports = true;
if ( platforms().minOS(ld::version2010) && (fOutputKind == kDynamicLibrary) )
fCanUseUpwardDylib = true;
if (fArchitecture == CPU_TYPE_ARM64) {
#if SUPPORT_ARCH_arm64e
if (fSubArchitecture == CPU_SUBTYPE_ARM64E)
{
fNoLazyBinding = true;
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
fUseLinkedListBinding = true;
fUseAuthenticatedStubs = true;
break;
case Options::kPreload:
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kKextBundle:
break;
}
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kObjectFile:
case Options::kDynamicBundle:
case Options::kKextBundle:
fSupportsAuthenticatedPointers = true;
break;
case Options::kStaticExecutable:
case Options::kPreload:
fSupportsAuthenticatedPointers = false;
break;
}
}
#endif
}
if ( fMakeThreadedStartsSection && (fArchitecture != CPU_TYPE_ARM64) ) {
warning("-threaded_starts_section ignored ignored for non-arm64");
fMakeThreadedStartsSection = false;
}
if (fMakeThreadedStartsSection) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kObjectFile:
case Options::kDynamicBundle:
case Options::kKextBundle:
warning("-threaded_starts_section ignored for binaries other than -static or -preload");
fMakeThreadedStartsSection = false;
break;
case Options::kStaticExecutable:
case Options::kPreload:
fUseLinkedListBinding = true;
fNoLazyBinding = true;
#if SUPPORT_ARCH_arm64e
if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64E) )
fSupportsAuthenticatedPointers = true;
#endif
break;
}
}
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
if ( platforms().minOS(ld::supportsChainedFixups) && (fArchitecture != CPU_TYPE_I386) ) {
}
break;
case Options::kDyld:
case Options::kStaticExecutable:
case Options::kPreload:
break;
case Options::kObjectFile:
case Options::kKextBundle:
break;
}
if ( fMakeChainedFixups ) {
fMakeCompressedDyldInfo = false;
fNoLazyBinding = true;
fMakeCompressedDyldInfo = false;
fMakeCompressedDyldInfoForceOff = true;
}
if ( fUseLinkedListBinding )
assert(fNoLazyBinding);
if ( (fArchitecture == CPU_TYPE_I386)
&& (fOutputKind == kDynamicExecutable)
&& platforms().minOS(ld::mac10_7) ) {
fPositionIndependentExecutable = true;
}
if ( (fArchitecture == CPU_TYPE_ARM)
&& fArchSupportsThumb2
&& (fOutputKind == kDynamicExecutable)
&& (platforms().contains(ld::Platform::watchOS) || platforms().minOS(ld::iOS_4_3)) ) {
fPositionIndependentExecutable = true;
}
if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (platforms().minOS(ld::mac10_6) || platforms().contains(ld::Platform::iOSMac)) )
fPositionIndependentExecutable = true;
if ( (fOutputKind == kDynamicExecutable) && platforms().contains(ld::Platform::driverKit) )
fPositionIndependentExecutable = true;
if ( targetIOSSimulator() && (fOutputKind == kDynamicExecutable) )
fPositionIndependentExecutable = true;
if ( fDisablePositionIndependentExecutable )
fPositionIndependentExecutable = false;
if ( ((fArchitecture == CPU_TYPE_ARM64)
)
&& (fOutputKind == kDynamicExecutable) ) {
fPositionIndependentExecutable = true;
if ( fDisablePositionIndependentExecutable )
warning("-no_pie ignored for arm64");
}
switch ( fOutputKind ) {
case Options::kObjectFile:
fOutputSlidable = false;
break;
case Options::kStaticExecutable:
case Options::kDynamicExecutable:
fOutputSlidable = fPositionIndependentExecutable;
break;
case Options::kPreload:
fOutputSlidable = fPIEOnCommandLine;
break;
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kKextBundle:
fOutputSlidable = true;
break;
}
if (platforms().minOS(ld::supportsTLV)
|| ((fArchitecture == CPU_TYPE_ARM64) && platforms().minOS(ld::iOS_8_0))
|| ((fArchitecture == CPU_TYPE_X86_64) && platforms().minOS(ld::iOS_9_0))) {
fTLVSupport = true;
}
switch ( fOutputKind ) {
case Options::kObjectFile:
fVersionLoadCommand = false;
break;
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
if ( fVersionLoadCommandForcedOn )
fVersionLoadCommand = true;
break;
case Options::kDynamicExecutable:
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
if ( !fVersionLoadCommandForcedOff )
fVersionLoadCommand = true;
break;
}
if ( (fOutputKind == kDynamicLibrary) && platforms().minOS(ld::version2010) )
fCanReExportSymbols = true;
switch ( fOutputKind ) {
case Options::kObjectFile:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
case Options::kDyld:
fObjcCategoryMerging = false;
break;
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
}
if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && !fDisableNonExecutableHeap)
fNonExecutableHeap = true;
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
if ( platforms().contains(ld::Platform::driverKit) )
break;
if ( platforms().minOS(ld::version2012) ) {
fEntryPointLoadCommand = true;
if ( fEntryName == NULL )
fEntryName = "_main";
if ( strcmp(fEntryName, "start") == 0 ) {
warning("Ignoring '-e start' because entry point 'start' is not used for the targeted OS version");
fEntryName = "_main";
}
}
else {
fNeedsThreadLoadCommand = true;
if ( fEntryName == NULL )
fEntryName = "start";
}
break;
case Options::kObjectFile:
case Options::kKextBundle:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kDyld:
fNeedsThreadLoadCommand = true;
if ( fEntryName == NULL )
fEntryName = "start"; break;
}
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kKextBundle:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
case Options::kStaticExecutable:
if ( fSourceVersionLoadCommandForceOn ) {
fSourceVersionLoadCommand = true;
}
else if ( fSourceVersionLoadCommandForceOff ) {
fSourceVersionLoadCommand = false;
}
else {
if ( platforms().minOS(ld::version2012) ) {
fSourceVersionLoadCommand = true;
}
else
fSourceVersionLoadCommand = false;
}
break;
case Options::kObjectFile:
case Options::kPreload:
fSourceVersionLoadCommand = false;
break;
}
if (fSDKVersion != 0) {
if (platforms().count() > 1) {
throw "-sdk_version may not be used for zippered binaries";
}
if (fPlatfromVersionCmdFound) {
warning("-sdk_version and -platform_version are not compatible, ignoring -sdk_version");
} else {
__block ld::Platform currentPlatform;
fPlatforms.forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
currentPlatform = platform;
});
fPlatforms.updateSDKVersion(currentPlatform, fSDKVersion);
}
} else {
bool inferredFromSDKpath = false;
if ( !fPlatfromVersionCmdFound && (fSDKPaths.size() > 0) ) {
const char* sdkPath = fSDKPaths.front();
const char* end = &sdkPath[strlen(sdkPath)-1];
while ( !isdigit(*end) && (end > sdkPath) )
--end;
const char* start = end-1;
while ( (isdigit(*start) || (*start == '.')) && (start > sdkPath))
--start;
char sdkVersionStr[32];
int len = end-start+1;
if ( len > 2 ) {
strlcpy(sdkVersionStr, start+1, len);
__block ld::Platform currentPlatform;
fPlatforms.forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
if ( platform != ld::Platform::iOSMac )
currentPlatform = platform;
});
fPlatforms.updateSDKVersion(currentPlatform, parseVersionNumber32(sdkVersionStr));
inferredFromSDKpath = true;
}
}
if ( !fPlatfromVersionCmdFound && !inferredFromSDKpath && (fSDKVersion == 0) && platforms().contains(ld::Platform::macOS)
&& !(getenv("RC_ProjectName") && getenv("MACOSX_DEPLOYMENT_TARGET")) ) {
int mib[2] = { CTL_KERN, KERN_OSRELEASE };
char kernVersStr[100];
size_t strlen = sizeof(kernVersStr);
if ( sysctl(mib, 2, kernVersStr, &strlen, NULL, 0) != -1 ) {
uint32_t kernVers = parseVersionNumber32(kernVersStr);
int minor = (kernVers >> 16) - 4; fPlatforms.updateSDKVersion(ld::Platform::macOS, (0x000A0000 + (minor << 8)));
}
}
}
if ( fMakeCompressedDyldInfo || fMakeChainedFixups ) {
if ( platforms().minOS(ld::version2013) ) {
fAbsoluteSymbols = true;
}
}
if ( fEncryptable ) {
if ( fSegmentAlignment == 4096 )
fSegmentAlignment = 4096*4;
}
if ( fSegmentAlignment == LD_PAGE_SIZE ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
if ( (fArchitecture == CPU_TYPE_ARM64)
|| (fArchitecture == CPU_TYPE_ARM) ) {
fSegmentAlignment = 4096*4;
}
break;
case Options::kStaticExecutable:
case Options::kKextBundle:
if ( (fArchitecture == CPU_TYPE_ARM64)
) {
fSegmentAlignment = 4096*4;
}
break;
case Options::kObjectFile:
break;
case Options::kPreload:
if ( fArchitecture == CPU_TYPE_ARM )
fSegmentAlignment = 4096;
break;
}
}
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
if ( fKeepDwarfUnwindForcedOn ) {
fKeepDwarfUnwind = true;
}
else if ( fKeepDwarfUnwindForcedOff ) {
fKeepDwarfUnwind = false;
}
else {
if ( platforms().minOS(ld::version2013) )
fKeepDwarfUnwind = false;
else
fKeepDwarfUnwind = true;
}
break;
case Options::kKextBundle:
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kPreload:
fKeepDwarfUnwind = true;
break;
}
uint64_t alignedBaseAddress = (fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment);
if ( alignedBaseAddress != fBaseAddress ) {
warning("base address 0x%llX is not properly aligned. Changing it to 0x%llX", fBaseAddress, alignedBaseAddress);
fBaseAddress = alignedBaseAddress;
}
if ( fSymbolsMovesData.empty() && fUseDataConstSegment && ( fDylibInstallName != NULL) && !fSDKPaths.empty() ) {
const char* dylibLeaf = strrchr(fDylibInstallName, '/');
if ( dylibLeaf ) {
char path[PATH_MAX];
strlcpy(path , fSDKPaths.front(), sizeof(path));
strlcat(path , "/AppleInternal/DirtyDataFiles", sizeof(path));
strlcat(path , dylibLeaf, sizeof(path));
strlcat(path , ".dirty", sizeof(path));
FileInfo info;
if ( info.checkFileExists(*this, path) )
addSymbolMove("__DATA_DIRTY", path, fSymbolsMovesData, "-dirty_data_list");
}
}
if ( !fSymbolsMovesData.empty() && !fAliases.empty() ) {
for (const AliasPair& pair : fAliases) {
for (SymbolsMove& moveInfo : fSymbolsMovesData) {
bool matchBecauseOfWildcard;
if ( moveInfo.symbols.contains(pair.alias, &matchBecauseOfWildcard) ) {
moveInfo.symbols.insert(pair.realName);
}
}
}
}
if ( fUseDataConstSegment && (fDylibInstallName != NULL) && !fSDKPaths.empty() ) {
const char* dylibLeaf = strrchr(fDylibInstallName, '/');
if ( dylibLeaf ) {
char path[PATH_MAX];
strlcpy(path , fSDKPaths.front(), sizeof(path));
strlcat(path , "/AppleInternal/AccessibilityLinkerSymbols", sizeof(path));
strlcat(path , dylibLeaf, sizeof(path));
strlcat(path , ".axsymbols", sizeof(path));
FileInfo info;
if ( info.checkFileExists(*this, path) ) {
SymbolsMove tmp;
fSymbolsMovesAXMethodLists.push_back(tmp);
loadExportFile(path, ".axsymbols", fSymbolsMovesAXMethodLists.back().symbols);
}
}
}
if ( (fFinalName != NULL) && fOrderedSymbols.empty() && !fSDKPaths.empty() ) {
char path[PATH_MAX];
strlcpy(path , fSDKPaths.front(), sizeof(path));
strlcat(path , "/AppleInternal/OrderFiles/", sizeof(path));
strlcat(path , fFinalName, sizeof(path));
strlcat(path , ".order", sizeof(path));
FileInfo info;
if ( info.checkFileExists(*this, path) )
parseOrderFile(path, false);
}
if ( fMaxDefaultCommonAlign == 0 ) {
if ( fOutputKind == Options::kPreload )
fMaxDefaultCommonAlign = 8;
else
fMaxDefaultCommonAlign = 15;
}
if ( fSharedRegionEligible && !fRPaths.empty() && !fDebugVariant ) {
warning("OS dylibs should not add rpaths (linker option: -rpath) (Xcode build setting: LD_RUNPATH_SEARCH_PATHS)");
}
if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) && (fFinalName != NULL) && sharedCacheEligiblePath(fFinalName) && !fDebugVariant ) {
if ( strncmp(fDylibInstallName, "@rpath", 6) == 0 )
warning("OS dylibs should not use @rpath for -install_name. Use absolute path instead");
if ( strcmp(fDylibInstallName, fFinalName) != 0 )
warning("OS dylibs -install_name should match its real absolute path");
}
#if SUPPORT_ARCH_arm64e
if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64E) ) {
if ( fUnalignedPointerTreatment == Options::kUnalignedPointerWarning )
warning("for arm64e unaligned pointer errors are fatal");
fUnalignedPointerTreatment = Options::kUnalignedPointerError;
}
#endif
if ( fUnalignedPointerTreatment == Options::kUnalignedPointerIgnore ) {
if ( platforms().minOS(ld::mac10_12) && fSharedRegionEligible ) {
fUnalignedPointerTreatment = Options::kUnalignedPointerWarning;
}
else if ( platforms().minOS(ld::iOS_10_0) ) {
fUnalignedPointerTreatment = Options::kUnalignedPointerWarning;
}
}
if ( fInitializersTreatment == Options::kInvalid ) {
if ( fSharedRegionEligible && (fOutputKind == Options::kDynamicLibrary) && !fDebugVariant ) {
fInitializersTreatment = Options::kWarning;
}
else
fInitializersTreatment = Options::kSuppress;
}
if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64E) ) {
}
}
void Options::checkIllegalOptionCombinations()
{
switch ( fUndefinedTreatment ) {
case kUndefinedError:
break;
case kUndefinedDynamicLookup: {
platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
switch (platform) {
case ld::Platform::macOS:
if ( fSharedRegionEligible )
warning("-undefined dynamic_lookup is incompatible with dyld share cache");
break;
case ld::Platform::iOSMac:
case ld::Platform::unknown:
break;
case ld::Platform::iOS:
case ld::Platform::iOS_simulator:
case ld::Platform::watchOS:
case ld::Platform::watchOS_simulator:
case ld::Platform::bridgeOS:
case ld::Platform::tvOS:
case ld::Platform::tvOS_simulator:
case ld::Platform::freestanding:
case ld::Platform::driverKit:
if ( fOutputKind != kKextBundle )
warning("-undefined dynamic_lookup is deprecated on %s", platformInfo(platform).printName);
break;
}
});
} break;
case kUndefinedWarning:
case kUndefinedSuppress:
if ( fNameSpace == kTwoLevelNameSpace )
throw "can't use -undefined warning or suppress with -twolevel_namespace";
break;
}
for (std::vector<const char*>::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) {
const char* subUmbrella = *it;
bool found = false;
for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
Options::FileInfo& info = *fit;
const char* lastSlash = strrchr(info.path, '/');
if ( lastSlash == NULL )
lastSlash = info.path - 1;
std::string path(&lastSlash[1]);
auto idx = path.find(".tbd", path.size() - 4);
if (idx != std::string::npos)
path.erase(idx);
if ( path == subUmbrella ) {
info.options.fReExport = true;
found = true;
fLinkSnapshot.recordSubUmbrella(info.path);
break;
}
}
if ( ! found )
warning("-sub_umbrella %s does not match a supplied dylib", subUmbrella);
}
for (std::vector<const char*>::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) {
const char* subLibrary = *it;
bool found = false;
for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
Options::FileInfo& info = *fit;
const char* lastSlash = strrchr(info.path, '/');
if ( lastSlash == NULL )
lastSlash = info.path - 1;
const char* dot = strchr(&lastSlash[1], '.');
if ( dot == NULL )
dot = &lastSlash[strlen(lastSlash)];
if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) {
info.options.fReExport = true;
found = true;
fLinkSnapshot.recordSubLibrary(info.path);
break;
}
}
if ( ! found )
warning("-sub_library %s does not match a supplied dylib", subLibrary);
}
if ( fNameSpace != kTwoLevelNameSpace ) {
fFlatNamespace = true;
platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) {
switch (platform) {
case ld::Platform::unknown:
case ld::Platform::macOS:
case ld::Platform::iOSMac:
break;
case ld::Platform::iOS:
case ld::Platform::iOS_simulator:
case ld::Platform::watchOS:
case ld::Platform::watchOS_simulator:
case ld::Platform::bridgeOS:
case ld::Platform::tvOS:
case ld::Platform::tvOS_simulator:
case ld::Platform::driverKit:
case ld::Platform::freestanding:
warning("-flat_namespace is deprecated on %s", platformInfo(platform).printName);
break;
}
});
}
if ( fStackAddr != 0 ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
case CPU_TYPE_ARM:
if ( fStackAddr > 0xFFFFFFFF )
throw "-stack_addr must be < 4G for 32-bit processes";
break;
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM64:
break;
}
if ( (fStackAddr & -4096) != fStackAddr )
throw "-stack_addr must be multiples of 4K";
if ( fStackSize == 0 )
throw "-stack_addr must be used with -stack_size";
}
if ( fStackSize != 0 ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
if ( platforms().contains(ld::Platform::macOS) ) {
if ( fStackSize > 0xFFFFFFFF )
throw "-stack_size must be < 4GB for 32-bit processes";
if ( fStackAddr == 0 )
fStackAddr = 0xC0000000;
if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) )
warning("custom stack placement overlaps and will disable shared region");
}
else {
if ( fStackSize > 0x1F000000 )
throw "-stack_size must be < 496MB";
if ( fStackAddr == 0 )
fStackAddr = 0xC0000000;
}
break;
case CPU_TYPE_ARM:
if ( fStackSize > 0x1F000000 )
throw "-stack_size must be < 496MB";
if ( fStackAddr == 0 )
fStackAddr = 0x1F000000;
if ( fStackAddr > 0x20000000)
throw "-stack_addr must be < 0x20000000 for arm";
break;
case CPU_TYPE_X86_64:
if ( platforms().contains(ld::Platform::macOS) ) {
if ( fStackSize > 0x10000000000 )
throw "-stack_size must be <= 1TB";
if ( fStackAddr == 0 ) {
fStackAddr = 0x00007FFF5C000000LL;
}
}
else {
if ( fStackSize > 0x20000000 )
throw "-stack_size must be <= 512MB";
if ( fStackAddr == 0 ) {
fStackAddr = 0x120000000;
}
break;
case CPU_TYPE_ARM64:
if ( fStackSize > 0x20000000 )
throw "-stack_size must be <= 512MB";
if ( fStackAddr == 0 )
fStackAddr = 0x120000000;
}
break;
}
if ( (fStackSize & (-fSegmentAlignment)) != fStackSize )
throwf("-stack_size must be multiple of segment alignment (%lldKB)", fSegmentAlignment/1024);
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
throw "-stack_size option can only be used when linking a main executable";
}
if ( fStackSize > fStackAddr )
throwf("-stack_size (0x%08llX) must be smaller than -stack_addr (0x%08llX)", fStackSize, fStackAddr);
}
if ( fExecutableStack ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
throw "-allow_stack_execute option can only be used when linking a main executable";
}
}
if ( fDisableNonExecutableHeap ) {
if ( fArchitecture != CPU_TYPE_I386 )
throw "-allow_heap_execute option can only be used when linking for i386";
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
break;
case Options::kStaticExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
throw "-allow_heap_execute option can only be used when linking a main executable";
}
}
if ( fClientName != NULL ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicBundle:
break;
case Options::kStaticExecutable:
case Options::kDynamicLibrary:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
throw "-client_name can only be used with -bundle";
}
}
if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) )
throw "-init can only be used with -dynamiclib";
if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) )
throw "-bundle_loader can only be used with -bundle";
if ( (fDtraceScriptName != NULL) && (fOutputKind == Options::kObjectFile) )
throw "-dtrace can only be used when creating final linked images";
if ( fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) )
throw "-d can only be used with -r";
if ( fRootSafe && (fOutputKind == Options::kObjectFile) )
throw "-root_safe cannot be used with -r";
if ( fSetuidSafe && (fOutputKind == Options::kObjectFile) )
throw "-setuid_safe cannot be used with -r";
if ( !fObjCABIVersion1Override && !fObjCABIVersion2Override && targetIOSSimulator() )
fObjCABIVersion2Override = true;
bool alterObjC1ClassNamesToObjC2 = false;
switch (fArchitecture) {
case CPU_TYPE_I386:
if ( fObjCABIVersion2Override )
alterObjC1ClassNamesToObjC2 = true;
break;
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM:
case CPU_TYPE_ARM64:
alterObjC1ClassNamesToObjC2 = true;
break;
}
std::vector<const char*> impliedExports;
for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); ++it) {
const char* name = *it;
const int len = strlen(name);
if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) {
warning("ignoring %s in export list", name);
}
else if ( (fArchitecture == CPU_TYPE_I386) && !fObjCABIVersion2Override && (strncmp(name, "_OBJC_CLASS_$", 13) == 0) ) {
warning("ignoring Objc2 Class symbol %s in i386 export list", name);
fRemovedExports.insert(name);
}
else if ( alterObjC1ClassNamesToObjC2 && (strncmp(name, ".objc_class_name_", 17) == 0) ) {
fRemovedExports.insert(name);
char* temp;
asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]);
impliedExports.push_back(temp);
asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]);
impliedExports.push_back(temp);
}
else {
fInitialUndefines.push_back(name);
}
}
fExportSymbols.remove(fRemovedExports);
for (std::vector<const char*>::iterator it=impliedExports.begin(); it != impliedExports.end(); ++it) {
const char* name = *it;
fExportSymbols.insert(name);
fInitialUndefines.push_back(name);
}
for (NameSet::iterator it=fReExportSymbols.regularBegin(); it != fReExportSymbols.regularEnd(); ++it) {
fInitialUndefines.push_back(*it);
}
if ( fInitFunctionName != NULL )
fInitialUndefines.push_back(fInitFunctionName);
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
case Options::kDyld:
case Options::kPreload:
if ( !platforms().contains(ld::Platform::driverKit) )
fInitialUndefines.push_back(fEntryName);
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kKextBundle:
break;
}
for (std::vector<AliasPair>::iterator it=fAliases.begin(); it != fAliases.end(); ++it) {
fInitialUndefines.push_back(it->realName);
}
if ( fCustomSegmentAddresses.size() != 0 ) {
if ( fZeroPageSize != ULLONG_MAX ) {
for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
if ( it->address < fZeroPageSize )
throwf("-segaddr %s 0x%llX conflicts with -pagezero_size", it->name, it->address);
}
}
for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
for (std::vector<SegmentStart>::iterator it2 = fCustomSegmentAddresses.begin(); it2 != fCustomSegmentAddresses.end(); ++it2) {
if ( (it->address == it2->address) && (it != it2) )
throwf("duplicate -segaddr addresses for %s and %s", it->name, it2->name);
}
if ( it->address == 0 )
fZeroPageSize = 0;
}
}
if ( fZeroPageSize == ULLONG_MAX ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
case CPU_TYPE_ARM:
fZeroPageSize = 0x1000;
break;
case CPU_TYPE_ARM64:
case CPU_TYPE_X86_64:
fZeroPageSize = 0x100000000ULL;
break;
default:
fZeroPageSize = 0x1000;
}
}
else {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
if ( fZeroPageSize != 0 )
throw "-pagezero_size option can only be used when linking a main executable";
}
}
if ( (fOutputKind == Options::kDynamicExecutable) && (fBaseAddress != 0) && (fZeroPageSize != 0) ) {
SegmentStart seg;
seg.name = "__PAGEZERO";
seg.address = 0;;
fCustomSegmentAddresses.push_back(seg);
}
if ( fDeadStrip && (fOutputKind == Options::kObjectFile) )
throw "-r and -dead_strip cannot be used together";
if ( fRPaths.size() > 0 ) {
if ( !platforms().minOS(ld::version2008) )
throw "-rpath can only be used when targeting Mac OS X 10.5 or later";
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
throw "-rpath can only be used when creating a dynamic final linked image";
}
}
if ( fPositionIndependentExecutable ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
if ( !platforms().minOS(ld::supportsPIE) ) {
throw "-pie requires targetting a newer minimum version";
}
break;
case Options::kStaticExecutable:
case Options::kPreload:
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
warning("-pie being ignored. It is only used when linking a main executable");
fPositionIndependentExecutable = false;
break;
case Options::kObjectFile:
case Options::kDyld:
case Options::kKextBundle:
throw "-pie can only be used when linking a main executable";
}
}
if ( fAllowTextRelocs ) {
if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind != kKextBundle) ) {
warning("-read_only_relocs cannot be used with x86_64");
fAllowTextRelocs = false;
}
else if ( fArchitecture == CPU_TYPE_ARM64 ) {
warning("-read_only_relocs cannot be used with arm64");
}
}
if ( fMarkDeadStrippableDylib ) {
if ( fOutputKind != Options::kDynamicLibrary ) {
warning("-mark_auto_dead_strip can only be used when creating a dylib");
fMarkDeadStrippableDylib = false;
}
}
if ( fForceSubtypeAll ) {
if ( fArchitecture == CPU_TYPE_ARM ) {
warning("-force_cpusubtype_ALL will become unsupported for ARM architectures");
}
}
if ( !fReExportSymbols.empty() ) {
if ( fOutputKind != Options::kDynamicLibrary )
throw "-reexported_symbols_list can only used used when created dynamic libraries";
if ( !platforms().minOS(ld::version2010) )
throw "targeted OS version does not support -reexported_symbols_list";
}
if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) )
throw "-dyld_env can only used used when created main executables";
if ( !fSegmentOrder.empty() && ((fOutputKind != Options::kPreload) && (fOutputKind != kStaticExecutable)) )
throw "-segment_order can only used used with -preload output";
if ( !fBundleBitcode ) {
if ( fVerifyBitcode )
warning("-bitcode_verify is ignored without -bitcode_bundle");
else if ( fHideSymbols )
warning("-bitcode_hide_symbols is ignored without -bitcode_bundle");
}
if ( fReverseMapPath != NULL && !fHideSymbols ) {
throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols";
}
if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess )
fBitcodeKind = Options::kBitcodeAsData;
if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) ) {
if ( platforms().contains(ld::Platform::iOS) && !platforms().minOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff )
warning("embedded dylibs/frameworks only run on iOS 8 or later");
}
if ( fInputFiles.empty() ) {
throw "no object files specified";
}
if (fDylibInstallName && platforms().count() == 1 && platforms().contains(ld::Platform::macOS)) {
std::vector<const char*> implictZipperPaths;
for (const auto& sdkPath : fSDKPaths) {
char autoZipperPath[MAXPATHLEN];
strlcpy(autoZipperPath, sdkPath, MAXPATHLEN);
strlcat(autoZipperPath, "/AppleInternal/LinkerAutoZipperList.txt", MAXPATHLEN);
loadImplictZipperFile(autoZipperPath, implictZipperPaths);
}
for (const auto& zipperPath : implictZipperPaths) {
if (strcmp(zipperPath, fDylibInstallName) == 0) {
fPlatforms.insert(ld::PlatformVersion(ld::Platform::iOSMac, 0x000D0000));
break;
}
}
}
if (platforms().count() > 2) {
throw "Illegal platform count. Only 2 platforms at a maximum can be specified";
}
if (platforms().contains(ld::Platform::iOSMac) && !platforms().minOS(ld::iOS_13_0)) {
throwf("%s platform version must be at least 13.0", ld::platformInfo(ld::Platform::iOSMac).printName);
}
if ( platforms().contains(ld::Platform::iOSMac) && platforms().contains(ld::Platform::macOS) ) {
if ( (fOutputKind != Options::kDynamicLibrary) && (fOutputKind != Options::kDynamicBundle) ) {
warning("Only dylibs and bundles can be zippered, changing output to be macOS only.");
fPlatforms.erase(ld::Platform::iOSMac);
}
}
for (const char* sdkPath : fSDKPaths) {
std::string possiblePath = std::string(sdkPath) + "/AppleInternal/";
struct stat statBuffer;
if ( stat(possiblePath.c_str(), &statBuffer) == 0 ) {
fInternalSDK = true;
break;
}
}
if ( (architecture() == CPU_TYPE_I386) && platforms().contains(ld::Platform::macOS) ) {
if ( !fInternalSDK )
warning("The i386 architecture is deprecated for macOS (remove from the Xcode build setting: ARCHS)");
}
if ( fMakeChainedFixups ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
if ( fBaseAddress != 0 ) {
warning("prefered load addresses (-seg1addr) are disabled with chained fixups");
fBaseAddress = 0;
}
break;
case Options::kPreload:
break;
case Options::kStaticExecutable:
if ( !fMakeChainedFixupsSection )
throwf("to use chained fixups with static executables, use -fixup_chains_section");
break;
case Options::kObjectFile:
throwf("chained fixups cannot be used with relocatable object files");
case Options::kKextBundle:
throwf("chained fixups cannot be used with kext bundles");
break;
}
}
}
void Options::inferArchAndPlatform()
{
uint8_t buffer[4096];
for (const Options::FileInfo& fInfo : fInputFiles) {
int fd = ::open(fInfo.path, O_RDONLY, 0);
if ( fd != -1 ) {
struct stat stat_buf;
if ( fstat(fd, &stat_buf) != -1) {
ssize_t readAmount = stat_buf.st_size;
if ( 4096 < readAmount )
readAmount = 4096;
ssize_t amount = read(fd, buffer, readAmount);
::close(fd);
try {
if ( amount >= readAmount ) {
cpu_type_t type;
cpu_subtype_t subtype;
ld::Platform platform;
uint32_t minOsVersion;
if ( mach_o::relocatable::isObjectFile(buffer, amount, &type, &subtype, &platform, &minOsVersion) ) {
if ( fArchitecture == 0 )
setInferredArch(type, subtype);
if ( platforms().empty() ) {
if ( platform == ld::Platform::unknown )
platform = ld::Platform::freestanding;
setInferredPlatform(platform, minOsVersion);
}
return;
}
}
}
catch (const char* msg) {
throwf("%s in %s", msg, fInfo.path);
}
}
}
}
if ( platforms().empty() )
warning("platform not specified");
if ( fArchitecture == 0 )
warning("-arch not specified");
}
void Options::checkForClassic(int argc, const char* argv[])
{
bool archFound = false;
bool staticFound = false;
bool dtraceFound = false;
bool kextFound = false;
bool rFound = false;
bool creatingMachKernel = false;
bool newLinker = false;
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
CRSetCrashLogMessage(crashreporterBuffer);
#endif
const char* srcRoot = getenv("SRCROOT");
if ( srcRoot != NULL ) {
strlcpy(crashreporterBuffer, "SRCROOT=", crashreporterBufferSize);
strlcat(crashreporterBuffer, srcRoot, crashreporterBufferSize);
strlcat(crashreporterBuffer, "\n", crashreporterBufferSize);
}
#ifdef LD_VERS
strlcat(crashreporterBuffer, LD_VERS, crashreporterBufferSize);
strlcat(crashreporterBuffer, "\n", crashreporterBufferSize);
#endif
strlcat(crashreporterBuffer, "ld ", crashreporterBufferSize);
for(int i=1; i < argc; ++i) {
strlcat(crashreporterBuffer, argv[i], crashreporterBufferSize);
strlcat(crashreporterBuffer, " ", crashreporterBufferSize);
}
for(int i=0; i < argc; ++i) {
const char* arg = argv[i];
if ( arg[0] == '-' ) {
if ( strcmp(arg, "-arch") == 0 ) {
parseArch(argv[++i]);
archFound = true;
}
else if ( strcmp(arg, "-static") == 0 ) {
staticFound = true;
}
else if ( strcmp(arg, "-kext") == 0 ) {
kextFound = true;
}
else if ( strcmp(arg, "-dtrace") == 0 ) {
dtraceFound = true;
}
else if ( strcmp(arg, "-r") == 0 ) {
rFound = true;
}
else if ( strcmp(arg, "-new_linker") == 0 ) {
newLinker = true;
}
else if ( strcmp(arg, "-classic_linker") == 0 ) {
for(int j=i; j < argc; ++j)
argv[j] = argv[j+1];
warning("using ld_classic");
this->gotoClassicLinker(argc-1, argv);
}
else if ( strcmp(arg, "-o") == 0 ) {
const char* outfile = argv[++i];
if ( (outfile != NULL) && (strstr(outfile, "/mach_kernel") != NULL) )
creatingMachKernel = true;
}
}
}
}
void Options::gotoClassicLinker(int argc, const char* argv[])
{
argv[0] = "ld_classic";
for(int j=0; j < argc; ++j) {
if ( (strcmp(argv[j], "-iphoneos_version_min") == 0) || (strcmp(argv[j], "-ios_version_min") == 0) ) {
argv[j] = "-macosx_version_min";
if ( j < argc-1 )
argv[j+1] = "10.5";
break;
}
}
for(int j=0; j < argc; ++j) {
if ( strcmp(argv[j], "-kext") == 0)
argv[j] = "-r";
else if ( strcmp(argv[j], "-dynamic") == 0)
argv[j] = "-static";
}
for(int j=0; j < argc; ++j) {
if ( strcmp(argv[j], "-demangle") == 0)
argv[j] = "-noprebind";
}
for(int i=0; i < argc; ++i) {
if ( strcmp(argv[i], "-v") == 0 ) {
for(int j=0; j < argc; ++j)
printf("%s ", argv[j]);
printf("\n");
break;
}
}
char rawPath[PATH_MAX];
char path[PATH_MAX];
uint32_t bufSize = PATH_MAX;
if ( _NSGetExecutablePath(rawPath, &bufSize) != -1 ) {
if ( realpath(rawPath, path) != NULL ) {
char* lastSlash = strrchr(path, '/');
if ( lastSlash != NULL ) {
strcpy(lastSlash+1, "ld_classic");
argv[0] = path;
execvp(path, (char**)argv);
}
}
}
execvp(argv[0], (char**)argv);
fprintf(stderr, "can't exec ld_classic\n");
exit(1);
}
const char* Options::demangleSymbol(const char* sym) const
{
if ( !fDemangle )
return sym;
static size_t size = 1024;
static char* buff = (char*)malloc(size);
#if DEMANGLE_SWIFT
if ( strncmp(sym, "_$", 2) == 0 ) {
size_t demangledSize = fnd_get_demangled_name(&sym[1], buff, size);
if ( demangledSize > size ) {
size = demangledSize+2;
buff = (char*)realloc(buff, size);
demangledSize = fnd_get_demangled_name(&sym[1], buff, size);
}
if ( demangledSize != 0 )
return buff;
}
#endif
if ( strncmp(sym, "__Z", 3) != 0 )
return sym;
int status;
char* result = abi::__cxa_demangle(&sym[1], buff, &size, &status);
if ( result != NULL ) {
buff = result;
return buff;
}
return sym;
}
void Options::writeDependencyInfo() const
{
if ( !dumpDependencyInfo() )
return;
std::sort(fDependencies.begin(), fDependencies.end(), [](const DependencyEntry& a, const DependencyEntry& b) -> bool {
if ( a.opcode != b.opcode )
return (a.opcode < b.opcode);
return (a.path < b.path);
});
int fd = open(this->dependencyInfoPath(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
if ( fd == -1 )
throwf("Could not open or create -dependency_info file: %s", this->dependencyInfoPath());
uint8_t version = depLinkerVersion;
if ( write(fd, &version, 1) == -1 )
throwf("write() to -dependency_info failed, errno=%d", errno);
extern const char ldVersionString[];
if ( write(fd, ldVersionString, strlen(ldVersionString)+1) == -1 )
throwf("write() to -dependency_info failed, errno=%d", errno);
for (const auto& entry: fDependencies) {
if ( write(fd, &entry.opcode, 1) == -1 )
throwf("write() to -dependency_info failed, errno=%d", errno);
if ( write(fd, entry.path.c_str(), entry.path.size()+1) == -1 )
throwf("write() to -dependency_info failed, errno=%d", errno);
}
::close(fd);
}
void Options::addDependency(uint8_t opcode, const char* path) const
{
if ( !this->dumpDependencyInfo() )
return;
char realPath[PATH_MAX];
if ( path[0] != '/' ) {
if ( realpath(path, realPath) != NULL ) {
path = realPath;
}
}
DependencyEntry entry;
entry.opcode = opcode;
entry.path = path;
fDependencies.push_back(entry);
}
void Options::writeToTraceFile(const char* buffer, size_t len) const
{
if ( fTraceFileDescriptor == -1 ) {
if ( fTraceOutputFile != NULL ) {
fTraceFileDescriptor = open(fTraceOutputFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
if ( fTraceFileDescriptor == -1 )
throwf("Could not open or create trace file (errno=%d): %s", errno, fTraceOutputFile);
}
else {
fTraceFileDescriptor = fileno(stderr);
}
}
while (len > 0) {
ssize_t amountWritten = write(fTraceFileDescriptor, buffer, len);
if ( amountWritten == -1 )
return;
buffer += amountWritten;
len -= amountWritten;
}
}
uint64_t Options::machHeaderVmAddr() const
{
if ( fOutputKind == Options::kDynamicExecutable )
return fBaseAddress + fZeroPageSize;
return fBaseAddress;
}