From 6e880c60815f85472e1ce645f79cc12fae3a8c1d Mon Sep 17 00:00:00 2001 From: Apple Date: Thu, 26 May 2005 21:37:27 +0000 Subject: [PATCH] ld64-26.0.80.tar.gz --- ld64.xcode/project.pbxproj | 18 ++ src/MachOAbstraction.h | 37 ++- src/ObjectFile.h | 26 +- src/Options.cpp | 181 +++++++++-- src/Options.h | 7 +- src/Readers/ObjectFileArchiveMachO.cpp | 29 +- src/Readers/ObjectFileDylibMachO.cpp | 3 +- src/Readers/ObjectFileMachO.cpp | 257 ++++++++++----- src/Writers/ExecutableFileMachO.cpp | 423 ++++++++++++++++++++----- src/ld.cpp | 207 ++++++------ 10 files changed, 886 insertions(+), 302 deletions(-) diff --git a/ld64.xcode/project.pbxproj b/ld64.xcode/project.pbxproj index ef5a16d..1831cbe 100644 --- a/ld64.xcode/project.pbxproj +++ b/ld64.xcode/project.pbxproj @@ -83,6 +83,8 @@ F97F5025070D0B6300B9FCD7, ); buildRules = ( + F9E8D4BE07FCAF2A00FD5801, + F9E8D4BD07FCAF2000FD5801, ); buildSettings = { CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -418,6 +420,22 @@ settings = { }; }; + F9E8D4BD07FCAF2000FD5801 = { + compilerSpec = com.apple.compilers.gcc.4_0; + fileType = sourcecode.c; + isEditable = 1; + isa = PBXBuildRule; + outputFiles = ( + ); + }; + F9E8D4BE07FCAF2A00FD5801 = { + compilerSpec = com.apple.compilers.gcc.4_0; + fileType = sourcecode.cpp; + isEditable = 1; + isa = PBXBuildRule; + outputFiles = ( + ); + }; }; rootObject = F9023C3006D5A227001BBF46; } diff --git a/src/MachOAbstraction.h b/src/MachOAbstraction.h index 211a189..fe4b01e 100644 --- a/src/MachOAbstraction.h +++ b/src/MachOAbstraction.h @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -1041,22 +1042,46 @@ void macho_routines_command::set_cmdsize(uint32_t _value) { inline __attribute__((always_inline)) uint64_t macho_routines_command::init_address() const { +#if defined(ARCH_PPC64) return ENDIAN_SWAP64(content.init_address); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.init_address); +#else + #error unknown architecture +#endif } inline __attribute__((always_inline)) void macho_routines_command::set_init_address(uint64_t _value) { +#if defined(ARCH_PPC64) content.init_address = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.init_address, _value); +#else + #error unknown architecture +#endif } inline __attribute__((always_inline)) uint64_t macho_routines_command::init_module() const { +#if defined(ARCH_PPC64) return ENDIAN_SWAP64(content.init_module); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + return ENDIAN_READ32(content.init_module); +#else + #error unknown architecture +#endif } inline __attribute__((always_inline)) void macho_routines_command::set_init_module(uint64_t _value) { +#if defined(ARCH_PPC64) content.init_module = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.init_module, _value); +#else + #error unknown architecture +#endif } @@ -1670,7 +1695,13 @@ uint64_t macho_nlist::n_value() const { inline __attribute__((always_inline)) void macho_nlist::set_n_value(uint64_t _value) { +#if defined(ARCH_PPC64) content.n_value = ENDIAN_SWAP64(_value); +#elif defined(ARCH_PPC) || defined(ARCH_I386) + ENDIAN_WRITE32(content.n_value, _value); +#else + #error unknown architecture +#endif } @@ -1766,7 +1797,7 @@ void macho_relocation_info::set_r_pcrel(bool _value) { if ( _value ) temp |= 0x00000080; #elif defined(ARCH_I386) - temp &= 0x7FFFFFFF; + temp &= 0xFEFFFFFF; if ( _value ) temp |= 0x01000000; #else @@ -1822,7 +1853,7 @@ void macho_relocation_info::set_r_extern(bool _value) { if ( _value ) temp |= 0x00000010; #elif defined(ARCH_I386) - temp &= 0xEFFFFFFF; + temp &= 0xF7FFFFFF; if ( _value ) temp |= 0x08000000; #else diff --git a/src/ObjectFile.h b/src/ObjectFile.h index 5a80c26..255c028 100644 --- a/src/ObjectFile.h +++ b/src/ObjectFile.h @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -81,6 +82,7 @@ public: protected: Reader() {} + virtual ~Reader() {} }; class Segment @@ -93,9 +95,11 @@ public: uint64_t getBaseAddress() const { return fBaseAddress; } void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + virtual bool hasFixedAddress() const { return false; } protected: - Segment() : fBaseAddress(0) {} + Segment() : fBaseAddress(0) {} + virtual ~Segment() {} uint64_t fBaseAddress; }; @@ -120,6 +124,9 @@ class ContentWriter { public: virtual void write(uint64_t atomOffset, const void* buffer, uint64_t size) = 0; +protected: + ContentWriter() {} + virtual ~ContentWriter() {} }; class Atom @@ -168,7 +175,8 @@ public: unsigned int setSortOrder(unsigned int order); // recursively sets follow-on atoms protected: - Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL) {} + Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL) {} + virtual ~Atom() {} uint64_t fSegmentOffset; uint64_t fSectionOffset; @@ -201,22 +209,28 @@ public: ppcFixupAbsLow16, ppcFixupAbsLow14, ppcFixupAbsHigh16, ppcFixupAbsHigh16AddLow, pointer32Difference, pointer64Difference, x86FixupBranch32 }; - virtual bool isUnbound() const = 0; + virtual bool isTargetUnbound() const = 0; + virtual bool isFromTargetUnbound() const = 0; + virtual bool requiresRuntimeFixUp(bool slideable) const = 0; virtual bool isWeakReference() const = 0; - virtual bool requiresRuntimeFixUp() const = 0; virtual bool isLazyReference() const = 0; virtual Kind getKind() const = 0; virtual uint64_t getFixUpOffset() const = 0; virtual const char* getTargetName() const = 0; virtual Atom& getTarget() const = 0; virtual uint64_t getTargetOffset() const = 0; + virtual bool hasFromTarget() const = 0; virtual Atom& getFromTarget() const = 0; virtual const char* getFromTargetName() const = 0; virtual uint64_t getFromTargetOffset() const = 0; - virtual void setTarget(Atom&) = 0; + virtual void setTarget(Atom&, uint64_t offset) = 0; virtual void setFromTarget(Atom&) = 0; virtual const char* getDescription() const = 0; + +protected: + Reference() {} + virtual ~Reference() {} }; diff --git a/src/Options.cpp b/src/Options.cpp index 6b913af..6984e67 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -25,6 +26,7 @@ #include #include #include +#include #include "Options.h" @@ -52,7 +54,7 @@ Options::Options(int argc, const char* argv[]) fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), fPICTreatment(kPICError), fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), fUmbrellaName(NULL), fInitFunctionName(NULL), fZeroPageSize(0x1000), fStackSize(0), fStackAddr(0), fMinimumHeaderPad(0), - fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false) + fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false), fVerbose(false) { this->parsePreCommandLineEnvironmentSettings(); this->parse(argc, argv); @@ -251,7 +253,6 @@ bool Options::warnCommons() return fWarnCommons; } - bool Options::shouldExport(const char* symbolName) { switch (fExportMode) { @@ -364,21 +365,40 @@ Options::FileInfo Options::findFramework(const char* rootName) throwf("framework not found %s", rootName); } - -Options::FileInfo Options::makeFileInfo(const char* path) +Options::FileInfo Options::findFile(const char* path) { + FileInfo result; struct stat statBuffer; + + // if absolute path and not a .o file, the use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { + const int pathLen = strlen(path); + for (std::vector::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; + return result; + } + } + } + // try raw path if ( stat(path, &statBuffer) == 0 ) { - FileInfo result; result.path = strdup(path); result.fileLen = statBuffer.st_size; return result; } - else { - throwf("file not found: %s", path); - } + // not found + throwf("file not found: %s", path); } + void Options::loadFileList(const char* fileOfPaths) { FILE* file = fopen(fileOfPaths, "r"); @@ -392,7 +412,7 @@ void Options::loadFileList(const char* fileOfPaths) if ( eol != NULL ) *eol = '\0'; - fInputFiles.push_back(makeFileInfo(path)); + fInputFiles.push_back(findFile(path)); } fclose(file); } @@ -641,7 +661,7 @@ void Options::parse(int argc, const char* argv[]) if ( arg[0] == '-' ) { if ( (arg[1] == 'L') || (arg[1] == 'F') ) { - // previously handled + // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-arch") == 0 ) { parseArch(argv[++i]); @@ -755,7 +775,7 @@ void Options::parse(int argc, const char* argv[]) fForceSubtypeAll = true; } else if ( strcmp(arg, "-weak_library") == 0 ) { - FileInfo info = makeFileInfo(argv[++i]); + FileInfo info = findFile(argv[++i]); info.options.fWeakImport = true; fInputFiles.push_back(info); } @@ -930,13 +950,6 @@ void Options::parse(int argc, const char* argv[]) // FIX FIX fprintf(stderr, "ld64: warning -dead_strip not yet supported for 64-bit code\n"); } - else if ( strcmp(arg, "-v") == 0 ) { - extern const char ld64VersionString[]; - fprintf(stderr, "%s", ld64VersionString); - // if only -v specified, exit cleanly - if ( argc == 2 ) - exit(0); - } else if ( strcmp(arg, "-w") == 0 ) { // FIX FIX } @@ -1003,36 +1016,141 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-commons") == 0 ) { fCommonsMode = parseCommonsTreatment(argv[++i]); } - + else if ( strcmp(arg, "-v") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-Z") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-syslibroot") == 0 ) { + ++i; + // previously handled by buildSearchPaths() + } else { fprintf(stderr, "unknown option: %s\n", arg); } } else { - fInputFiles.push_back(makeFileInfo(arg)); + fInputFiles.push_back(findFile(arg)); } } } + + +// +// -syslibroot is used for SDK support. +// The rule is that all search paths (both explicit and default) are +// checked to see if they exist in the SDK. If so, that path is +// replaced with the sdk prefixed path. If not, that search path +// is used as is. If multiple -syslibroot options are specified +// their directory structures are logically overlayed and files +// from sdks specified earlier on the command line used before later ones. +// void Options::buildSearchPaths(int argc, const char* argv[]) { bool addStandardLibraryDirectories = true; - // scan through argv looking for -L and -F options + std::vector libraryPaths; + std::vector frameworkPaths; + // scan through argv looking for -L, -F, -Z, and -syslibroot options for(int i=0; i < argc; ++i) { if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) - fLibrarySearchPaths.push_back(&argv[i][2]); + libraryPaths.push_back(&argv[i][2]); else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) - fFrameworkSearchPaths.push_back(&argv[i][2]); + 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 ld64VersionString[]; + fprintf(stderr, "%s", ld64VersionString); + // if only -v specified, exit cleanly + 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); + } } if ( addStandardLibraryDirectories ) { - fLibrarySearchPaths.push_back("/usr/lib"); - fLibrarySearchPaths.push_back("/usr/local/lib"); + libraryPaths.push_back("/usr/lib"); + libraryPaths.push_back("/usr/local/lib"); - fFrameworkSearchPaths.push_back("/Library/Frameworks/"); - fFrameworkSearchPaths.push_back("/Network/Library/Frameworks/"); - fFrameworkSearchPaths.push_back("/System/Library/Frameworks/"); + frameworkPaths.push_back("/Library/Frameworks/"); + frameworkPaths.push_back("/Network/Library/Frameworks/"); + frameworkPaths.push_back("/System/Library/Frameworks/"); + } + + // now merge sdk and library paths to make real search paths + for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { + const char* libDir = *it; + bool sdkOverride = false; + if ( libDir[0] == '/' ) { + char betterLibDir[PATH_MAX]; + if ( strstr(libDir, "/..") != NULL ) { + if ( realpath(libDir, betterLibDir) != NULL ) + libDir = betterLibDir; + } + const int libDirLen = strlen(libDir); + for (std::vector::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 ) + fLibrarySearchPaths.push_back(libDir); + } + + // now merge sdk and framework paths to make real search paths + for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { + const char* frameworkDir = *it; + bool sdkOverride = false; + if ( frameworkDir[0] == '/' ) { + char betterFrameworkDir[PATH_MAX]; + if ( strstr(frameworkDir, "/..") != NULL ) { + if ( realpath(frameworkDir, betterFrameworkDir) != NULL ) + frameworkDir = betterFrameworkDir; + } + const int frameworkDirLen = strlen(frameworkDir); + for (std::vector::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 ) + fFrameworkSearchPaths.push_back(frameworkDir); + } + + if ( fVerbose ) { + fprintf(stderr,"Library search paths:\n"); + for (std::vector::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) + fprintf(stderr,"\t%s\n", *it); + fprintf(stderr,"Framework search paths:\n"); + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) + fprintf(stderr,"\t%s\n", *it); } } @@ -1169,6 +1287,11 @@ void Options::checkIllegalOptionCombinations() // check -init is only used when building a dylib if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) throw "-init can only be used with -dynamiclib"; + + // make sure all required exported symbols exist + for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++) + fInitialUndefines.push_back(*it); + } diff --git a/src/Options.h b/src/Options.h index 33d1137..520b304 100644 --- a/src/Options.h +++ b/src/Options.h @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -125,6 +126,7 @@ public: std::vector& sectionAlignments(); CommonsMode commonsMode(); bool warnCommons(); + FileInfo findFile(const char* path); private: class CStringEquals @@ -143,7 +145,6 @@ private: void parseArch(const char* architecture); FileInfo findLibrary(const char* rootName); FileInfo findFramework(const char* rootName); - FileInfo makeFileInfo(const char* path); bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result); uint32_t parseVersionNumber(const char*); void parseSectionOrderFile(const char* segment, const char* section, const char* path); @@ -200,12 +201,14 @@ private: uint32_t fMinimumHeaderPad; CommonsMode fCommonsMode; bool fWarnCommons; + bool fVerbose; std::vector fInitialUndefines; std::vector fExtraSections; std::vector fSectionAlignments; std::vector fLibrarySearchPaths; std::vector fFrameworkSearchPaths; + std::vector fSDKPaths; }; diff --git a/src/Readers/ObjectFileArchiveMachO.cpp b/src/Readers/ObjectFileArchiveMachO.cpp index a6851a1..b01d8a0 100644 --- a/src/Readers/ObjectFileArchiveMachO.cpp +++ b/src/Readers/ObjectFileArchiveMachO.cpp @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -71,6 +72,14 @@ private: std::vector Reader::fgEmptyList; +#undef SwapArchToHostInt32 +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + #define SwapArchToHostInt32(value) OSSwapBigToHostInt32(value) +#elif defined(ARCH_I386) + #define SwapArchToHostInt32(value) OSSwapLittleToHostInt32(value) +#endif + + bool Reader::Entry::hasLongName() const { @@ -130,7 +139,7 @@ uint32_t Reader::Entry::getContentSize() const const Reader::Entry* Reader::Entry::getNext() const { const uint8_t* p = this->getContent() + getContentSize(); - p = (const uint8_t*)(((uint32_t)p+3) & (-4)); // 4-byte align + p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align return (Reader::Entry*)p; } @@ -156,7 +165,7 @@ Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* pat else throw "archive has no table of contents"; const uint8_t* contents = firstMember->getContent(); - uint32_t ranlibArrayLen = OSReadBigInt32((void *) contents, 0); + uint32_t ranlibArrayLen = SwapArchToHostInt32(*((uint32_t*)contents)); fTableOfContents = (const struct ranlib*)&contents[4]; fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); fStringPool = (const char*)&contents[ranlibArrayLen+8]; @@ -222,7 +231,7 @@ const struct ranlib* Reader::ranlibBinarySearch(const char* key) const struct ranlib* base = fTableOfContents; for (uint32_t n = fTableOfContentCount; n > 0; n /= 2) { const struct ranlib* pivot = &base[n/2]; - const char* pivotStr = &fStringPool[OSSwapBigToHostInt32(pivot->ran_un.ran_strx)]; + const char* pivotStr = &fStringPool[SwapArchToHostInt32(pivot->ran_un.ran_strx)]; int cmp = strcmp(key, pivotStr); if ( cmp == 0 ) return pivot; @@ -244,7 +253,7 @@ const struct ranlib* Reader::ranlibLinearSearch(const char* key) { for (uint32_t i = 0; i < fTableOfContentCount; ++i) { const struct ranlib* entry = &fTableOfContents[i]; - const char* entryName = &fStringPool[OSSwapBigToHostInt32(entry->ran_un.ran_strx)]; + const char* entryName = &fStringPool[SwapArchToHostInt32(entry->ran_un.ran_strx)]; if ( strcmp(key, entryName) == 0 ) return entry; } @@ -256,7 +265,7 @@ void Reader::dumpTableOfContents() { for (unsigned int i=0; i < fTableOfContentCount; ++i) { const struct ranlib* e = &fTableOfContents[i]; - printf("%s in %s\n", &fStringPool[OSSwapBigToHostInt32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[OSSwapBigToHostInt32(e->ran_off)])->getName()); + printf("%s in %s\n", &fStringPool[SwapArchToHostInt32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[SwapArchToHostInt32(e->ran_off)])->getName()); } } @@ -268,20 +277,20 @@ std::vector* Reader::getJustInTimeAtomsFor(const char* else { const struct ranlib* result = NULL; if ( fSorted ) { - // do a binary search of table of contents lookig for requested symbol + // do a binary search of table of contents looking for requested symbol result = ranlibBinarySearch(name); } else { - // do a linear search of table of contents lookig for requested symbol + // do a linear search of table of contents looking for requested symbol result = ranlibLinearSearch(name); } if ( result != NULL ) { - const Entry* member = (Entry*)&fFileContent[OSSwapBigToHostInt32(result->ran_off)]; - //fprintf(stderr, "%s found in %s\n", name, member->getName()); + const Entry* member = (Entry*)&fFileContent[SwapArchToHostInt32(result->ran_off)]; if ( fInstantiatedEntries.count(member) == 0 ) { // only return these atoms once fInstantiatedEntries.insert(member); ObjectFile::Reader* r = makeObjectReaderForMember(member); + //fprintf(stderr, "%s found in %s\n", name, member->getName()); return new std::vector(r->getAtoms()); } } diff --git a/src/Readers/ObjectFileDylibMachO.cpp b/src/Readers/ObjectFileDylibMachO.cpp index cadbef3..fad4dcb 100644 --- a/src/Readers/ObjectFileDylibMachO.cpp +++ b/src/Readers/ObjectFileDylibMachO.cpp @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ diff --git a/src/Readers/ObjectFileMachO.cpp b/src/Readers/ObjectFileMachO.cpp index 5736d00..154ff8b 100644 --- a/src/Readers/ObjectFileMachO.cpp +++ b/src/Readers/ObjectFileMachO.cpp @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -20,7 +21,6 @@ * * @APPLE_LICENSE_HEADER_END@ */ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- */ namespace ObjectFileMachO { @@ -34,20 +34,23 @@ public: virtual ~Reference(); - virtual bool isUnbound() const; + virtual bool isTargetUnbound() const; + virtual bool isFromTargetUnbound() const; virtual bool isWeakReference() const; - virtual bool requiresRuntimeFixUp() const; + virtual bool requiresRuntimeFixUp(bool slideable) const; virtual bool isLazyReference() const; virtual Kind getKind() const; virtual uint64_t getFixUpOffset() const; virtual const char* getTargetName() const; virtual ObjectFile::Atom& getTarget() const; virtual uint64_t getTargetOffset() const; + virtual bool hasFromTarget() const; virtual ObjectFile::Atom& getFromTarget() const; virtual const char* getFromTargetName() const; - virtual void setTarget(ObjectFile::Atom&); + virtual void setTarget(ObjectFile::Atom&, uint64_t offset); virtual void setFromTarget(ObjectFile::Atom&); virtual void setFromTargetName(const char*); + virtual void setFromTargetOffset(uint64_t); virtual const char* getDescription() const; virtual uint64_t getFromTargetOffset() const; @@ -165,7 +168,7 @@ protected: Atom(Reader&, uint32_t offset); virtual ~Atom(); - const macho_section* findSectionFromOffset(macho_uintptr_t offset); + const macho_section* findSectionFromOffset(uint32_t offset); const macho_section* getCommonsSection(); void setSize(macho_uintptr_t); void setFollowOnAtom(Atom&); @@ -205,7 +208,7 @@ Reader::Reader(const macho_header* header, const char* path, const ObjectFile::R Reader::Reader(const char* path) : fPath(NULL), fOptions(*(new ObjectFile::ReaderOptions())), fHeader(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), - fNonAtomStabsStartIndex(0), fNonAtomStabsCount(0) + fIndirectTable(NULL), fNonAtomStabsStartIndex(0), fNonAtomStabsCount(0) { struct stat stat_buf; @@ -340,7 +343,7 @@ void Reader::init(const macho_header* header, const char* path) for (std::set::iterator it=cleavePoints.begin(); it != cleavePoints.end(); it++) { uint32_t cleavePoint = *it; - //printf("cleave offset 0x%08X\n", cleavePoint); + //printf("cleave offset 0x%08X, don't cleave=%d, isSymbol=%d\n", cleavePoint, dontCleavePoints.count(cleavePoint), symbolAtomOffsets.count(cleavePoint)); // only create an atom if it is not a don't-cleave point and there is not already an atom at this offset if ( (dontCleavePoints.count(cleavePoint) == 0) && (symbolAtomOffsets.count(cleavePoint) == 0) ) fAtoms.push_back(new Atom(*this, cleavePoint)); @@ -394,6 +397,7 @@ void Reader::init(const macho_header* header, const char* path) } } } + } // process stabs debugging info @@ -688,27 +692,75 @@ void Reader::addRelocReference(const macho_section* sect, const macho_relocation { srcAddr = sect->addr() + reloc->r_address(); Atom* srcAtom = findAtomCoveringOffset(srcAddr); - // lazy pointers have references to dyld_stub_binding_helper which need to be ignored - if ( (srcAtom->fSection->flags() & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS ) { - uint32_t offsetInSrcAtom = srcAddr - srcAtom->fOffset; - macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - uint8_t type = targetSymbol->n_type() & N_TYPE; - if ( type == N_UNDF ) { - const char* targetName = &fStrings[targetSymbol->n_strx()]; - macho_uintptr_t addend = pointerValue; + uint32_t offsetInSrcAtom = srcAddr - srcAtom->fOffset; + macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + uint8_t type = targetSymbol->n_type() & N_TYPE; + if ( type == N_UNDF ) { + const char* targetName = &fStrings[targetSymbol->n_strx()]; + macho_uintptr_t addend = pointerValue; + // ppc lazy pointers have initial reference to dyld_stub_binding_helper + if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { + std::vector& refs = srcAtom->getReferences(); + if ( refs.size() > 0 ) { + Reference* ref = (Reference*)refs[0]; + #if defined(ARCH_PPC64) + // hack to work around bad crt1.o in Mac OS X 10.4 + targetName = "dyld_stub_binding_helper"; + #endif + ref->setFromTargetName(targetName); + } + else { + fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size()); + } + } + #if defined(ARCH_PPC64) + // hack to work around bad crt1.o in Mac OS X 10.4 + else if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + // ignore extra relocation + } + #endif + else { srcAtom->addByNameReference(offsetInSrcAtom, Reference::pointer, targetName, addend, 0); } + } + else { + dstAddr = targetSymbol->n_value(); + Atom* dstAtom = findAtomCoveringOffset(dstAddr); + macho_uintptr_t addend = pointerValue; + // ppc lazy pointers have initial reference to dyld_stub_binding_helper + if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { + std::vector& refs = srcAtom->getReferences(); + if ( refs.size() > 0 ) { + Reference* ref = (Reference*)refs[0]; + ref->setFromTarget(*dstAtom); + ref->setFromTargetOffset(dstAddr - dstAtom->fOffset); + } + else { + fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size()); + } + } else { - dstAddr = targetSymbol->n_value(); - Atom* dstAtom = findAtomCoveringOffset(dstAddr); - macho_uintptr_t addend = pointerValue; srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, addend, 0); } } + } + else { + Atom* dstAtom = findAtomCoveringOffset(pointerValue); + // lazy pointers have references to dyld_stub_binding_helper which need to be ignored + if ( (srcAtom->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { + std::vector& refs = srcAtom->getReferences(); + if ( refs.size() > 0 ) { + Reference* ref = (Reference*)refs[0]; + ref->setFromTarget(*dstAtom); + ref->setFromTargetOffset(pointerValue - dstAtom->fOffset); + } + else { + fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", srcAtom->getDisplayName(), refs.size()); + } + } else { - Atom* dstAtom = findAtomCoveringOffset(pointerValue); srcAtom->addReference(offsetInSrcAtom, Reference::pointer, *dstAtom, pointerValue-dstAtom->fOffset, 0); } } @@ -723,39 +775,51 @@ void Reader::addRelocReference(const macho_section* sect, const macho_relocation { srcAddr = sect->addr() + reloc->r_address(); src = findAtomCoveringOffset(srcAddr); - // lazy pointers have references to dyld_stub_binding_helper which need to be ignored - if ( (src->fSection->flags() & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS ) { - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - Reference::Kind kind; - macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); - if ( reloc->r_pcrel() ) { - kind = Reference::x86FixupBranch32; - pointerValue += reloc->r_address() + sizeof(macho_uintptr_t); + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + Reference::Kind kind; + macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + kind = Reference::x86FixupBranch32; + pointerValue += srcAddr + sizeof(macho_uintptr_t); + } + else { + kind = Reference::pointer; + } + uint32_t offsetInSrcAtom = srcAddr - src->fOffset; + if ( reloc->r_extern() ) { + const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + uint8_t type = targetSymbol->n_type() & N_TYPE; + if ( type == N_UNDF ) { + const char* targetName = &fStrings[targetSymbol->n_strx()]; + macho_uintptr_t addend = pointerValue; + src->addByNameReference(offsetInSrcAtom, kind, targetName, addend, 0); } else { - kind = Reference::pointer; + dstAddr = targetSymbol->n_value(); + dst = findAtomCoveringOffset(dstAddr); + macho_uintptr_t addend = pointerValue - dstAddr; + src->addReference(offsetInSrcAtom, kind, *dst, addend, 0); } - uint32_t offsetInSrcAtom = srcAddr - src->fOffset; - if ( reloc->r_extern() ) { - const macho_nlist* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - uint8_t type = targetSymbol->n_type() & N_TYPE; - if ( type == N_UNDF ) { - const char* targetName = &fStrings[targetSymbol->n_strx()]; - macho_uintptr_t addend = pointerValue; - src->addByNameReference(offsetInSrcAtom, kind, targetName, addend, 0); + } + else { + dst = findAtomCoveringOffset(pointerValue); + // lazy pointers have references to dyld_stub_binding_helper which need to be ignored + if ( (src->fSection->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { + std::vector& refs = src->getReferences(); + if ( refs.size() == 1 ) { + Reference* ref = (Reference*)refs[0]; + ref->setFromTarget(*dst); + ref->setFromTargetOffset(pointerValue - dst->fOffset); } else { - dstAddr = targetSymbol->n_value(); - dst = findAtomCoveringOffset(dstAddr); - macho_uintptr_t addend = pointerValue - dstAddr; - src->addReference(offsetInSrcAtom, kind, *dst, addend, 0); + fprintf(stderr, "lazy pointer (%s) should only have one reference - has %ld references\n", src->getDisplayName(), refs.size()); } } - else { - dst = findAtomCoveringOffset(pointerValue); + else if ( ((uint8_t*)fixUpPtr)[-1] == 0xE8 ) // special case call instruction + this->addCallSiteReference(*src, offsetInSrcAtom, kind, *dst, 0, pointerValue - dst->fOffset); + else src->addReference(offsetInSrcAtom, kind, *dst, 0, 0); - } } } break; @@ -1005,6 +1069,10 @@ Atom* Reader::findAtomCoveringOffset(uint32_t offset) // keep same base } } + // possible that last atom is zero length + Atom* lastAtom = fAtoms.back(); + if ( (lastAtom->fOffset == offset) && (lastAtom->fSize == 0) ) + return lastAtom; #else const uint32_t atomCount = fAtoms.size(); for (uint32_t i=0; i < atomCount; ++i) { @@ -1013,7 +1081,7 @@ Atom* Reader::findAtomCoveringOffset(uint32_t offset) return atom; } #endif - return NULL; + throwf("address 0x%08X is not in any atom", offset); } uint32_t Reader::findAtomIndex(const Atom& atom) @@ -1169,11 +1237,16 @@ void Reader::buildOffsetsSet(const macho_relocation_info* reloc, const macho_sec if ( reloc->r_length() != 2 ) throw "vanilla pointer relocation found that is not 4-bytes"; #endif - //fprintf(stderr, "pcrel=%d, len=%d, extern=%d, type=%d\n", reloc->r_pcrel(), reloc->r_length(), reloc->r_extern(), reloc->r_type()); + //fprintf(stderr, "addr=0x%08X, pcrel=%d, len=%d, extern=%d, type=%d\n", reloc->r_address(), reloc->r_pcrel(), reloc->r_length(), reloc->r_extern(), reloc->r_type()); if ( !reloc->r_extern() ) { macho_uintptr_t pointerValue = ENDIAN_SWAP_POINTER(*((macho_uintptr_t*)fixUpPtr)); +#if defined(ARCH_I386) + // i386 stubs have internal relocs that should not cause a cleave + if ( (sect->flags() & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) + break; +#endif if ( reloc->r_pcrel() ) - pointerValue += reloc->r_address() + sizeof(macho_uintptr_t); + pointerValue += reloc->r_address() + sect->addr() + sizeof(macho_uintptr_t); // a pointer into code does not cleave the code (gcc always pointers to labels) insertOffsetIfNotText(cleavePoints, pointerValue); } @@ -1407,7 +1480,6 @@ Atom::Atom(Reader& owner, const macho_nlist* symbol) Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); if ( type == S_LAZY_SYMBOL_POINTERS ) { ref->setLazy(true); - ref->setFromTargetName("dyld_stub_binding_helper"); } } break; @@ -1465,7 +1537,6 @@ Atom::Atom(Reader& owner, uint32_t offset) Reference* ref = this->addByNameReference(0, Reference::pointer, name, 0, 0); if ( type == S_LAZY_SYMBOL_POINTERS ) { ref->setLazy(true); - ref->setFromTargetName("dyld_stub_binding_helper"); } const macho_nlist* sym = &fOwner.fSymbols[symbolIndex]; if ( (sym->n_type() & N_TYPE) == N_UNDF ) { @@ -1770,15 +1841,13 @@ void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer case Reference::pointer: { //fprintf(stderr, "writeContent: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( target.isImportProxy() ) { - if ( ref->isLazyReference() && finalLinkedImage ) { - // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) - *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getFromTarget().getAddress()); - } - else { - // external realocation ==> pointer contains addend - *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); - } + if ( ref->isLazyReference() && finalLinkedImage ) { + // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) + *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getFromTarget().getAddress()); + } + else if ( target.isImportProxy() ) { + // external realocation ==> pointer contains addend + *((macho_uintptr_t*)instructionPtr) = ENDIAN_SWAP_POINTER(ref->getTargetOffset()); } else { // internal relocation @@ -1806,8 +1875,8 @@ void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer else { const int64_t bl_eightMegLimit = 0x00FFFFFF; if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - //fprintf(stderr, "bl out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "bl out of range"; + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bl out of range (%lld max is +/-16M) from %s in %s to %s in %s", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); } } instruction = OSReadBigInt32(instructionPtr, 0); @@ -1942,7 +2011,7 @@ void Atom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer -const macho_section* Atom::findSectionFromOffset(macho_uintptr_t offset) +const macho_section* Atom::findSectionFromOffset(uint32_t offset) { const macho_section* const sectionsStart = (const macho_section*)( (char*)fOwner.fSegment + sizeof(macho_segment_command) ); const macho_section* const sectionsEnd = §ionsStart[fOwner.fSegment->nsects()]; @@ -1950,7 +2019,7 @@ const macho_section* Atom::findSectionFromOffset(macho_uintptr_t offset) if ( (s->addr() <= offset) && (offset < (s->addr()+s->size())) ) return s; } - throw "section not found"; + throwf("address 0x%08X is not in any section", offset); } void Atom::setSize(macho_uintptr_t size) @@ -2073,13 +2142,14 @@ Reference::~Reference() { } -bool Reference::isUnbound() const +bool Reference::isTargetUnbound() const { - if ( fTarget == NULL ) - return true; - if ( (fFromTargetName!=NULL) && (fFromTarget==NULL) ) - return true; - return false; + return ( fTarget == NULL ); +} + +bool Reference::isFromTargetUnbound() const +{ + return ( fFromTarget == NULL ); } bool Reference::isWeakReference() const @@ -2087,9 +2157,24 @@ bool Reference::isWeakReference() const return fWeak; } -bool Reference::requiresRuntimeFixUp() const +bool Reference::requiresRuntimeFixUp(bool slideable) const { - return ( fKind == Reference::pointer ); + // This static linker only supports pure code (no code fixups are runtime) +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + // Only data can be fixed up, and the codegen assures only "pointers" need runtime fixups + return ( (fKind == Reference::pointer) && (fTarget->isImportProxy() || fTarget->isWeakDefinition() || slideable) ); +#elif defined(ARCH_I386) + // For i386, Reference::pointer is used for both data pointers and instructions with 32-bit absolute operands + if ( fKind == Reference::pointer ) { + if ( fTarget->isImportProxy() ) + return true; + else + return slideable; + } + return false; +#else + #error +#endif } bool Reference::isLazyReference() const @@ -2119,9 +2204,10 @@ ObjectFile::Atom& Reference::getTarget() const return *fTarget; } -void Reference::setTarget(ObjectFile::Atom& target) +void Reference::setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ + fTargetOffset = offset; } @@ -2130,6 +2216,11 @@ ObjectFile::Atom& Reference::getFromTarget() const return *fFromTarget; } +bool Reference::hasFromTarget() const +{ + return ( (fFromTarget != NULL) || (fFromTargetName != NULL) ); +} + const char* Reference::getFromTargetName() const { if ( fFromTargetName != NULL ) @@ -2147,6 +2238,11 @@ void Reference::setFromTargetName(const char* name) fFromTargetName = name; } +void Reference::setFromTargetOffset(uint64_t offset) +{ + fFromTargetOffset = offset; +} + uint64_t Reference::getTargetOffset() const { return fTargetOffset; @@ -2237,7 +2333,7 @@ const char* Reference::getDescription() const // handled above break; case x86FixupBranch32: - sprintf(temp, "offset 0x%04llX, call pc-rel fixup to ", this->getFixUpOffset()); + sprintf(temp, "offset 0x%04llX, pc-rel fixup to ", this->getFixUpOffset()); break; } // always quote by-name references @@ -2256,8 +2352,15 @@ const char* Reference::getDescription() const sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getTargetOffset()); if ( (fKind==pointer) && fLazy ) { strcat(temp, " initially bound to \""); - strcat(temp, this->getFromTargetName()); - strcat(temp, "\""); + if ( (fFromTarget != NULL) || (fFromTargetName != NULL) ) { + strcat(temp, this->getFromTargetName()); + strcat(temp, "\""); + if ( this->getFromTargetOffset() != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08llX", this->getFromTargetOffset()); + } + else { + strcat(temp, "\" << missing >>"); + } } } return temp; diff --git a/src/Writers/ExecutableFileMachO.cpp b/src/Writers/ExecutableFileMachO.cpp index 5020cfe..90a9527 100644 --- a/src/Writers/ExecutableFileMachO.cpp +++ b/src/Writers/ExecutableFileMachO.cpp @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -42,6 +43,7 @@ public: private: void assignFileOffsets(); void partitionIntoSections(); + bool addBranchIslands(); void adjustLoadCommandsAndPadding(); void createDynamicLinkerCommand(); void createDylibCommands(); @@ -98,6 +100,7 @@ private: uint64_t fFileSize; uint64_t fBaseAddress; uint64_t fSize; + bool fFixedAddress; }; @@ -115,6 +118,7 @@ private: struct StabChunks { ObjectFile::Atom* fAtom; ObjectFile::Reader* fReader; + unsigned int fReaderOrder; unsigned int fOrderInReader; std::vector* fStabs; }; @@ -222,17 +226,19 @@ protected: class Segment : public ObjectFile::Segment { public: - Segment(const char* name, bool readable, bool writable, bool executable) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable) {} + Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} virtual const char* getName() const { return fName; } virtual bool isContentReadable() const { return fReadable; } virtual bool isContentWritable() const { return fWritable; } virtual bool isContentExecutable() const { return fExecutable; } + virtual bool hasFixedAddress() const { return fFixedAddress; } private: const char* fName; const bool fReadable; const bool fWritable; const bool fExecutable; + const bool fFixedAddress; }; static std::vector fgEmptyReferenceList; @@ -247,10 +253,10 @@ protected: }; -WriterAtom::Segment WriterAtom::fgPageZeroSegment("__PAGEZERO", false, false, false); -WriterAtom::Segment WriterAtom::fgTextSegment("__TEXT", true, false, true); -WriterAtom::Segment WriterAtom::fgLinkEditSegment("__LINKEDIT", true, false, false); -WriterAtom::Segment WriterAtom::fgStackSegment("__UNIXSTACK", true, true, false); +WriterAtom::Segment WriterAtom::fgPageZeroSegment("__PAGEZERO", false, false, false, true); +WriterAtom::Segment WriterAtom::fgTextSegment("__TEXT", true, false, true, false); +WriterAtom::Segment WriterAtom::fgLinkEditSegment("__LINKEDIT", true, false, false, false); +WriterAtom::Segment WriterAtom::fgStackSegment("__UNIXSTACK", true, true, false, true); std::vector WriterAtom::fgEmptyReferenceList; class PageZeroAtom : public WriterAtom @@ -535,7 +541,22 @@ private: WeakImportSetting fWeakImportSetting; }; - +#if defined(ARCH_PPC) || defined(ARCH_PPC64) +class BranchIslandAtom : public WriterAtom +{ +public: + BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); + virtual const char* getName() const { return fName; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return 4; } + virtual const char* getSectionName() const { return "__text"; } + virtual void writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const; +private: + const char* fName; + ObjectFile::Atom& fTarget; + uint32_t fTargetOffset; +}; +#endif struct ExportSorter { @@ -560,7 +581,7 @@ Writer::SectionInfo::SectionInfo() } Writer::SegmentInfo::SegmentInfo() - : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), fBaseAddress(0), fSize(0) + : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), fBaseAddress(0), fSize(0), fFixedAddress(false) { fName[0] = '\0'; } @@ -806,6 +827,10 @@ void Writer::write(std::vector& atoms, class ObjectFile // assign each section a file offset assignFileOffsets(); + // if need to add branch islands, reassign file offsets + if ( addBranchIslands() ) + assignFileOffsets(); + // build symbol table and relocations buildLinkEdit(); @@ -1004,7 +1029,7 @@ void Writer::collectExportedAndImportedAndLocalAtoms() bool Writer::stabChunkCompare(const struct StabChunks& lhs, const struct StabChunks& rhs) { if ( lhs.fReader != rhs.fReader ) { - return lhs.fReader < rhs.fReader; + return lhs.fReaderOrder < rhs.fReaderOrder; } return lhs.fOrderInReader < rhs.fOrderInReader; } @@ -1015,28 +1040,39 @@ unsigned int Writer::collectStabs() // collect all stabs chunks std::set seenReaders; + std::map readerOrdinals; const int atomCount = fAllAtoms->size(); for (int i=0; i < atomCount; ++i) { ObjectFile::Atom* atom = (*fAllAtoms)[i]; ObjectFile::Reader* atomsReader = atom->getFile(); - if ( (atomsReader != NULL) && (seenReaders.count(atomsReader) == 0) ) { - seenReaders.insert(atomsReader); - std::vector* readerStabs = atomsReader->getStabsDebugInfo(); - if ( readerStabs != NULL ) { - StabChunks chunk; - chunk.fAtom = NULL; - chunk.fReader = atomsReader; - chunk.fOrderInReader = 0; - chunk.fStabs = readerStabs; - fStabChunks.push_back(chunk); - count += readerStabs->size() + 1; // extra one is for trailing N_SO + unsigned int readerOrder = 0; + if ( atomsReader != NULL ) { + std::map::iterator pos = readerOrdinals.find(atomsReader); + if ( pos == readerOrdinals.end() ) { + readerOrder = readerOrdinals.size(); + readerOrdinals[atomsReader] = readerOrder; + std::vector* readerStabs = atomsReader->getStabsDebugInfo(); + if ( readerStabs != NULL ) { + StabChunks chunk; + chunk.fAtom = NULL; + chunk.fReader = atomsReader; + chunk.fReaderOrder = readerOrder; + chunk.fOrderInReader = 0; + chunk.fStabs = readerStabs; + fStabChunks.push_back(chunk); + count += readerStabs->size() + 1; // extra one is for trailing N_SO + } } - } + else { + readerOrder = pos->second; + } + } std::vector* atomStabs = atom->getStabsDebugInfo(); if ( atomStabs != NULL ) { StabChunks chunk; chunk.fAtom = atom; chunk.fReader = atomsReader; + chunk.fReaderOrder = readerOrder; chunk.fOrderInReader = atom->getSortOrder(); chunk.fStabs = atomStabs; fStabChunks.push_back(chunk); @@ -1044,9 +1080,18 @@ unsigned int Writer::collectStabs() } } - // sort by order in original .o file + // sort stabs: group by .o file std::sort(fStabChunks.begin(), fStabChunks.end(), stabChunkCompare); + //fprintf(stderr, "Sorted stabs:\n"); + //for (std::vector::iterator it=fStabChunks.begin(); it != fStabChunks.end(); it++) { + // ObjectFile::Atom* atom = (*it).fAtom; + // if ( atom != NULL ) + // fprintf(stderr, "\t%s\n", (*it).fAtom->getDisplayName()); + // else + // fprintf(stderr, "\t%s\n", (*it).fReader->getPath()); + //} + return count; } @@ -1103,28 +1148,30 @@ void Writer::addStabs(uint32_t startIndex, uint32_t count) - uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) { // search imports - const int importCount = fImportedAtoms.size(); - for (int i=0; i < importCount; ++i) { - if ( &atom == fImportedAtoms[i] ) + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + if ( &atom == *it ) return i + fSymbolTableImportStartIndex; + ++i; } // search locals - const int localCount = fLocalSymbolAtoms.size(); - for (int i=0; i < localCount; ++i) { - if ( &atom == fLocalSymbolAtoms[i] ) + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + if ( &atom == *it ) return i + fSymbolTableLocalStartIndex; + ++i; } // search exports - const int exportCount = fExportedAtoms.size(); - for (int i=0; i < exportCount; ++i) { - if ( &atom == fExportedAtoms[i] ) + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + if ( &atom == *it ) return i + fSymbolTableExportStartIndex; + ++i; } fprintf(stderr, "symbolIndex(%s)\n", atom.getDisplayName()); @@ -1132,7 +1179,6 @@ uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) throw "atom not found"; } - void Writer::buildFixups() { if ( fOptions.outputKind() == Options::kObjectFile ) @@ -1438,7 +1484,6 @@ void Writer::buildObjectFileFixups() const int refCount = refs.size(); for (int l=0; l < refCount; ++l) { ObjectFile::Reference* ref = refs[l]; - relocIndex += this->addRelocs(atom, ref); if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) { uint32_t offsetInSection = atom->getSectionOffset(); uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t); @@ -1447,6 +1492,36 @@ void Writer::buildObjectFileFixups() IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; //printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); fIndirectSymbolTable.push_back(entry); + if ( curSection->fAllLazyPointers ) { + ObjectFile::Atom& target = ref->getTarget(); + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + if ( &fromTarget == NULL ) { + fprintf(stderr, "lazy pointer %s missing initial binding\n", atom->getDisplayName()); + } + else { + bool isExtern = target.isImportProxy(); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset(); + macho_relocation_info reloc1; + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(macho_relocation_info::pointer_length); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.insert(fInternalRelocs.begin(), reloc1); + ++relocIndex; + } + } + } + else { + relocIndex += this->addRelocs(atom, ref); } } } @@ -1490,47 +1565,46 @@ void Writer::buildExecutableFixups() std::vector& refs = atom->getReferences(); const int refCount = refs.size(); //printf("atom %s has %d references\n", atom->getDisplayName(), refCount); - for (int l=0; l < refCount; ++l) { ObjectFile::Reference* ref = refs[l]; - // only care about references that need dyld fixups - if ( ref->requiresRuntimeFixUp() ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) { + // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol + if ( atom->getSize() != sizeof(macho_uintptr_t) ) { + printf("wrong size pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + } + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t); + uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + //fprintf(stderr,"indirect pointer atom %p %s section offset = %d\n", atom, atom->getDisplayName(), offsetInSection); + if ( ref->getTarget().isImportProxy() + || ref->getTarget().isWeakDefinition() + || (fOptions.interposable() && fOptions.shouldExport(ref->getTarget().getName())) + || (fOptions.nameSpace() == Options::kFlatNameSpace) + || (fOptions.nameSpace() == Options::kForceFlatNameSpace) ) { + undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); + } + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); + fIndirectSymbolTable.push_back(entry); + if ( slideable && curSection->fAllLazyPointers ) { + // if this is a dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides + macho_relocation_info pblaReloc; + SectionInfo* sectInfo = (SectionInfo*)ref->getFromTarget().getSection(); + uint32_t sectionNum = sectInfo->getIndex(); + pblaReloc.set_r_address(atom->getAddress()-fOptions.baseAddress()); + pblaReloc.set_r_symbolnum(sectionNum); + pblaReloc.set_r_pcrel(false); + pblaReloc.set_r_length(macho_relocation_info::pointer_length); + pblaReloc.set_r_extern(false); + pblaReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(pblaReloc); + } + } + else if ( ref->requiresRuntimeFixUp(slideable) ) { if ( ! atom->getSegment().isContentWritable() ) throwf("relocations in read-only segments not supported. %s in %s reference to %s", atom->getDisplayName(), atom->getFile()->getPath(), ref->getTarget().getDisplayName()); - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) { - // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol - if ( atom->getSize() != sizeof(macho_uintptr_t) ) { - printf("oversize pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath()); - } - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t); - uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - //printf("indirect pointer atom %s section offset = %d\n", atom->getDisplayName(), offsetInSection); - if ( ref->getTarget().isImportProxy() - || ref->getTarget().isWeakDefinition() - || (fOptions.interposable() && fOptions.shouldExport(ref->getTarget().getName())) - || (fOptions.nameSpace() == Options::kFlatNameSpace) - || (fOptions.nameSpace() == Options::kForceFlatNameSpace) ) { - undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); - } - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); - fIndirectSymbolTable.push_back(entry); - if ( slideable && curSection->fAllLazyPointers ) { - // if this is a dylib/bundle, need PBLAPTR internal relocation to fix up binding handler if image slides - macho_relocation_info pblaReloc; - macho_scattered_relocation_info* pblaSReloc = (macho_scattered_relocation_info*)&pblaReloc; - pblaSReloc->set_r_scattered(true); - pblaSReloc->set_r_pcrel(false); - pblaSReloc->set_r_length(macho_relocation_info::pointer_length); - pblaSReloc->set_r_type(PPC_RELOC_PB_LA_PTR); - pblaSReloc->set_r_address(atom->getAddress()-fOptions.baseAddress()); - pblaSReloc->set_r_value(ref->getFromTarget().getAddress()); // helper is stored in "from" address - fInternalRelocs.push_back(pblaReloc); - } - } - else if ( ref->getTarget().isImportProxy() ) { + if ( ref->getTarget().isImportProxy() ) { // if import is to antoher dylib, this is encoded as an external relocation macho_relocation_info externalReloc; externalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress()); @@ -1541,7 +1615,7 @@ void Writer::buildExecutableFixups() externalReloc.set_r_type(GENERIC_RELOC_VANILLA); fExternalRelocs.push_back(externalReloc); } - else if ( slideable ) { + else { // if this is a dylib/bundle, need fix-up encoded as an internal relocation macho_relocation_info internalReloc; SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); @@ -1613,10 +1687,10 @@ void Writer::writeAtoms() ::pwrite(fFileDescriptor, &x86Nop, 1, p); #endif } + end = offset+atom->getSize(); + //fprintf(stderr, "writing 0x%08X -> 0x%08X, atom %s\n", offset, end, atom->getDisplayName()); ContentWriter writer(fFileDescriptor, offset); atom->writeContent(requireAllFixUps, writer); - end = offset+atom->getSize(); - //printf("wrote 0x%08X -> 0x%08X, atom %s\n", offset, end, atom->getDisplayName()); } } } @@ -1672,6 +1746,7 @@ void Writer::partitionIntoSections() else currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); + currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); this->fSegmentInfos.push_back(currentSegmentInfo); } currentSectionInfo = new SectionInfo(); @@ -1696,6 +1771,8 @@ void Writer::partitionIntoSections() } if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) ) + currentSectionInfo->fAllLazyPointers = true; if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) currentSectionInfo->fAllNonLazyPointers = true; curSection = atom->getSection(); @@ -1721,6 +1798,135 @@ void Writer::partitionIntoSections() } +struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; +class TargetAndOffsetComparor +{ +public: + bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const + { + if ( left.atom != right.atom ) + return ( left.atom < right.atom ); + return ( left.offset < right.offset ); + } +}; + +// +// PowerPC can do PC relative branches as far as +/-16MB. +// If a branch target is >16MB then we insert one or more +// "branch islands" between the branch and its target that +// allows island hoping to the target. +// +// Branch Island Algorithm +// +// If the __TEXT segment < 16MB, then no branch islands needed +// Otherwise, every 15MB into the __TEXT segment is region is +// added which can contain branch islands. Every out of range +// bl instruction is checked. If it crosses a region, an island +// is added to that region with the same target and the bl is +// adjusted to target the island instead. +// +// In theory, if too many islands are added to one region, it +// could grow the __TEXT enough that other previously in-range +// bl branches could be pushed out of range. We reduce the +// probability this could happen by placing the ranges every +// 15MB which means the region would have to be 1MB (256K islands) +// before any branches could be pushed out of range. +// +bool Writer::addBranchIslands() +{ + bool result = false; +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + // Can only possibly need branch islands if __TEXT segment > 16M + if ( fLoadCommandsSegment->fSize > 16000000 ) { + const uint32_t kBetweenRegions = 15000000; // place regions of islands every 15MB in __text section + SectionInfo* textSection = NULL; + for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { + if ( strcmp((*it)->fSectionName, "__text") == 0 ) + textSection = *it; + } + const int kIslandRegionsCount = textSection->fSize / kBetweenRegions; + typedef std::map AtomToIsland; + AtomToIsland regionsMap[kIslandRegionsCount]; + std::vector regionsIslands[kIslandRegionsCount]; + unsigned int islandCount = 0; + + // create islands for branch references that are out of range + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == ObjectFile::Reference::ppcFixupBranch24 ) { + ObjectFile::Atom& target = ref->getTarget(); + int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); + int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); + int64_t displacement = dstAddr - srcAddr; + const int64_t kFifteenMegLimit = kBetweenRegions; + if ( (displacement > kFifteenMegLimit) || (displacement < (-kFifteenMegLimit)) ) { + for (int i=0; i < kIslandRegionsCount; ++i) { + AtomToIsland* region=®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( ((srcAddr < islandRegionAddr) && (dstAddr > islandRegionAddr)) + ||((dstAddr < islandRegionAddr) && (srcAddr > islandRegionAddr)) ) { + TargetAndOffset islandTarget = { &target, ref->getTargetOffset() }; + AtomToIsland::iterator pos = region->find(islandTarget); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, target, ref->getTargetOffset()); + (*region)[islandTarget] = island; + regionsIslands[i].push_back(island); + ++islandCount; + ref->setTarget(*island, 0); + } + else { + ref->setTarget(*(pos->second), 0); + } + } + } + } + } + } + } + + // insert islands into __text section and adjust section offsets + if ( islandCount > 0 ) { + std::vector newAtomList; + newAtomList.reserve(textSection->fAtoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions; + int regionIndex = 0; + uint64_t sectionOffset = 0; + for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + newAtomList.push_back(atom); + if ( atom->getAddress() > islandRegionAddr ) { + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + islandAtom->setSection(textSection); + uint64_t alignment = 1 << (islandAtom->getAlignment()); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + ++regionIndex; + islandRegionAddr += kBetweenRegions; + } + uint64_t alignment = 1 << (atom->getAlignment()); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + atom->setSectionOffset(sectionOffset); + sectionOffset += atom->getSize(); + } + textSection->fAtoms = newAtomList; + textSection->fSize = sectionOffset; + result = true; + } + + } +#endif + return result; +} + + void Writer::adjustLoadCommandsAndPadding() { fSegmentCommands->computeSize(); @@ -1787,20 +1993,26 @@ void Writer::assignFileOffsets() { bool haveFixedSegments = false; uint64_t fileOffset = 0; - uint64_t nextContiguousAddress = fOptions.baseAddress(); + uint64_t nextContiguousAddress = 0; + bool baseAddressUsed = false; std::vector& segmentInfos = fSegmentInfos; const int segCount = segmentInfos.size(); for(int i=0; i < segCount; ++i) { SegmentInfo* curSegment = segmentInfos[i]; fileOffset = (fileOffset+4095) & (-4096); curSegment->fFileOffset = fileOffset; - if ( curSegment->fBaseAddress == 0 ) { - // segment has uses next address - curSegment->fBaseAddress = nextContiguousAddress; + if ( curSegment->fFixedAddress ) { + // segment has fixed address already set + haveFixedSegments = true; } else { - // segment has fixed address - haveFixedSegments = true; + // segment uses next address + if ( !baseAddressUsed ) { + baseAddressUsed = true; + if ( fOptions.baseAddress() != 0 ) + nextContiguousAddress = fOptions.baseAddress(); + } + curSegment->fBaseAddress = nextContiguousAddress; } uint64_t address = curSegment->fBaseAddress; std::vector& sectionInfos = curSegment->fSections; @@ -1821,6 +2033,9 @@ void Writer::assignFileOffsets() fileOffset += curSection->fSize; } } + // page align segment size + curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); + curSegment->fSize = (curSegment->fSize+4095) & (-4096); if ( curSegment->fBaseAddress == nextContiguousAddress ) nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); } @@ -1881,7 +2096,6 @@ void Writer::adjustLinkEditSections() address += sectionOffset; } if ( fOptions.outputKind() == Options::kObjectFile ) { - //lastSeg->fBaseAddress = 0; //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> //lastSeg->fFileOffset = 0; @@ -1889,7 +2103,7 @@ void Writer::adjustLinkEditSections() } else { lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; - lastSeg->fSize = address - lastSeg->fBaseAddress; + lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); } } @@ -2033,7 +2247,13 @@ void MachHeaderAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWrit // fill out mach_header mh.set_magic(macho_header::magic_value); mh.set_cputype(fWriter.fOptions.architecture()); - mh.set_cpusubtype(0); +#if defined(ARCH_PPC) || defined(ARCH_PPC64) + mh.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); +#elif defined(ARCH_I386) + mh.set_cpusubtype(CPU_SUBTYPE_I386_ALL); +#else + #error unknown architecture +#endif mh.set_filetype(fileType); mh.set_ncmds(commandsCount); mh.set_sizeofcmds(commandsSize); @@ -2141,6 +2361,9 @@ void SegmentLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::Co else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { sect->set_flags(S_MOD_TERM_FUNC_POINTERS); } + else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED); + } } } p = &p[macho_segment_command::size + sectionsEmitted*macho_section::content_size]; @@ -2210,6 +2433,7 @@ void DyldLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::Conte { uint64_t size = this->getSize(); uint8_t buffer[size]; + bzero(buffer, size); macho_dylinker_command* cmd = (macho_dylinker_command*)buffer; if ( fWriter.fOptions.outputKind() == Options::kDyld ) cmd->set_cmd(LC_ID_DYLINKER); @@ -2484,7 +2708,7 @@ void IndirectTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile:: ENDIAN_WRITE32(indirectTable[entry.indirectIndex], entry.symbolIndex); } else { - throw "malformed indirect table"; + throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, entry.indirectIndex); } } writer.write(0, buffer, size); @@ -2546,6 +2770,35 @@ int32_t StringsLinkEditAtom::emptyString() return 1; } +#if defined(ARCH_PPC) || defined(ARCH_PPC64) +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) + : WriterAtom(writer, fgTextSegment), fTarget(target), fTargetOffset(targetOffset) +{ + char* buf = new char[strlen(name)+32]; + if ( targetOffset == 0 ) { + if ( islandRegion == 0 ) + sprintf(buf, "%s$island", name); + else + sprintf(buf, "%s$island_%d", name, islandRegion); + } + else { + sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); + } + fName = buf; +} + + +void BranchIslandAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + uint8_t instruction[4]; + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(&instruction, 0, branchInstruction); + writer.write(0, &instruction, 4); +} + + +#endif }; diff --git a/src/ld.cpp b/src/ld.cpp index 7b05e32..3b68059 100644 --- a/src/ld.cpp +++ b/src/ld.cpp @@ -1,4 +1,5 @@ -/* +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -283,21 +285,13 @@ private: void writeOutput(); void resolve(ObjectFile::Reference* reference); + void resolveFrom(ObjectFile::Reference* reference); void addJustInTimeAtoms(const char* name); void addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info); void addIndirectLibraries(ObjectFile::Reader* reader); bool haveIndirectLibrary(const char* path, ObjectFile::Reader* reader); bool haveDirectLibrary(const char* path); - - struct SegmentAndItsAtoms - { - class Segment* fSegment; - uint64_t fSegmentSize; - uint64_t fSegmentBaseAddress; - std::vector fAtoms; - }; - class SymbolTable { @@ -338,7 +332,6 @@ private: std::vector fDynamicLibraries; std::list fIndirectDynamicLibraries; std::vector fAllAtoms; - std::vector< SegmentAndItsAtoms > fAllAtomsBySegment; std::set fDeadAtoms; SectionOrder fSectionOrder; unsigned int fNextSortOrder; @@ -363,7 +356,7 @@ void Linker::setOutputFile(ExecutableFile::Writer* writer) } void Linker::link() -{ +{ this->buildAtomList(); this->loadUndefines(); this->resolveReferences(); @@ -382,9 +375,11 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) std::vector& references = atom.getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { ObjectFile::Reference* reference = *it; - if ( reference->isUnbound() ) { + if ( reference->isTargetUnbound() ) { fGlobalSymbolTable.require(reference->getTargetName()); } + if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + fGlobalSymbolTable.require(reference->getFromTargetName()); } // if in global namespace, add atom itself to symbol table @@ -479,6 +474,7 @@ void Linker::loadUndefines() std::vector unresolvableUndefines; fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); const int unresolvableCount = unresolvableUndefines.size(); + int unresolvableExportsCount = 0; if ( unresolvableCount != 0 ) { if ( doPrint ) { fprintf(stderr, "can't resolve symbols:\n"); @@ -494,12 +490,14 @@ void Linker::loadUndefines() strcat(nonLazyName, "$non_lazy_ptr"); ObjectFile::Atom* lastStubAtomWithUnresolved = NULL; ObjectFile::Atom* lastNonLazyAtomWithUnresolved = NULL; + // scan all atoms for references + bool foundAtomReference = false; for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { ObjectFile::Atom* atom = *it; std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* reference = *rit; - if ( reference->isUnbound() ) { + if ( reference->isTargetUnbound() ) { if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getTargetName(), stubName) == 0) ) { const char* path = atom->getFile()->getPath(); const char* shortPath = strrchr(path, '/'); @@ -509,6 +507,7 @@ void Linker::loadUndefines() shortPath = &shortPath[1]; fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); lastStubAtomWithUnresolved = atom; + foundAtomReference = true; } else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getTargetName(), nonLazyName) == 0) ) { const char* path = atom->getFile()->getPath(); @@ -519,13 +518,43 @@ void Linker::loadUndefines() shortPath = &shortPath[1]; fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); lastNonLazyAtomWithUnresolved = atom; + foundAtomReference = true; + } + } + if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) { + if ( (atom != lastStubAtomWithUnresolved) && (strcmp(reference->getFromTargetName(), stubName) == 0) ) { + const char* path = atom->getFile()->getPath(); + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + shortPath = path; + else + shortPath = &shortPath[1]; + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); + lastStubAtomWithUnresolved = atom; + foundAtomReference = true; + } + else if ( (atom != lastNonLazyAtomWithUnresolved) && (strcmp(reference->getFromTargetName(), nonLazyName) == 0) ) { + const char* path = atom->getFile()->getPath(); + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + shortPath = path; + else + shortPath = &shortPath[1]; + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), shortPath); + lastNonLazyAtomWithUnresolved = atom; + foundAtomReference = true; } } } } + // scan command line options + if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbols_list command line option\n"); + ++unresolvableExportsCount; + } } } - if ( doError ) + if ( doError && (unresolvableCount > unresolvableExportsCount) ) // last check should be removed. It exists so broken projects still build throw "symbol(s) not found"; } @@ -541,40 +570,52 @@ void Linker::loadUndefines() void Linker::addJustInTimeAtoms(const char* name) { - // give writer a crack at it - ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); - if ( atom != NULL ) { - this->addAtom(*atom); + // when creating final linked image, write gets first chance + if ( fOptions.outputKind() != Options::kObjectFile ) { + ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); + if ( atom != NULL ) { + this->addAtom(*atom);\ + return; + } } - else { - // give direct readers a chance - const int readerCount = fInputFiles.size(); - for (int i=0; i < readerCount; ++i) { - // if this reader is a static archive that has the symbol we need, pull in all atoms in that module - // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. - std::vector* atoms = fInputFiles[i]->getJustInTimeAtomsFor(name); + + // give direct readers a chance + const int readerCount = fInputFiles.size(); + for (int i=0; i < readerCount; ++i) { + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + std::vector* atoms = fInputFiles[i]->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + delete atoms; + return; // found a definition, no need to search anymore + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); + } + } + + // give indirect readers a chance + for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { + ObjectFile::Reader* reader = it->reader; + if ( reader != NULL ) { + std::vector* atoms = reader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); delete atoms; - return; // found a definition, no need to search anymore + break; //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); } } - - // give indirect readers a chance - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - ObjectFile::Reader* reader = it->reader; - if ( reader != NULL ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - delete atoms; - break; - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file #%d\n", name, i); - } - } + } + + // when creating .o file, writer goes last (this is so any static archives will be searched above) + if ( fOptions.outputKind() == Options::kObjectFile ) { + ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); + if ( atom != NULL ) { + this->addAtom(*atom); + return; } } + } void Linker::resolve(ObjectFile::Reference* reference) @@ -592,7 +633,7 @@ void Linker::resolve(ObjectFile::Reference* reference) target = fGlobalSymbolTable.find(nonStubTarget); // also need indirection to all exported weak symbols for C++ support if ( (target != NULL) && !target->isImportProxy() && (!target->isWeakDefinition() || (target->getScope() != ObjectFile::Atom::scopeGlobal)) ) { - reference->setTarget(*target); + reference->setTarget(*target, reference->getTargetOffset()); // mark stub as no longer being needed ObjectFile::Atom* stub = fGlobalSymbolTable.find(targetName); if ( stub != NULL ) { @@ -614,7 +655,7 @@ void Linker::resolve(ObjectFile::Reference* reference) if ( target == NULL ) { fprintf(stderr, "can't resolve: %s\n", targetName); } - reference->setTarget(*target); + reference->setTarget(*target, reference->getTargetOffset()); // handle weak-imports if ( target->isImportProxy() ) { @@ -622,8 +663,8 @@ void Linker::resolve(ObjectFile::Reference* reference) if ( reference->isWeakReference() ) { switch(target->getImportWeakness()) { case ObjectFile::Atom::kWeakUnset: - target->setImportWeakness(true); - break; + target->setImportWeakness(true); + break; case ObjectFile::Atom::kWeakImport: break; case ObjectFile::Atom::kNonWeakImport: @@ -634,8 +675,8 @@ void Linker::resolve(ObjectFile::Reference* reference) else { switch(target->getImportWeakness()) { case ObjectFile::Atom::kWeakUnset: - target->setImportWeakness(false); - break; + target->setImportWeakness(false); + break; case ObjectFile::Atom::kWeakImport: mismatch = true; break; @@ -656,16 +697,17 @@ void Linker::resolve(ObjectFile::Reference* reference) } } } - +} + +void Linker::resolveFrom(ObjectFile::Reference* reference) +{ // handle references that have two (from and to) targets - if ( reference->isUnbound() ) { - const char* fromTargetName = reference->getFromTargetName(); - ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); - if ( target == NULL ) { - fprintf(stderr, "can't resolve: %s\n", fromTargetName); - } - reference->setFromTarget(*fromTarget); + const char* fromTargetName = reference->getFromTargetName(); + ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); + if ( fromTarget == NULL ) { + fprintf(stderr, "can't resolve: %s\n", fromTargetName); } + reference->setFromTarget(*fromTarget); } @@ -677,9 +719,10 @@ void Linker::resolveReferences() std::vector& references = atom->getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { ObjectFile::Reference* reference = *it; - if ( reference->isUnbound() ) { + if ( reference->isTargetUnbound() ) this->resolve(reference); - } + if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + this->resolveFrom(reference); } } } @@ -715,6 +758,10 @@ void Linker::sortAtoms() { Section::assignIndexes(); std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter()); + //fprintf(stderr, "Sorted atoms:\n"); + //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + // fprintf(stderr, "\t%s\n", (*it)->getDisplayName()); + //} } @@ -722,16 +769,12 @@ void Linker::sortAtoms() // make sure given addresses are within reach of branches, etc void Linker::tweakLayout() { - - - } - void Linker::writeOutput() { // if main executable, find entry point atom - ObjectFile::Atom* entryPoint; + ObjectFile::Atom* entryPoint = NULL; switch ( fOptions.outputKind() ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: @@ -749,8 +792,10 @@ void Linker::writeOutput() } } break; - default: + case Options::kObjectFile: + case Options::kDynamicBundle: entryPoint = NULL; + break; } // tell writer about each segment's atoms @@ -766,12 +811,13 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) uint64_t len = info.fileLen; int fd = ::open(info.path, O_RDONLY, 0); if ( fd == -1 ) - throw "can't open file"; + throwf("can't open file, errno=%d", errno); if ( info.fileLen < 20 ) throw "file too small"; - char* p = (char*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE, fd, 0); + + char* p = (char*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); if ( p == (char*)(-1) ) - throw "can't map file"; + throwf("can't map file, errno=%d", errno); ::close(fd); // if fat file, skip to architecture we want @@ -855,7 +901,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) throw "wrong architecture in object file"; } } - else if ( fileType == MH_DYLIB ) { + else if ( (fileType == MH_DYLIB) || (fileType == MH_DYLIB_STUB) ) { ObjectFile::Reader* dylibReader = NULL; switch ( cpuType ) { case CPU_TYPE_POWERPC: @@ -925,18 +971,11 @@ void Linker::createReaders() // with flat namespace, blindly load all indirect libraries // the indirect list will grow as indirect libraries are loaded for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - struct stat statBuffer; - if ( stat(it->path, &statBuffer) == 0 ) { - Options::FileInfo info; - info.path = it->path; - info.fileLen = statBuffer.st_size; - info.options.fWeakImport = false; - info.options.fReExport = false; - info.options.fInstallPathOverride = NULL; - it->reader = this->createReader(info); + try { + it->reader = this->createReader(fOptions.findFile(it->path)); } - else { - fprintf(stderr, "ld64 warning: indirect library not found: %s\n", it->path); + catch (const char* msg) { + fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); } } break; @@ -951,17 +990,7 @@ void Linker::createReaders() for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { if ( it->reader == NULL ) { try { - struct stat statBuffer; - if ( stat(it->path, &statBuffer) != 0 ) - throw "file not found"; - - Options::FileInfo info; - info.path = it->path; - info.fileLen = statBuffer.st_size; - info.options.fWeakImport = false; - info.options.fReExport = false; - info.options.fInstallPathOverride = NULL; - it->reader = this->createReader(info); + it->reader = this->createReader(fOptions.findFile(it->path)); indirectAdded = true; } catch (const char* msg) { @@ -996,7 +1025,7 @@ void Linker::createReaders() dylibInfo.directReader = it->reExportParent; fDynamicLibraries.push_back(dylibInfo); if ( fOptions.readerOptions().fTraceIndirectDylibs ) - printf("[Logging for Build & Integration] Used indirect dynamic library: %s\n", it->path); + printf("[Logging for Build & Integration] Used indirect dynamic library: %s\n", it->reader->getPath()); } } } -- 2.45.2