#include <sys/types.h>
#include <sys/stat.h>
#include <mach/vm_prot.h>
#include <mach-o/dyld.h>
#include <fcntl.h>
#include <vector>
#include "configure.h"
#include "Options.h"
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
extern void printLTOVersion(Options &opts);
extern "C" char* __crashreporter_info__;
static char crashreporterBuffer[1000];
char* __crashreporter_info__ = crashreporterBuffer;
static bool sEmitWarnings = true;
static const char* sWarningsSideFilePath = NULL;
static FILE* sWarningsSideFile = NULL;
void warning(const char* format, ...)
{
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;
}
Options::Options(int argc, const char* argv[])
: fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable),
fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false),
fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace),
fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0),
fBaseWritableAddress(0), fSplitSegs(false),
fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives),
fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false),
fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak),
fClientName(NULL),
fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL),
fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false),
fMinimumHeaderPad(32), fSegmentAlignment(4096),
fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false),
fVerbose(false), fKeepRelocations(false), fWarnStabs(false),
fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false),
fSharedRegionEligible(false), fPrintOrderFileStatistics(false),
fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false),
fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false),
fUsingLazyDylibLinking(false), fEncryptable(true),
fOrderData(true), fMarkDeadStrippableDylib(false),
fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false),
fUseSimplifiedDylibReExports(false), fSaveTempFiles(false)
{
this->checkForClassic(argc, argv);
this->parsePreCommandLineEnvironmentSettings();
this->parse(argc, argv);
this->parsePostCommandLineEnvironmentSettings();
this->reconfigureDefaults();
this->checkIllegalOptionCombinations();
}
Options::~Options()
{
}
const ObjectFile::ReaderOptions& Options::readerOptions()
{
return fReaderOptions;
}
const char* Options::getOutputFilePath()
{
return fOutputFile;
}
std::vector<Options::FileInfo>& Options::getInputFiles()
{
return fInputFiles;
}
Options::OutputKind Options::outputKind()
{
return fOutputKind;
}
bool Options::bindAtLoad()
{
return fBindAtLoad;
}
bool Options::prebind()
{
return fPrebind;
}
bool Options::fullyLoadArchives()
{
return fReaderOptions.fFullyLoadArchives;
}
Options::NameSpace Options::nameSpace()
{
return fNameSpace;
}
const char* Options::installPath()
{
if ( fDylibInstallName != NULL )
return fDylibInstallName;
else if ( fFinalName != NULL )
return fFinalName;
else
return fOutputFile;
}
uint32_t Options::currentVersion()
{
return fDylibCurrentVersion;
}
uint32_t Options::compatibilityVersion()
{
return fDylibCompatVersion;
}
const char* Options::entryName()
{
return fEntryName;
}
uint64_t Options::baseAddress()
{
return fBaseAddress;
}
bool Options::keepPrivateExterns()
{
return fKeepPrivateExterns;
}
bool Options::interposable(const char* name)
{
switch ( fInterposeMode ) {
case kInterposeNone:
return false;
case kInterposeAllExternal:
return true;
case kInterposeSome:
return fInterposeList.contains(name);
}
throw "internal error";
}
bool Options::needsModuleTable()
{
return fNeedsModuleTable;
}
bool Options::ignoreOtherArchInputFiles()
{
return fIgnoreOtherArchFiles;
}
bool Options::forceCpuSubtypeAll()
{
return fForceSubtypeAll;
}
bool Options::traceDylibs()
{
return fReaderOptions.fTraceDylibs;
}
bool Options::traceArchives()
{
return fReaderOptions.fTraceArchives;
}
Options::UndefinedTreatment Options::undefinedTreatment()
{
return fUndefinedTreatment;
}
Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment()
{
return fWeakReferenceMismatchTreatment;
}
const char* Options::umbrellaName()
{
return fUmbrellaName;
}
std::vector<const char*>& Options::allowableClients()
{
return fAllowableClients;
}
const char* Options::clientName()
{
return fClientName;
}
uint64_t Options::zeroPageSize()
{
return fZeroPageSize;
}
bool Options::hasCustomStack()
{
return (fStackSize != 0);
}
uint64_t Options::customStackSize()
{
return fStackSize;
}
uint64_t Options::customStackAddr()
{
return fStackAddr;
}
bool Options::hasExecutableStack()
{
return fExecutableStack;
}
std::vector<const char*>& Options::initialUndefines()
{
return fInitialUndefines;
}
bool Options::printWhyLive(const char* symbolName)
{
return ( fWhyLive.find(symbolName) != fWhyLive.end() );
}
const char* Options::initFunctionName()
{
return fInitFunctionName;
}
const char* Options::dotOutputFile()
{
return fDotOutputFile;
}
bool Options::hasExportRestrictList()
{
return (fExportMode != kExportDefault);
}
bool Options::hasExportMaskList()
{
return (fExportMode == kExportSome);
}
bool Options::hasWildCardExportRestrictList()
{
return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards());
}
bool Options::allGlobalsAreDeadStripRoots()
{
if ( fExportMode == kExportSome )
return false;
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
case Options::kPreload:
return false;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kKextBundle:
return true;
}
return false;
}
uint32_t Options::minimumHeaderPad()
{
return fMinimumHeaderPad;
}
std::vector<Options::ExtraSection>& Options::extraSections()
{
return fExtraSections;
}
std::vector<Options::SectionAlignment>& Options::sectionAlignments()
{
return fSectionAlignments;
}
Options::CommonsMode Options::commonsMode()
{
return fCommonsMode;
}
bool Options::warnCommons()
{
return fWarnCommons;
}
bool Options::keepRelocations()
{
return fKeepRelocations;
}
bool Options::warnStabs()
{
return fWarnStabs;
}
const char* Options::executablePath()
{
return fExecutablePath;
}
Options::DeadStripMode Options::deadStrip()
{
return fDeadStrip;
}
bool Options::hasExportedSymbolOrder()
{
return (fExportSymbolsOrder.size() > 0);
}
bool Options::exportedSymbolOrder(const char* sym, unsigned int* order)
{
NameToOrder::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;
}
}
bool Options::shouldExport(const char* symbolName)
{
switch (fExportMode) {
case kExportSome:
return fExportSymbols.contains(symbolName);
case kDontExportSome:
return ! fDontExportSymbols.contains(symbolName);
case kExportDefault:
return true;
}
throw "internal error";
}
bool Options::keepLocalSymbol(const char* symbolName)
{
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";
}
void Options::parseArch(const char* architecture)
{
if ( architecture == NULL )
throw "-arch must be followed by an architecture string";
if ( strcmp(architecture, "ppc") == 0 ) {
fArchitecture = CPU_TYPE_POWERPC;
fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL;
}
else if ( strcmp(architecture, "ppc64") == 0 ) {
fArchitecture = CPU_TYPE_POWERPC64;
fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL;
}
else if ( strcmp(architecture, "i386") == 0 ) {
fArchitecture = CPU_TYPE_I386;
fSubArchitecture = CPU_SUBTYPE_I386_ALL;
}
else if ( strcmp(architecture, "x86_64") == 0 ) {
fArchitecture = CPU_TYPE_X86_64;
fSubArchitecture = CPU_SUBTYPE_X86_64_ALL;
}
else if ( strcmp(architecture, "arm") == 0 ) {
fArchitecture = CPU_TYPE_ARM;
fSubArchitecture = CPU_SUBTYPE_ARM_ALL;
}
else if ( strcmp(architecture, "ppc750") == 0 ) {
fArchitecture = CPU_TYPE_POWERPC;
fSubArchitecture = CPU_SUBTYPE_POWERPC_750;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "ppc7400") == 0 ) {
fArchitecture = CPU_TYPE_POWERPC;
fSubArchitecture = CPU_SUBTYPE_POWERPC_7400;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "ppc7450") == 0 ) {
fArchitecture = CPU_TYPE_POWERPC;
fSubArchitecture = CPU_SUBTYPE_POWERPC_7450;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "ppc970") == 0 ) {
fArchitecture = CPU_TYPE_POWERPC;
fSubArchitecture = CPU_SUBTYPE_POWERPC_970;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "armv6") == 0 ) {
fArchitecture = CPU_TYPE_ARM;
fSubArchitecture = CPU_SUBTYPE_ARM_V6;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "armv5") == 0 ) {
fArchitecture = CPU_TYPE_ARM;
fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "armv4t") == 0 ) {
fArchitecture = CPU_TYPE_ARM;
fSubArchitecture = CPU_SUBTYPE_ARM_V4T;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "xscale") == 0 ) {
fArchitecture = CPU_TYPE_ARM;
fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE;
fHasPreferredSubType = true;
}
else if ( strcmp(architecture, "armv7") == 0 ) {
fArchitecture = CPU_TYPE_ARM;
fSubArchitecture = CPU_SUBTYPE_ARM_V7;
fHasPreferredSubType = true;
}
else
throwf("unknown/unsupported architecture name for: -arch %s", architecture);
}
bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result)
{
struct stat statBuffer;
char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8];
sprintf(possiblePath, format, dir, rootName);
bool found = (stat(possiblePath, &statBuffer) == 0);
if ( fTraceDylibSearching )
printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath);
if ( found ) {
result.path = strdup(possiblePath);
result.fileLen = statBuffer.st_size;
result.modTime = statBuffer.st_mtime;
return true;
}
return false;
}
Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly)
{
FileInfo result;
const int rootNameLen = strlen(rootName);
if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) {
for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( checkForFile("%s/%s", dir, rootName, result) )
return result;
}
}
else {
bool lookForDylibs = ( fOutputKind != Options::kDyld);
switch ( fLibrarySearchMode ) {
case kSearchAllDirsForDylibsThenAllDirsForArchives:
if ( lookForDylibs ) {
for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) )
return result;
}
for (std::vector<const char*>::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*>::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*>::iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, 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)
{
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)
{
struct stat statBuffer;
for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
it != fFrameworkSearchPaths.end();
it++) {
const char* dir = *it;
char possiblePath[PATH_MAX];
strcpy(possiblePath, dir);
strcat(possiblePath, "/");
strcat(possiblePath, rootName);
strcat(possiblePath, ".framework/");
strcat(possiblePath, rootName);
if ( suffix != NULL ) {
char realPath[PATH_MAX];
if ( realpath(possiblePath, realPath) != NULL ) {
strcpy(possiblePath, realPath);
strcat(possiblePath, suffix);
}
}
bool found = (stat(possiblePath, &statBuffer) == 0);
if ( fTraceDylibSearching )
printf("[Logging for XBS]%sfound framework: '%s'\n",
(found ? " " : " not "), possiblePath);
if ( found ) {
FileInfo result;
result.path = strdup(possiblePath);
result.fileLen = statBuffer.st_size;
result.modTime = statBuffer.st_mtime;
return result;
}
}
if ( suffix != NULL )
return findFramework(rootName, NULL);
else
throwf("framework not found %s", rootName);
}
Options::FileInfo Options::findFile(const char* path)
{
FileInfo result;
struct stat statBuffer;
if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) {
const int pathLen = strlen(path);
for (std::vector<const char*>::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) {
const char* sdkPathDir = *it;
const int sdkPathDirLen = strlen(sdkPathDir);
char possiblePath[sdkPathDirLen+pathLen+4];
strcpy(possiblePath, sdkPathDir);
if ( possiblePath[sdkPathDirLen-1] == '/' )
possiblePath[sdkPathDirLen-1] = '\0';
strcat(possiblePath, path);
if ( stat(possiblePath, &statBuffer) == 0 ) {
result.path = strdup(possiblePath);
result.fileLen = statBuffer.st_size;
result.modTime = statBuffer.st_mtime;
return result;
}
}
}
if ( stat(path, &statBuffer) == 0 ) {
result.path = strdup(path);
result.fileLen = statBuffer.st_size;
result.modTime = statBuffer.st_mtime;
return result;
}
if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) {
char newPath[strlen(fExecutablePath) + strlen(path)];
strcpy(newPath, fExecutablePath);
char* addPoint = strrchr(newPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[17]);
else
strcpy(newPath, &path[17]);
if ( stat(newPath, &statBuffer) == 0 ) {
result.path = strdup(newPath);
result.fileLen = statBuffer.st_size;
result.modTime = statBuffer.st_mtime;
return result;
}
}
throwf("file not found: %s", path);
}
Options::FileInfo Options::findFileUsingPaths(const char* path)
{
FileInfo result;
const char* lastSlash = strrchr(path, '/');
const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1];
bool isFramework = false;
if ( lastSlash != NULL ) {
char frameworkDir[strlen(leafName) + 20];
strcpy(frameworkDir, "/");
strcat(frameworkDir, leafName);
strcat(frameworkDir, ".framework/");
if ( strstr(path, frameworkDir) != NULL )
isFramework = true;
}
if ( isFramework ) {
for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin();
it != fFrameworkSearchPaths.end();
it++) {
const char* dir = *it;
char possiblePath[PATH_MAX];
strcpy(possiblePath, dir);
strcat(possiblePath, "/");
strcat(possiblePath, leafName);
strcat(possiblePath, ".framework");
if ( checkForFile("%s/%s", possiblePath, leafName, result) )
return result;
}
}
else {
int leafLen = strlen(leafName);
bool embeddedDylib = ( (leafLen > 6)
&& (strcmp(&leafName[leafLen-6], ".dylib") == 0)
&& (strstr(path, ".framework/") != NULL) );
if ( !embeddedDylib ) {
for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin();
it != fLibrarySearchPaths.end();
it++) {
const char* dir = *it;
if ( checkForFile("%s/%s", dir, leafName, result) )
return result;
}
}
}
return findFile(path);
}
void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath)
{
FILE* file = fopen(segAddrPath, "r");
if ( file == NULL ) {
warning("-seg_addr_table file cannot be read: %s", segAddrPath);
return;
}
char path[PATH_MAX];
uint64_t firstColumAddress = 0;
uint64_t secondColumAddress = 0;
bool hasSecondColumn = false;
while ( fgets(path, PATH_MAX, file) != NULL ) {
path[PATH_MAX-1] = '\0';
char* eol = strchr(path, '\n');
if ( eol != NULL )
*eol = '\0';
if ( (path[0] == '0') && (path[1] == 'x') ) {
char* p;
firstColumAddress = strtoull(path, &p, 16);
while ( isspace(*p) )
++p;
if ( (p[0] == '0') && (p[1] == 'x') ) {
secondColumAddress = strtoull(p, &p, 16);
hasSecondColumn = true;
while ( isspace(*p) )
++p;
}
while ( isspace(*p) )
++p;
if ( p[0] == '/' ) {
for(char* end = eol-1; (end > p) && isspace(*end); --end)
*end = '\0';
if ( strcmp(p, installPath) == 0 ) {
fBaseAddress = firstColumAddress;
if ( hasSecondColumn ) {
fBaseWritableAddress = secondColumAddress;
fSplitSegs = true;
}
break; }
}
}
}
fclose(file);
}
void Options::loadFileList(const char* fileOfPaths)
{
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 not found: %s\n", realFileOfPaths);
}
}
else {
file = fopen(fileOfPaths, "r");
if ( file == NULL )
throwf("-filelist file not found: %s\n", fileOfPaths);
}
char path[PATH_MAX];
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);
fInputFiles.push_back(findFile(builtPath));
}
else {
fInputFiles.push_back(findFile(path));
}
}
fclose(file);
}
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)
{
if ( fRegular.find(symbol) != fRegular.end() )
return true;
for(std::vector<const char*>::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) {
if ( wildCardMatch(*it, symbol) )
return true;
}
return false;
}
bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c)
{
++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 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)
{
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);
::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);
ObjectFile::ReaderOptions::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;
}
fReaderOptions.fAliases.push_back(pair);
state = inComment;
}
else if ( *s == '\n' ) {
*s = '\0';
char* last = s-1;
while ( isspace(*last) ) {
*last = '\0';
--last;
}
fReaderOptions.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::setMacOSXVersionMin(const char* version)
{
if ( version == NULL )
throw "-macosx_version_min argument missing";
if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) {
int num = version[3] - '0';
switch ( num ) {
case 0:
case 1:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_1;
break;
case 2:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_2;
break;
case 3:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_3;
break;
case 4:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
break;
case 5:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_5;
break;
case 6:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6;
break;
default:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6;
break;
}
}
else {
warning("unknown option to -macosx_version_min, not 10.x");
}
}
void Options::setIPhoneVersionMin(const char* version)
{
if ( version == NULL )
throw "-iphoneos_version_min argument missing";
if ( ! isdigit(version[0]) )
throw "-iphoneos_version_min argument is not a number";
if ( version[1] != '.' )
throw "-iphoneos_version_min argument is missing period as second character";
if ( ! isdigit(version[2]) )
throw "-iphoneos_version_min argument is not a number";
if ( version[0] == '2' )
fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
else if ( (version[0] == '3') && (version[2] == '0') )
fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
else if ( (version[0] == '3') && (version[2] >= '1') )
fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1;
else if ( (version[0] >= '4') )
fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1;
else {
fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
warning("unknown option to -iphoneos_version_min, not 2.x, 3.x, or 4.x");
}
}
bool Options::minOS(ObjectFile::ReaderOptions::MacVersionMin requiredMacMin, ObjectFile::ReaderOptions::IPhoneVersionMin requirediPhoneOSMin)
{
if ( fReaderOptions.fMacVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) {
return ( fReaderOptions.fMacVersionMin >= requiredMacMin );
}
else {
return ( fReaderOptions.fIPhoneVersionMin >= requirediPhoneOSMin);
}
}
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;
}
uint32_t Options::parseVersionNumber(const char* versionString)
{
unsigned long x = 0;
unsigned long y = 0;
unsigned long 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 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)
{
fReaderOptions.fAutoOrderInitializers = false;
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';
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 ) {
if ( fArchitecture == CPU_TYPE_POWERPC )
symbolStart = &symbolStart[4];
else
symbolStart = NULL;
}
else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) {
if ( fArchitecture == CPU_TYPE_POWERPC64 )
symbolStart = &symbolStart[6];
else
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;
}
if ( symbolStart != NULL ) {
char* objFileName = NULL;
char* colon = strstr(symbolStart, ".o:");
if ( colon != NULL ) {
colon[2] = '\0';
objFileName = symbolStart;
symbolStart = &colon[3];
}
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, stat_buf.st_size };
fExtraSections.push_back(info);
}
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::parse(int argc, const char* argv[])
{
this->buildSearchPaths(argc, argv);
fInputFiles.reserve(32);
for(int i=1; i < argc; ++i) {
const char* arg = argv[i];
if ( arg[0] == '-' ) {
if (fPrintOptions)
fprintf (stderr, "[Logging ld64 options]\t%s\n", arg);
if ( (arg[1] == 'L') || (arg[1] == 'F') ) {
}
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 ) {
parseArch(argv[++i]);
}
else if ( strcmp(arg, "-dynamic") == 0 ) {
}
else if ( strcmp(arg, "-static") == 0 ) {
fReaderOptions.fForStatic = true;
if ( fOutputKind != kObjectFile ) {
fOutputKind = kStaticExecutable;
}
}
else if ( strcmp(arg, "-dylib") == 0 ) {
fOutputKind = kDynamicLibrary;
}
else if ( strcmp(arg, "-bundle") == 0 ) {
fOutputKind = kDynamicBundle;
}
else if ( strcmp(arg, "-dylinker") == 0 ) {
fOutputKind = kDyld;
}
else if ( strcmp(arg, "-execute") == 0 ) {
if ( fOutputKind != kStaticExecutable )
fOutputKind = kDynamicExecutable;
}
else if ( strcmp(arg, "-preload") == 0 ) {
fOutputKind = kPreload;
}
else if ( strcmp(arg, "-r") == 0 ) {
fOutputKind = kObjectFile;
}
else if ( strcmp(arg, "-kext") == 0 ) {
fOutputKind = kKextBundle;
}
else if ( strcmp(arg, "-o") == 0 ) {
fOutputFile = argv[++i];
}
else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) {
addLibrary(findLibrary(&arg[2]));
}
else if ( strncmp(arg, "-weak-l", 7) == 0 ) {
FileInfo info = findLibrary(&arg[7]);
info.options.fWeakImport = true;
addLibrary(info);
}
else if ( strncmp(arg, "-lazy-l", 7) == 0 ) {
FileInfo info = findLibrary(&arg[7], true);
info.options.fLazyLoad = true;
addLibrary(info);
fUsingLazyDylibLinking = true;
}
else if ( strcmp(arg, "-bind_at_load") == 0 ) {
fBindAtLoad = true;
}
else if ( strcmp(arg, "-twolevel_namespace") == 0 ) {
fNameSpace = kTwoLevelNameSpace;
}
else if ( strcmp(arg, "-flat_namespace") == 0 ) {
fNameSpace = kFlatNameSpace;
}
else if ( strcmp(arg, "-force_flat_namespace") == 0 ) {
fNameSpace = kForceFlatNameSpace;
}
else if ( strcmp(arg, "-all_load") == 0 ) {
fReaderOptions.fFullyLoadArchives = true;
}
else if ( strcmp(arg, "-noall_load") == 0) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-ObjC") == 0 ) {
fReaderOptions.fLoadAllObjcObjectsFromArchives = true;
}
else if ( strcmp(arg, "-force_load") == 0 ) {
FileInfo info = findFile(argv[++i]);
info.options.fForceLoad = true;
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 = parseVersionNumber(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 = parseVersionNumber(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>";
parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
}
else if ( strcmp(arg, "-order_file") == 0 ) {
parseOrderFile(argv[++i], false);
}
else if ( strcmp(arg, "-order_file_statistics") == 0 ) {
fPrintOrderFileStatistics = true;
}
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>";
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;
}
}
else if ( strcmp(arg, "-e") == 0 ) {
fEntryName = argv[++i];
}
else if ( strcmp(arg, "-filelist") == 0 ) {
const char* path = argv[++i];
if ( (path == NULL) || (path[0] == '-') )
throw "-filelist missing <path>";
loadFileList(path);
}
else if ( strcmp(arg, "-keep_private_externs") == 0 ) {
fKeepPrivateExterns = true;
}
else if ( strcmp(arg, "-final_output") == 0 ) {
fFinalName = argv[++i];
}
else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) {
switch ( fInterposeMode ) {
case kInterposeNone:
case kInterposeAllExternal:
fInterposeMode = kInterposeAllExternal;
break;
case kInterposeSome:
break;
}
}
else if ( strcmp(arg, "-interposable_list") == 0 ) {
fInterposeMode = kInterposeSome;
loadExportFile(argv[++i], "-interposable_list", fInterposeList);
}
else if ( strcmp(arg, "-single_module") == 0 ) {
fInterposeMode = kInterposeNone;
}
else if ( strcmp(arg, "-exported_symbols_list") == 0 ) {
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 ) {
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;
fExportSymbols.insert(argv[++i]);
}
else if ( strcmp(arg, "-unexported_symbol") == 0 ) {
if ( fExportMode == kExportSome )
throw "can't use -unexported_symbol and -exported_symbol";
fExportMode = kDontExportSome;
fDontExportSymbols.insert(argv[++i]);
}
else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) {
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);
}
else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) {
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);
}
else if ( strcmp(arg, "-no_arch_warnings") == 0 ) {
fIgnoreOtherArchFiles = true;
}
else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) {
fForceSubtypeAll = true;
}
else if ( strcmp(arg, "-weak_library") == 0 ) {
FileInfo info = findFile(argv[++i]);
info.options.fWeakImport = true;
addLibrary(info);
}
else if ( strcmp(arg, "-lazy_library") == 0 ) {
FileInfo info = findFile(argv[++i]);
info.options.fLazyLoad = true;
addLibrary(info);
fUsingLazyDylibLinking = true;
}
else if ( strcmp(arg, "-framework") == 0 ) {
addLibrary(findFramework(argv[++i]));
}
else if ( strcmp(arg, "-weak_framework") == 0 ) {
FileInfo info = findFramework(argv[++i]);
info.options.fWeakImport = true;
addLibrary(info);
}
else if ( strcmp(arg, "-lazy_framework") == 0 ) {
FileInfo info = findFramework(argv[++i]);
info.options.fLazyLoad = true;
addLibrary(info);
fUsingLazyDylibLinking = true;
}
else if ( strcmp(arg, "-search_paths_first") == 0 ) {
}
else if ( strcmp(arg, "-undefined") == 0 ) {
setUndefinedTreatment(argv[++i]);
}
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;
break;
case kSuppress:
fWarnTextRelocs = false;
fAllowTextRelocs = true;
break;
case kError:
fWarnTextRelocs = false;
fAllowTextRelocs = false;
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 ) {
fPrebind = true;
}
else if ( strcmp(arg, "-noprebind") == 0 ) {
warnObsolete(arg);
fPrebind = false;
}
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 ) {
addDylibOverride(argv[++i]);
}
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;
}
}
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 & (-4096); if ( (seg.address != temp) )
warning("-segaddr %s not page aligned, rounding down", seg.name);
fCustomSegmentAddresses.push_back(seg);
}
else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) {
fBaseAddress = parseAddress(argv[++i]);
}
else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) {
fBaseWritableAddress = parseAddress(argv[++i]);
fSplitSegs = true;
}
else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-seg_addr_table missing argument";
fSegAddrTablePath = name;
}
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]);
fCustomSegmentProtections.push_back(seg);
}
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;
}
else if ( strcmp(arg, "-stack_addr") == 0 ) {
const char* address = argv[++i];
if ( address == NULL )
throw "-stack_addr missing <address>";
fStackAddr = parseAddress(address);
}
else if ( strcmp(arg, "-stack_size") == 0 ) {
const char* size = argv[++i];
if ( size == NULL )
throw "-stack_size missing <address>";
fStackSize = parseAddress(size);
uint64_t temp = fStackSize & (-4096); if ( (fStackSize != temp) )
warning("-stack_size not page aligned, rounding down");
}
else if ( strcmp(arg, "-allow_stack_execute") == 0 ) {
fExecutableStack = true;
}
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;
}
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 ) {
fBundleLoader = argv[++i];
if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') )
throw "-bundle_loader missing <path>";
FileInfo info = findFile(fBundleLoader);
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 ) {
}
else if ( strcmp(arg, "-macosx_version_min") == 0 ) {
setMacOSXVersionMin(argv[++i]);
}
else if ( strcmp(arg, "-iphoneos_version_min") == 0 ) {
setIPhoneVersionMin(argv[++i]);
}
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) ) {
fReaderOptions.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);
}
else if ( strcmp(arg, "-U") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-U missing argument";
fAllowedUndefined.insert(name);
}
else if ( strcmp(arg, "-s") == 0 ) {
warnObsolete(arg);
fLocalSymbolHandling = kLocalSymbolsNone;
fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
}
else if ( strcmp(arg, "-x") == 0 ) {
fLocalSymbolHandling = kLocalSymbolsNone;
}
else if ( strcmp(arg, "-S") == 0 ) {
fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
}
else if ( strcmp(arg, "-X") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-Si") == 0 ) {
warnObsolete(arg);
fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull;
}
else if ( strcmp(arg, "-b") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-Sn") == 0 ) {
warnObsolete(arg);
fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull;
}
else if ( strcmp(arg, "-Sp") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-dead_strip") == 0 ) {
fDeadStrip = kDeadStripOnPlusUnusedInits;
}
else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) {
fDeadStrip = kDeadStripOn;
}
else if ( strcmp(arg, "-w") == 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);
}
else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) {
fMaxMinimumHeaderPad = true;
}
else if ( strcmp(arg, "-t") == 0 ) {
fReaderOptions.fLogAllFiles = true;
}
else if ( strcmp(arg, "-whatsloaded") == 0 ) {
fReaderOptions.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;
}
else if ( strcmp(arg, "-allowable_client") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-allowable_client missing argument";
fAllowableClients.push_back(name);
}
else if ( strcmp(arg, "-client_name") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-client_name missing argument";
fClientName = name;
}
else if ( strcmp(arg, "-sub_umbrella") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-sub_umbrella missing argument";
fSubUmbellas.push_back(name);
}
else if ( strcmp(arg, "-sub_library") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-sub_library missing argument";
fSubLibraries.push_back(name);
}
else if ( strcmp(arg, "-init") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-init missing argument";
fInitFunctionName = name;
}
else if ( strcmp(arg, "-dot") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-dot missing argument";
fDotOutputFile = name;
}
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 ) {
fReaderOptions.fMakeTentativeDefinitionsReal = true;
}
else if ( strcmp(arg, "-v") == 0 ) {
}
else if ( strcmp(arg, "-Z") == 0 ) {
}
else if ( strcmp(arg, "-syslibroot") == 0 ) {
++i;
}
else if ( strcmp(arg, "-no_uuid") == 0 ) {
fUUIDMode = kUUIDNone;
}
else if ( strcmp(arg, "-random_uuid") == 0 ) {
fUUIDMode = kUUIDRandom;
}
else if ( strcmp(arg, "-dtrace") == 0 ) {
const char* name = argv[++i];
if ( name == NULL )
throw "-dtrace missing argument";
fDtraceScriptName = name;
}
else if ( strcmp(arg, "-root_safe") == 0 ) {
fReaderOptions.fRootSafe = true;
}
else if ( strcmp(arg, "-setuid_safe") == 0 ) {
fReaderOptions.fSetuidSafe = true;
}
else if ( strcmp(arg, "-alias") == 0 ) {
ObjectFile::ReaderOptions::AliasPair pair;
pair.realName = argv[++i];
if ( pair.realName == NULL )
throw "missing argument to -alias";
pair.alias = argv[++i];
if ( pair.alias == NULL )
throw "missing argument to -alias";
fReaderOptions.fAliases.push_back(pair);
}
else if ( strcmp(arg, "-alias_list") == 0 ) {
parseAliasFile(argv[++i]);
}
else if ( strncmp(arg, "-i", 2) == 0 ) {
const char* colon = strchr(arg, ':');
if ( colon == NULL )
throwf("unknown option: %s", arg);
ObjectFile::ReaderOptions::AliasPair pair;
char* temp = new char[colon-arg];
strlcpy(temp, &arg[2], colon-arg-1);
pair.realName = &colon[1];
pair.alias = temp;
fReaderOptions.fAliases.push_back(pair);
}
else if ( strcmp(arg, "-save-temps") == 0 ) {
fSaveTempFiles = true;
}
else if ( strcmp(arg, "-rpath") == 0 ) {
const char* path = argv[++i];
if ( path == NULL )
throw "missing argument to -rpath";
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 = argv[++i];
if ( fMapPath == NULL )
throw "missing argument to -map";
}
else if ( strcmp(arg, "-pie") == 0 ) {
fPositionIndependentExecutable = true;
}
else if ( strncmp(arg, "-reexport-l", 11) == 0 ) {
FileInfo info = findLibrary(&arg[11], true);
info.options.fReExport = true;
addLibrary(info);
}
else if ( strcmp(arg, "-reexport_library") == 0 ) {
FileInfo info = findFile(argv[++i]);
info.options.fReExport = true;
addLibrary(info);
}
else if ( strcmp(arg, "-reexport_framework") == 0 ) {
FileInfo info = findFramework(argv[++i]);
info.options.fReExport = true;
addLibrary(info);
}
else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) {
fDeadStripDylibs = true;
}
else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) {
fReaderOptions.fImplicitlyLinkPublicDylibs = false;
}
else if ( strcmp(arg, "-new_linker") == 0 ) {
}
else if ( strcmp(arg, "-no_encryption") == 0 ) {
fEncryptable = false;
}
else if ( strcmp(arg, "-no_compact_unwind") == 0 ) {
fReaderOptions.fAddCompactUnwindEncoding = false;
}
else if ( strcmp(arg, "-mllvm") == 0 ) {
const char* opts = argv[++i];
if ( opts == NULL )
throw "missing argument to -mllvm";
fLLVMOptions.push_back(opts);
}
else if ( strcmp(arg, "-no_order_inits") == 0 ) {
fReaderOptions.fAutoOrderInitializers = false;
}
else if ( strcmp(arg, "-no_order_data") == 0 ) {
fOrderData = false;
}
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);
}
else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) {
fMarkDeadStrippableDylib = true;
}
else if ( strcmp(arg, "-exported_symbols_order") == 0 ) {
loadSymbolOrderFile(argv[++i], fExportSymbolsOrder);
}
else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) {
fMakeCompressedDyldInfo = false;
}
else if ( strcmp(arg, "-no_eh_labels") == 0 ) {
fReaderOptions.fNoEHLabels = true;
}
else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) {
fReaderOptions.fWarnCompactUnwind = true;
}
else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) {
fAllowCpuSubtypeMismatches = true;
}
else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) {
fReaderOptions.fOptimizeZeroFill = false;
}
else {
throwf("unknown option: %s", arg);
}
}
else {
FileInfo info = findFile(arg);
if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 )
addLibrary(info);
else
fInputFiles.push_back(info);
}
}
if ( fUsingLazyDylibLinking ) {
addLibrary(findLibrary("lazydylib1.o"));
}
}
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') )
libraryPaths.push_back(&argv[i][2]);
else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') )
frameworkPaths.push_back(&argv[i][2]);
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);
if ( argc == 2 ) {
#if LTO_SUPPORT
printLTOVersion(*this);
#endif
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], "-w") == 0 ) {
sEmitWarnings = false;
}
}
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 ) {
fLibrarySearchPaths.push_back(strdup(newPath));
sdkOverride = true;
}
}
}
if ( !sdkOverride ) {
if ( (libIndex >= standardLibraryPathsStartIndex) && (fSDKPaths.size() == 1) ) {
}
else {
fLibrarySearchPaths.push_back(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 ) {
fFrameworkSearchPaths.push_back(strdup(newPath));
sdkOverride = true;
}
}
}
if ( !sdkOverride ) {
if ( (frameIndex >= standardFrameworkPathsStartIndex) && (fSDKPaths.size() == 1) ) {
}
else {
fFrameworkSearchPaths.push_back(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_TRACE_ARCHIVES") != NULL)
|| (getenv("RC_TRACE_ARCHIVES") != NULL))
fReaderOptions.fTraceArchives = true;
if ((getenv("LD_TRACE_DYLIBS") != NULL)
|| (getenv("RC_TRACE_DYLIBS") != NULL)) {
fReaderOptions.fTraceDylibs = true;
fReaderOptions.fTraceIndirectDylibs = true;
}
if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) {
fTraceDylibSearching = true;
}
if (getenv("LD_PRINT_OPTIONS") != NULL)
fPrintOptions = true;
if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives)
fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE");
if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL)
fPrintOrderFileStatistics = true;
if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL)
fSplitSegs = true;
if (getenv("LD_NO_ENCRYPT") != NULL)
fEncryptable = false;
if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL)
fAllowCpuSubtypeMismatches = true;
if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) {
fMakeCompressedDyldInfo = false;
fMakeClassicDyldInfo = true;
}
sWarningsSideFilePath = getenv("LD_WARN_FILE");
}
void Options::parsePostCommandLineEnvironmentSettings()
{
if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) {
fExecutablePath = fOutputFile;
}
if ( fSegAddrTablePath == NULL )
fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE");
if ( !fPrebind ) {
fPrebind = ( getenv("LD_PREBIND") != NULL );
}
if ( fDeadStrip == kDeadStripOff ) {
if ( getenv("LD_DEAD_STRIP") != NULL ) {
switch (fOutputKind) {
case Options::kDynamicLibrary:
case Options::kDynamicExecutable:
case Options::kDynamicBundle:
fDeadStrip = kDeadStripOn;
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;
}
void Options::reconfigureDefaults()
{
switch ( fOutputKind ) {
case Options::kObjectFile:
fReaderOptions.fForFinalLinkedImage = false;
break;
case Options::kDyld:
fReaderOptions.fForDyld = true;
fReaderOptions.fForFinalLinkedImage = true;
fReaderOptions.fNoEHLabels = true;
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kKextBundle:
fReaderOptions.fForFinalLinkedImage = true;
fReaderOptions.fNoEHLabels = true;
break;
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
case Options::kPreload:
fReaderOptions.fLinkingMainExecutable = true;
fReaderOptions.fForFinalLinkedImage = true;
fReaderOptions.fNoEHLabels = true;
break;
}
if ( (fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::kMinMacVersionUnset)
&& (fReaderOptions.fIPhoneVersionMin == ObjectFile::ReaderOptions::kMinIPhoneVersionUnset) ) {
const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET");
const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET");
if ( macVers != NULL )
setMacOSXVersionMin(macVers);
else if ( iPhoneVers != NULL )
setIPhoneVersionMin(iPhoneVers);
else {
switch ( fArchitecture ) {
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
case CPU_TYPE_POWERPC:
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; break;
case CPU_TYPE_ARM:
fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0;
break;
}
}
}
switch ( fArchitecture ) {
case CPU_TYPE_I386:
if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
}
break;
case CPU_TYPE_POWERPC64:
if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
}
break;
case CPU_TYPE_X86_64:
if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) {
fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4;
}
break;
}
if ( fOutputKind == kKextBundle ) {
switch ( fArchitecture ) {
case CPU_TYPE_X86_64:
fMakeClassicDyldInfo = true;
fMakeCompressedDyldInfo = false;
fAllowTextRelocs = true;
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
case CPU_TYPE_POWERPC:
case CPU_TYPE_I386:
case CPU_TYPE_ARM:
fOutputKind = kObjectFile;
break;
}
}
if ( !minOS(ObjectFile::ReaderOptions::k10_4, ObjectFile::ReaderOptions::k2_0) )
fReaderOptions.fImplicitlyLinkPublicDylibs = false;
if ( getenv("LD_FORCE_NO_PREBIND") != NULL )
fPrebind = false;
if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL )
fSegAddrTablePath = NULL;
if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) {
parseSegAddrTable(fSegAddrTablePath, this->installPath());
if ( fBaseAddress == 0 ) {
if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) {
parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib");
if ( fBaseAddress == 0 )
parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.9.dylib");
}
else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 )
parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib");
else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 )
parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib");
}
}
if ( fSplitSegs ) {
switch ( fArchitecture ) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_I386:
if ( fOutputKind != Options::kDynamicLibrary )
fSplitSegs = false;
if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) )
fBaseWritableAddress = fBaseAddress + 0x10000000;
break;
case CPU_TYPE_ARM:
if ( fOutputKind != Options::kDynamicLibrary ) {
fSplitSegs = false;
}
else {
if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) )
fBaseWritableAddress = fBaseAddress + 0x08000000;
}
break;
default:
fSplitSegs = false;
fBaseAddress = 0;
fBaseWritableAddress = 0;
}
}
if ( fOutputKind == Options::kObjectFile )
fPrebind = false;
if ( fPrebind ) {
switch ( fArchitecture ) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_I386:
if ( fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::k10_4 ) {
if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs )
fPrebind = false;
}
else if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) {
fPrebind = false;
}
else {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
break;
case Options::kStaticExecutable:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
fPrebind = false;
break;
}
}
break;
case CPU_TYPE_POWERPC64:
case CPU_TYPE_X86_64:
fPrebind = false;
break;
case CPU_TYPE_ARM:
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
break;
case Options::kStaticExecutable:
case Options::kDynamicBundle:
case Options::kObjectFile:
case Options::kDyld:
case Options::kPreload:
case Options::kKextBundle:
fPrebind = false;
break;
}
break;
}
}
if ( fSplitSegs && !fPrebind )
fSplitSegs = false;
if ( fOutputKind == Options::kDynamicLibrary ) {
if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_1) )
if ( !fPrebind )
if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0)
|| (strncmp(this->installPath(), "/System/Library/", 16) == 0) )
fSharedRegionEligible = true;
}
if ( fOutputKind == Options::kDynamicLibrary ) {
switch ( fArchitecture ) {
case CPU_TYPE_POWERPC: case CPU_TYPE_I386: if ( fReaderOptions.fMacVersionMin <= ObjectFile::ReaderOptions::k10_5 )
fNeedsModuleTable = true;
break;
case CPU_TYPE_ARM:
if ( fPrebind )
fNeedsModuleTable = true; break;
}
}
if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) )
fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone;
switch ( fArchitecture ) {
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
switch ( fOutputKind ) {
case Options::kObjectFile:
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
fReaderOptions.fAddCompactUnwindEncoding = false;
break;
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDynamicExecutable:
break;
}
break;
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
case CPU_TYPE_ARM:
fReaderOptions.fAddCompactUnwindEncoding = false;
fReaderOptions.fRemoveDwarfUnwindIfCompactExists = false;
break;
case 0:
fReaderOptions.fAddCompactUnwindEncoding = false;
break;
}
if ( fOutputKind != Options::kDynamicExecutable )
fEncryptable = false;
if ( fArchitecture != CPU_TYPE_ARM )
fEncryptable = false;
if ( fOutputKind == Options::kDyld )
fReaderOptions.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;
}
if ( fMakeCompressedDyldInfo ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_6 )
fMakeClassicDyldInfo = false;
else if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_5 )
fMakeCompressedDyldInfo = false;
break;
case CPU_TYPE_ARM:
if ( fReaderOptions.fIPhoneVersionMin >= ObjectFile::ReaderOptions::k3_1 )
fMakeClassicDyldInfo = false;
else if ( fReaderOptions.fIPhoneVersionMin < ObjectFile::ReaderOptions::k3_1 )
fMakeCompressedDyldInfo = false;
break;
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
default:
fMakeCompressedDyldInfo = false;
}
}
if ( fMakeCompressedDyldInfo ) {
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:
fMakeCompressedDyldInfo = false;
break;
}
}
fReaderOptions.fMakeCompressedDyldInfo = fMakeCompressedDyldInfo;
if ( fArchitecture != CPU_TYPE_ARM )
fAllowCpuSubtypeMismatches = true;
if ( fOutputKind == Options::kObjectFile )
fReaderOptions.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 ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) )
fUseSimplifiedDylibReExports = true;
}
void Options::checkIllegalOptionCombinations()
{
switch ( fUndefinedTreatment ) {
case kUndefinedError:
case kUndefinedDynamicLookup:
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;
if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) {
info.options.fReExport = true;
found = true;
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;
break;
}
}
if ( ! found )
warning("-sub_library %s does not match a supplied dylib", subLibrary);
}
if ( fNameSpace != kTwoLevelNameSpace )
fReaderOptions.fFlatNamespace = true;
if ( fStackAddr != 0 ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
case CPU_TYPE_POWERPC:
case CPU_TYPE_ARM:
if ( fStackAddr > 0xFFFFFFFF )
throw "-stack_addr must be < 4G for 32-bit processes";
break;
case CPU_TYPE_POWERPC64:
case CPU_TYPE_X86_64:
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:
case CPU_TYPE_POWERPC:
if ( fStackSize > 0xFFFFFFFF )
throw "-stack_size must be < 4G 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");
break;
case CPU_TYPE_ARM:
if ( fStackSize > 0x2F000000 )
throw "-stack_size must be < 752MB";
if ( fStackAddr == 0 )
fStackAddr = 0x2F000000;
if ( fStackAddr > 0x30000000)
throw "-stack_addr must be < 0x30000000 for arm";
case CPU_TYPE_POWERPC64:
case CPU_TYPE_X86_64:
if ( fStackAddr == 0 ) {
fStackAddr = 0x00007FFF5C000000LL;
}
break;
}
if ( (fStackSize & -4096) != fStackSize )
throw "-stack_size must be multiples of 4K";
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 ( 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 ( 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 ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) )
throw "-d can only be used with -r";
if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) )
throw "-root_safe cannot be used with -r";
if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) )
throw "-setuid_safe cannot be used with -r";
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
fInitialUndefines.push_back(name);
if ( strncmp(name, ".objc_class_name_", 17) == 0 ) {
switch (fArchitecture) {
case CPU_TYPE_POWERPC64:
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM:
char* temp;
asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]);
impliedExports.push_back(temp);
asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]);
impliedExports.push_back(temp);
break;
}
}
}
for (std::vector<const char*>::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) {
const char* name = *it;
fExportSymbols.insert(name);
fInitialUndefines.push_back(name);
}
if ( fInitFunctionName != NULL )
fInitialUndefines.push_back(fInitFunctionName);
if ( fCustomSegmentAddresses.size() != 0 ) {
if ( fZeroPageSize != ULLONG_MAX ) {
for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) {
if ( (it->address >= 0) && (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_POWERPC:
case CPU_TYPE_ARM:
fZeroPageSize = 0x1000;
break;
case CPU_TYPE_POWERPC64:
if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 )
fZeroPageSize = 0x100000000ULL;
else
fZeroPageSize = 0x1000; break;
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 ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) )
throw "-r and -dead_strip cannot be used together";
if ( fRPaths.size() > 0 ) {
if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) )
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:
case Options::kPreload:
break;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
warning("-pie being ignored. It is only used when linking a main executable");
break;
case Options::kStaticExecutable:
case Options::kObjectFile:
case Options::kDyld:
case Options::kKextBundle:
throw "-pie can only be used when linking a main executable";
}
if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) )
throw "-pie can only be used when targeting Mac OS X 10.5 or later";
}
if ( fAllowTextRelocs ) {
if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind != kKextBundle) ) {
warning("-read_only_relocs cannot be used with x86_64");
fAllowTextRelocs = false;
}
}
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");
}
}
}
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;
for(int i=1; i < argc; ++i) {
strlcat(crashreporterBuffer, argv[i], 1000);
strlcat(crashreporterBuffer, " ", 1000);
}
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];
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;
}
}
}
if( dtraceFound )
return;
if( archFound ) {
switch ( fArchitecture ) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_I386:
case CPU_TYPE_ARM:
if ( (staticFound || kextFound) && !newLinker ) {
if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) {
for(int j=0; j < argc; ++j) {
if ( strcmp(argv[j], "-iphoneos_version_min") == 0) {
argv[j] = "-macosx_version_min";
if ( j < argc-1 )
argv[j+1] = "10.5";
break;
}
}
if ( kextFound ) {
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";
}
}
this->gotoClassicLinker(argc, argv);
}
}
break;
}
}
else {
if ( staticFound )
this->gotoClassicLinker(argc, argv);
}
}
void Options::gotoClassicLinker(int argc, const char* argv[])
{
argv[0] = "ld_classic";
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);
}