X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/a645023da60d22e86be13f7b4d97adeff8bc6665..bee7e226299dacc6d80d9cf6f1585c3d5d0645e0:/src/ld/ld.cpp?ds=inline diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index d9d66a6..a8d2276 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * - * Copyright (c) 2005-2010 Apple Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -54,8 +54,7 @@ extern "C" double log2 ( double ); #include #include #include -#include -#include +#include #include #include "Options.h" @@ -67,6 +66,7 @@ extern "C" double log2 ( double ); #include "InputFiles.h" #include "Resolver.h" #include "OutputFile.h" +#include "Snapshot.h" #include "passes/stubs/make_stubs.h" #include "passes/dtrace_dof.h" @@ -74,11 +74,13 @@ extern "C" double log2 ( double ); #include "passes/tlvp.h" #include "passes/huge.h" #include "passes/compact_unwind.h" -#include "passes/order_file.h" +#include "passes/order.h" #include "passes/branch_island.h" #include "passes/branch_shim.h" #include "passes/objc.h" #include "passes/dylibs.h" +#include "passes/bitcode_bundle.h" +#include "passes/code_dedup.h" #include "parsers/archive_file.h" #include "parsers/macho_relocatable_file.h" @@ -87,28 +89,49 @@ extern "C" double log2 ( double ); #include "parsers/opaque_section_file.h" +struct PerformanceStatistics { + uint64_t startTool; + uint64_t startInputFileProcessing; + uint64_t startResolver; + uint64_t startDylibs; + uint64_t startPasses; + uint64_t startOutput; + uint64_t startDone; + vm_statistics_data_t vmStart; + vm_statistics_data_t vmEnd; +}; + + class InternalState : public ld::Internal { public: - InternalState(const Options& opts) : _options(opts) { } + InternalState(const Options& opts) : _options(opts), _atomsOrderedInSections(false) { } virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); + ld::Internal::FinalSection* getFinalSection(const char* seg, const char* sect, ld::Section::Type type); + uint64_t assignFileOffsets(); + void setSectionSizesAndAlignments(); void sortSections(); + void markAtomsOrdered() { _atomsOrderedInSections = true; } + bool hasReferenceToWeakExternal(const ld::Atom& atom); + virtual ~InternalState() {} private: + bool inMoveRWChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch); + bool inMoveROChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch); class FinalSection : public ld::Internal::FinalSection { public: - FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile); + FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options&); static int sectionComparer(const void* l, const void* r); - static const ld::Section& outputSection(const ld::Section& sect); - static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal); + static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill); + static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&); private: friend class InternalState; - static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen); - static uint32_t segmentOrder(const ld::Section& sect, bool objFile); + static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen, const Options& options); + static uint32_t segmentOrder(const ld::Section& sect, const Options& options); uint32_t _segmentOrder; uint32_t _sectionOrder; @@ -119,8 +142,14 @@ private: static ld::Section _s_TEXT_const; static ld::Section _s_DATA_nl_symbol_ptr; static ld::Section _s_DATA_common; + static ld::Section _s_DATA_zerofill; + static ld::Section _s_DATA_DIRTY_data; + static ld::Section _s_DATA_CONST_const; }; + bool hasZeroForFileOffset(const ld::Section* sect); + uint64_t pageAlign(uint64_t addr); + uint64_t pageAlign(uint64_t addr, uint64_t pageSize); struct SectionHash { size_t operator()(const ld::Section*) const; @@ -128,11 +157,13 @@ private: struct SectionEquals { bool operator()(const ld::Section* left, const ld::Section* right) const; }; - typedef __gnu_cxx::hash_map SectionInToOut; + typedef std::unordered_map SectionInToOut; SectionInToOut _sectionInToFinalMap; const Options& _options; + bool _atomsOrderedInSections; + std::unordered_map _pendingSegMove; }; ld::Section InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified); @@ -141,13 +172,17 @@ ld::Section InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld:: ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified); ld::Section InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); ld::Section InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill); +ld::Section InternalState::FinalSection::_s_DATA_zerofill("__DATA", "__zerofill", ld::Section::typeZeroFill); +ld::Section InternalState::FinalSection::_s_DATA_DIRTY_data( "__DATA_DIRTY", "__data", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_DATA_CONST_const( "__DATA_CONST", "__const", ld::Section::typeUnclassified); + std::vector InternalState::FinalSection::_s_segmentsSeen; size_t InternalState::SectionHash::operator()(const ld::Section* sect) const { size_t hash = 0; - __gnu_cxx::hash temp; + ld::CStringHash temp; hash += temp.operator()(sect->segmentName()); hash += temp.operator()(sect->sectionName()); return hash; @@ -159,23 +194,25 @@ bool InternalState::SectionEquals::operator()(const ld::Section* left, const ld: } -InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile) +InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options& opts) : ld::Internal::FinalSection(sect), - _segmentOrder(segmentOrder(sect, objFile)), - _sectionOrder(sectionOrder(sect, sectionsSeen)) + _segmentOrder(segmentOrder(sect, opts)), + _sectionOrder(sectionOrder(sect, sectionsSeen, opts)) { - //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n", + //fprintf(stderr, "FinalSection(%16s, %16s) _segmentOrder=%3d, _sectionOrder=0x%08X\n", // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder); } -const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect) +const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect, bool mergeZeroFill) { // merge sections in final linked image switch ( sect.type() ) { case ld::Section::typeLiteral4: case ld::Section::typeLiteral8: case ld::Section::typeLiteral16: - return _s_TEXT_const; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return _s_TEXT_const; + break; case ld::Section::typeUnclassified: if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) @@ -187,6 +224,18 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) return _s_TEXT_const; } + else if ( strcmp(sect.segmentName(), "__DATA_DIRTY") == 0 ) { + if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) + return _s_DATA_DIRTY_data; + } + else if ( strcmp(sect.segmentName(), "__DATA_CONST") == 0 ) { + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_DATA_CONST_const; + } + break; + case ld::Section::typeZeroFill: + if ( mergeZeroFill ) + return _s_DATA_zerofill; break; case ld::Section::typeCode: if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { @@ -207,7 +256,12 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& } break; case ld::Section::typeTentativeDefs: - return _s_DATA_common; + if ( (strcmp(sect.segmentName(), "__DATA") == 0) && (strcmp(sect.sectionName(), "__comm/tent") == 0) ) { + if ( mergeZeroFill ) + return _s_DATA_zerofill; + else + return _s_DATA_common; + } break; // FIX ME: more default: @@ -216,40 +270,67 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& return sect; } -const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal) +const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, const Options& options) { // in -r mode the only section that ever changes is __tenative -> __common with -d option - if ( (sect.type() == ld::Section::typeTentativeDefs) && makeTentativeDefsReal) + if ( (sect.type() == ld::Section::typeTentativeDefs) && options.makeTentativeDefinitionsReal()) return _s_DATA_common; return sect; } -uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, bool objFile) +uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, const Options& options) { - if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) - return 0; - if ( strcmp(sect.segmentName(), "__HEADER") == 0 ) // only used with -preload - return 0; - if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) - return 1; - // in -r mode, want __DATA last so zerofill sections are at end - if ( strcmp(sect.segmentName(), "__DATA") == 0 ) - return (objFile ? 5 : 2); - if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) - return 3; - if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) - return 4; - - // layout non-standard segments in order seen (+10 to shift beyond standard segments) + if ( options.outputKind() == Options::kPreload ) { + if ( strcmp(sect.segmentName(), "__HEADER") == 0 ) + return 0; + const std::vector& order = options.segmentOrder(); + for (size_t i=0; i != order.size(); ++i) { + if ( strcmp(sect.segmentName(), order[i]) == 0 ) + return i+1; + } + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return order.size()+1; + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) + return order.size()+2; + } + else if ( options.outputKind() == Options::kStaticExecutable ) { + const std::vector& order = options.segmentOrder(); + for (size_t i=0; i != order.size(); ++i) { + if ( strcmp(sect.segmentName(), order[i]) == 0 ) + return i+1; + } + if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) + return 0; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return order.size()+1; + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) + return order.size()+2; + } + else { + if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) + return 0; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return 1; + if ( strcmp(sect.segmentName(), "__TEXT_EXEC") == 0 ) + return 2; + // in -r mode, want __DATA last so zerofill sections are at end + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) + return (options.outputKind() == Options::kObjectFile) ? 6 : 3; + if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) + return 4; + if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) + return 5; + } + // layout non-standard segments in order seen (+100 to shift beyond standard segments) for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) { if ( strcmp(_s_segmentsSeen[i], sect.segmentName()) == 0 ) - return i+10; + return i+100; } _s_segmentsSeen.push_back(sect.segmentName()); - return _s_segmentsSeen.size()-1+10; + return _s_segmentsSeen.size()-1+100; } -uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen) +uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen, const Options& options) { if ( sect.type() == ld::Section::typeFirstSection ) return 0; @@ -257,6 +338,14 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint return 1; if ( sect.type() == ld::Section::typeLastSection ) return INT_MAX; + const std::vector* sectionList = options.sectionOrder(sect.segmentName()); + if ( ((options.outputKind() == Options::kPreload) || (options.outputKind() == Options::kDyld)) && (sectionList != NULL) ) { + uint32_t count = 10; + for (std::vector::const_iterator it=sectionList->begin(); it != sectionList->end(); ++it, ++count) { + if ( strcmp(*it, sect.sectionName()) == 0 ) + return count; + } + } if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { switch ( sect.type() ) { case ld::Section::typeCode: @@ -265,23 +354,28 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint return 10; else return 11; + case ld::Section::typeNonStdCString: + if ( (strcmp(sect.sectionName(), "__oslogstring") == 0) && options.makeEncryptable() ) + return INT_MAX-1; + else + return sectionsSeen+20; case ld::Section::typeStub: return 12; case ld::Section::typeStubHelper: return 13; case ld::Section::typeLSDA: - return INT_MAX-3; + return INT_MAX-4; case ld::Section::typeUnwindInfo: - return INT_MAX-2; + return INT_MAX-3; case ld::Section::typeCFI: - return INT_MAX-1; + return INT_MAX-2; case ld::Section::typeStubClose: return INT_MAX; default: return sectionsSeen+20; } } - else if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + else if ( strncmp(sect.segmentName(), "__DATA", 6) == 0 ) { switch ( sect.type() ) { case ld::Section::typeLazyPointerClose: return 8; @@ -296,48 +390,58 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint case ld::Section::typeTerminatorPointers: return 13; case ld::Section::typeTLVInitialValues: - return INT_MAX-4; // need TLV zero-fill to follow TLV init values + return INT_MAX-259; // need TLV zero-fill to follow TLV init values case ld::Section::typeTLVZeroFill: - return INT_MAX-3; + return INT_MAX-258; case ld::Section::typeZeroFill: // make sure __huge is always last zerofill section if ( strcmp(sect.sectionName(), "__huge") == 0 ) return INT_MAX-1; else - return INT_MAX-2; + return INT_MAX-256+sectionsSeen; // zero fill need to be last and in "seen" order default: + // __DATA,__const section should be near __mod_init_func not __data + if ( strcmp(sect.sectionName(), "__const") == 0 ) + return 14; + // Linker should put __cfstring near __const + if ( strcmp(sect.sectionName(), "__cfstring") == 0 ) + return 15; // Reorder sections to reduce page faults in object files - if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) return 20; else if ( strcmp(sect.sectionName(), "__objc_nlclslist") == 0 ) return 21; else if ( strcmp(sect.sectionName(), "__objc_catlist") == 0 ) return 22; - else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_nlcatlist") == 0 ) return 23; - else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 ) return 24; - else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) return 25; - else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) return 26; - else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) return 27; - else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) return 28; - else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) return 29; - else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) return 30; - else if ( strcmp(sect.sectionName(), "__objc_data") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) return 31; + else if ( strcmp(sect.sectionName(), "__objc_ivar") == 0 ) + return 32; + else if ( strcmp(sect.sectionName(), "__objc_data") == 0 ) + return 33; else return sectionsSeen+40; } } // make sure zerofill in any other section is at end of segment if ( sect.type() == ld::Section::typeZeroFill ) - return INT_MAX-1; + return INT_MAX-256+sectionsSeen; return sectionsSeen+20; } @@ -350,7 +454,7 @@ static void validateFixups(const ld::Atom& atom) uint32_t curClusterOffsetInAtom = 0; for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize); - assert((fit->offsetInAtom < atom.size()) || (fit->offsetInAtom == 0)); + assert((fit->offsetInAtom <= atom.size()) || (fit->offsetInAtom == 0)); if ( fit->firstInCluster() ) { assert(lastWasClusterEnd); curClusterOffsetInAtom = fit->offsetInAtom; @@ -428,38 +532,240 @@ static void validateFixups(const ld::Atom& atom) } #endif -ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) +bool InternalState::hasReferenceToWeakExternal(const ld::Atom& atom) +{ + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = indirectBindingTable[fit->u.bindingIndex]; + break; + } + if ( (target != NULL) && (target->definition() == ld::Atom::definitionRegular) + && (target->combine() == ld::Atom::combineByName) && (target->scope() == ld::Atom::scopeGlobal) ) { + return true; + } + } + return false; +} + +bool InternalState::inMoveRWChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch) { - ld::Internal::FinalSection* fs = this->getFinalSection(atom.section()); + if ( !_options.hasDataSymbolMoves() ) + return false; - // When order file used on data, turn ordered zero fill symbols into zero data - switch ( atom.section().type() ) { - case ld::Section::typeZeroFill: - case ld::Section::typeTentativeDefs: - if ( (_options.outputKind() == Options::kDyld) && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) - && (atom.size() <= 512) && (_options.orderedSymbolsCount() != 0) ) { - for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) { - if ( (it->objectFileName == NULL) && (strcmp(it->symbolName, atom.name()) == 0) ) { - // found in order file, move to __data section - fs = getFinalSection(InternalState::FinalSection::_s_DATA_data);\ - //fprintf(stderr, "moved %s to __data section\n", atom.name()); - break; - } + auto pos = _pendingSegMove.find(&atom); + if ( pos != _pendingSegMove.end() ) { + dstSeg = pos->second; + return true; + } + + bool result = false; + if ( _options.moveRwSymbol(atom.name(), filePath, dstSeg, wildCardMatch) ) + result = true; + + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + if ( inMoveRWChain(*(fit->u.target), filePath, dstSeg, wildCardMatch) ) + result = true; + } + } + } + + if ( result ) { + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + _pendingSegMove[fit->u.target] = dstSeg; } } - break; - default: - break; + } } - - //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs); + + return result; +} + + +bool InternalState::inMoveROChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch) +{ + if ( !_options.hasCodeSymbolMoves() ) + return false; + + auto pos = _pendingSegMove.find(&atom); + if ( pos != _pendingSegMove.end() ) { + dstSeg = pos->second; + return true; + } + + bool result = false; + if ( _options.moveRoSymbol(atom.name(), filePath, dstSeg, wildCardMatch) ) + result = true; + + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + if ( inMoveROChain(*(fit->u.target), filePath, dstSeg, wildCardMatch) ) + result = true; + } + } + } + + if ( result ) { + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + _pendingSegMove[fit->u.target] = dstSeg; + } + } + } + } + + return result; +} + + + + +ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) +{ + //fprintf(stderr, "addAtom: %s\n", atom.name()); + ld::Internal::FinalSection* fs = NULL; + const char* curSectName = atom.section().sectionName(); + const char* curSegName = atom.section().segmentName(); + ld::Section::Type sectType = atom.section().type(); + const ld::File* f = atom.file(); + const char* path = (f != NULL) ? f->path() : NULL; + if ( atom.section().type() == ld::Section::typeTentativeDefs ) { + // tentative defintions don't have a real section name yet + sectType = ld::Section::typeZeroFill; + if ( _options.mergeZeroFill() ) + curSectName = FinalSection::_s_DATA_zerofill.sectionName(); + else + curSectName = FinalSection::_s_DATA_common.sectionName(); + } + // Support for -move_to_r._segment + if ( atom.symbolTableInclusion() == ld::Atom::symbolTableIn ) { + const char* dstSeg; + bool wildCardMatch; + if ( inMoveRWChain(atom, path, dstSeg, wildCardMatch) ) { + if ( (sectType != ld::Section::typeZeroFill) + && (sectType != ld::Section::typeUnclassified) + && (sectType != ld::Section::typeTentativeDefs) + && (sectType != ld::Section::typeDyldInfo) ) { + if ( !wildCardMatch ) + warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType); + } + else { + curSegName = dstSeg; + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), curSegName, curSectName); + fs = this->getFinalSection(curSegName, curSectName, sectType); + } + } + if ( (fs == NULL) && inMoveROChain(atom, path, dstSeg, wildCardMatch) ) { + if ( (sectType != ld::Section::typeCode) + && (sectType != ld::Section::typeUnclassified) ) { + if ( !wildCardMatch ) + warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType); + } + else { + curSegName = dstSeg; + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), curSegName, curSectName); + fs = this->getFinalSection(curSegName, curSectName, ld::Section::typeCode); + } + } + } + // support for -rename_section and -rename_segment + for (const Options::SectionRename& rename : _options.sectionRenames()) { + if ( (strcmp(curSectName, rename.fromSection) == 0) && (strcmp(curSegName, rename.fromSegment) == 0) ) { + if ( _options.useDataConstSegment() && (strcmp(curSectName, "__const") == 0) && (strcmp(curSegName, "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + curSectName = "__const_weak"; + fs = this->getFinalSection(curSegName, curSectName, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__const_weak\n", atom.name()); + } + else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST + curSectName = "__got_weak"; + fs = this->getFinalSection("__DATA", curSectName, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name()); + } + else { + curSegName = rename.toSegment; + curSectName = rename.toSection; + fs = this->getFinalSection(rename.toSegment, rename.toSection, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); + } + } + } + for (const Options::SegmentRename& rename : _options.segmentRenames()) { + if ( strcmp(curSegName, rename.fromSegment) == 0 ) { + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), rename.toSegment, curSectName); + fs = this->getFinalSection(rename.toSegment, curSectName, sectType); + } + } + + // if no override, use default location + if ( fs == NULL ) { + fs = this->getFinalSection(atom.section()); + if ( _options.traceSymbolLayout() && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) ) + printf("symbol '%s', use default mapping to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); + } + + //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalseg=%s\n", &atom, atom.name(), atom.section().sectionName(), fs->segmentName()); #ifndef NDEBUG validateFixups(atom); #endif - fs->atoms.push_back(&atom); + if ( _atomsOrderedInSections ) { + // make sure this atom is placed before any trailing section$end$ atom + if ( (fs->atoms.size() > 1) && (fs->atoms.back()->contentType() == ld::Atom::typeSectionEnd) ) { + // last atom in section$end$ atom, insert before it + const ld::Atom* endAtom = fs->atoms.back(); + fs->atoms.pop_back(); + fs->atoms.push_back(&atom); + fs->atoms.push_back(endAtom); + } + else { + // not end atom, just append new atom + fs->atoms.push_back(&atom); + } + } + else { + // normal case + fs->atoms.push_back(&atom); + } + this->atomToSection[&atom] = fs; return fs; } + + +ld::Internal::FinalSection* InternalState::getFinalSection(const char* seg, const char* sect, ld::Section::Type type) +{ + for (std::vector::iterator it=sections.begin(); it != sections.end(); ++it) { + if ( (strcmp((*it)->segmentName(),seg) == 0) && (strcmp((*it)->sectionName(),sect) == 0) ) + return *it; + } + return this->getFinalSection(*new ld::Section(seg, sect, type, false)); +} + ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& inputSection) { const ld::Section* baseForFinalSection = &inputSection; @@ -471,7 +777,6 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in } // otherwise, create a new final section - bool objFile = false; switch ( _options.outputKind() ) { case Options::kStaticExecutable: case Options::kDynamicExecutable: @@ -482,7 +787,7 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in case Options::kPreload: { // coalesce some sections - const ld::Section& outSect = FinalSection::outputSection(inputSection); + const ld::Section& outSect = FinalSection::outputSection(inputSection, _options.mergeZeroFill()); pos = _sectionInToFinalMap.find(&outSect); if ( pos != _sectionInToFinalMap.end() ) { _sectionInToFinalMap[&inputSection] = pos->second; @@ -496,21 +801,20 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in } break; case Options::kObjectFile: - baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options.makeTentativeDefinitionsReal()); + baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options); pos = _sectionInToFinalMap.find(baseForFinalSection); if ( pos != _sectionInToFinalMap.end() ) { _sectionInToFinalMap[&inputSection] = pos->second; //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); return pos->second; } - objFile = true; break; } InternalState::FinalSection* result = new InternalState::FinalSection(*baseForFinalSection, - _sectionInToFinalMap.size(), objFile); + _sectionInToFinalMap.size(), _options); _sectionInToFinalMap[baseForFinalSection] = result; - //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", baseForFinalSection, result); + //fprintf(stderr, "_sectionInToFinalMap[%p(%s)] = %p\n", baseForFinalSection, baseForFinalSection->sectionName(), result); sections.push_back(result); return result; } @@ -543,6 +847,420 @@ void InternalState::sortSections() } + +bool InternalState::hasZeroForFileOffset(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + +uint64_t InternalState::pageAlign(uint64_t addr) +{ + const uint64_t alignment = _options.segmentAlignment(); + return ((addr+alignment-1) & (-alignment)); +} + +uint64_t InternalState::pageAlign(uint64_t addr, uint64_t pageSize) +{ + return ((addr+pageSize-1) & (-pageSize)); +} + +void InternalState::setSectionSizesAndAlignments() +{ + for (std::vector::iterator sit = sections.begin(); sit != sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { + // absolute symbols need their finalAddress() to their value + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + (const_cast(atom))->setSectionOffset(atom->objectAddress()); + } + } + else { + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + bool pagePerAtom = false; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + uint32_t atomModulus = atom->alignment().modulus; + if ( _options.pageAlignDataAtoms() && ( strncmp(atom->section().segmentName(), "__DATA", 6) == 0) ) { + // most objc sections cannot be padded + bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 ); + if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 ) + contiguousObjCSection = false; + if ( strcmp(atom->section().sectionName(), "__objc_data") == 0 ) + contiguousObjCSection = false; + switch ( atom->section().type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeTentativeDefs: + case ld::Section::typeZeroFill: + if ( contiguousObjCSection ) + break; + pagePerAtom = true; + if ( atomAlignmentPowerOf2 < 12 ) { + atomAlignmentPowerOf2 = 12; + atomModulus = 0; + } + break; + default: + break; + } + } + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atomAlignmentPowerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atomModulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + // LINKEDIT atoms are laid out later + if ( sect->type() != ld::Section::typeLinkEdit ) { + (const_cast(atom))->setSectionOffset(offset); + offset += atom->size(); + if ( pagePerAtom ) { + offset = (offset + 4095) & (-4096); // round up to end of page + } + } + if ( (atom->scope() == ld::Atom::scopeGlobal) + && (atom->definition() == ld::Atom::definitionRegular) + && (atom->combine() == ld::Atom::combineByName) + && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { + this->hasWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("weak external symbol: %s", atom->name()); + } + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + // unless -sectalign command line option overrides + if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) + sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); + // each atom in __eh_frame has zero alignment to assure they pack together, + // but compilers usually make the CFIs pointer sized, so we want whole section + // to start on pointer sized boundary. + if ( sect->type() == ld::Section::typeCFI ) + sect->alignment = 3; + if ( sect->type() == ld::Section::typeTLVDefs ) + this->hasThreadLocalVariableDefinitions = true; + } + } + + // All __thread_data and __thread_bss sections must have same alignment + uint8_t maxThreadAlign = 0; + for (ld::Internal::FinalSection* sect : sections) { + if ( (sect->type() == ld::Section::typeTLVInitialValues) || (sect->type() == ld::Section::typeTLVZeroFill) ) { + if ( sect->alignment > maxThreadAlign ) + maxThreadAlign = sect->alignment; + } + } + for (ld::Internal::FinalSection* sect : sections) { + if ( (sect->type() == ld::Section::typeTLVInitialValues) || (sect->type() == ld::Section::typeTLVZeroFill) ) { + sect->alignment = maxThreadAlign; + } + } + +} + +uint64_t InternalState::assignFileOffsets() +{ + const bool log = false; + const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kPreload)); + const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); + + uint64_t address = 0; + const char* lastSegName = ""; + uint64_t floatingAddressStart = _options.baseAddress(); + bool haveFixedSegments = false; + + // mark all sections as not having an address yet + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + sect->alignmentPaddingBytes = 0; + sect->address = ULLONG_MAX; + } + + // first pass, assign addresses to sections in segments with fixed start addresses + if ( log ) fprintf(stderr, "Fixed address segments:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + haveFixedSegments = true; + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + address = _options.customSegmentAddress(sect->segmentName()); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + + // if TEXT segment address is fixed, then flow other segments after it + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + floatingAddressStart = address; + } + } + + // second pass, assign section addresses to sections in segments that are ordered after a segment with a fixed address + if ( haveFixedSegments && !_options.segmentOrder().empty() ) { + if ( log ) fprintf(stderr, "After Fixed address segments:\n"); + lastSegName = ""; + ld::Internal::FinalSection* lastSect = NULL; + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( (sect->address == ULLONG_MAX) && _options.segmentOrderAfterFixedAddressSegment(sect->segmentName()) ) { + address = lastSect->address + lastSect->size; + if ( (strcmp(lastSegName, sect->segmentName()) != 0) && segmentsArePageAligned ) { + // round up size of last segment + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + sect->alignmentPaddingBytes = (address - unalignedAddress); + sect->address = address; + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + lastSegName = sect->segmentName(); + lastSect = sect; + } + } + + // last pass, assign addresses to remaining sections + address = floatingAddressStart; + lastSegName = ""; + ld::Internal::FinalSection* overlappingFixedSection = NULL; + ld::Internal::FinalSection* overlappingFlowSection = NULL; + ld::Internal::FinalSection* prevSect = NULL; + if ( log ) fprintf(stderr, "Regular layout segments:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->address != ULLONG_MAX ) + continue; + if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { + sect->alignmentPaddingBytes = 0; + continue; + } + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + // round up size of last segment if needed + if ( *lastSegName != '\0' ) { + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // set segment address based on end of last segment + address = pageAlign(address); + lastSegName = sect->segmentName(); + } + } + + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // if first section is more aligned than segment, move segment start up to match + if ( (prevSect != NULL) && (prevSect->type() == ld::Section::typeFirstSection) && (strcmp(prevSect->segmentName(), sect->segmentName()) == 0) ) { + assert(prevSect->size == 0); + if ( prevSect->address != sect->address ) { + prevSect->alignmentPaddingBytes += (sect->address - prevSect->address); + prevSect->address = sect->address; + } + } + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + // sanity check it does not overlap a fixed address segment + for (std::vector::iterator sit = sections.begin(); sit != sections.end(); ++sit) { + ld::Internal::FinalSection* otherSect = *sit; + if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) + continue; + if ( otherSect->size == 0 ) + continue; + if ( sect->size == 0 ) + continue; + if ( sect->address > otherSect->address ) { + if ( (otherSect->address+otherSect->size) > sect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + else { + if ( (sect->address+sect->size) > otherSect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + } + + if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + prevSect = sect; + } + if ( overlappingFixedSection != NULL ) { + fprintf(stderr, "Section layout:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + //if ( sect->isSectionHidden() ) + // continue; + fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", + sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + + } + throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", + overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), + overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); + } + + + // third pass, assign section file offsets + uint64_t fileOffset = 0; + lastSegName = ""; + if ( log ) fprintf(stderr, "All segments with file offsets:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( hasZeroForFileOffset(sect) ) { + // fileoff of zerofill sections is moot, but historically it is set to zero + sect->fileOffset = 0; + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + } + else { + // page align file offset at start of each segment + if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { + fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); + } + lastSegName = sect->segmentName(); + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + + // update section info + sect->fileOffset = fileOffset; + + // update running total + fileOffset += sect->size; + } + + if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", + sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, + sect->segmentName(), sect->sectionName()); + } + +#if 0 + // for encrypted iPhoneOS apps + if ( _options.makeEncryptable() ) { + // remember end of __TEXT for later use by load command + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); + } + } + } +#endif + + // return total file size + return fileOffset; +} + +static char* commatize(uint64_t in, char* out) +{ + char* result = out; + char rawNum[30]; + sprintf(rawNum, "%llu", in); + const int rawNumLen = strlen(rawNum); + for(int i=0; i < rawNumLen-1; ++i) { + *out++ = rawNum[i]; + if ( ((rawNumLen-i) % 3) == 1 ) + *out++ = ','; + } + *out++ = rawNum[rawNumLen-1]; + *out = '\0'; + return result; +} + +static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS ) + return; + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; + uint32_t milliSeconds = milliSecondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%24s: % 4d.%d milliseconds (% 4d.%d%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%24s: % 4d.%d seconds (% 4d.%d%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + + static void getVMInfo(vm_statistics_data_t& info) { mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); @@ -553,25 +1271,44 @@ static void getVMInfo(vm_statistics_data_t& info) } } + + +static const char* sOverridePathlibLTO = NULL; + +// +// This is magic glue that overrides the default behaviour +// of lazydylib1.o which is used to lazily load libLTO.dylib. +// +extern "C" const char* dyld_lazy_dylib_path_fix(const char* path); +const char* dyld_lazy_dylib_path_fix(const char* path) +{ + if ( sOverridePathlibLTO != NULL ) + return sOverridePathlibLTO; + else + return path; +} + + + int main(int argc, const char* argv[]) { -#if DEBUG - usleep(1000000); -#endif const char* archName = NULL; bool showArch = false; bool archInferred = false; try { - vm_statistics_data_t vmStart; - vm_statistics_data_t vmEnd; - getVMInfo(vmStart); - + PerformanceStatistics statistics; + statistics.startTool = mach_absolute_time(); + // create object to track command line arguments Options options(argc, argv); + InternalState state(options); - // gather stats + // allow libLTO to be overridden by command line -lto_library + sOverridePathlibLTO = options.overridePathlibLTO(); + + // gather vm stats if ( options.printStatistics() ) - getVMInfo(vmStart); + getVMInfo(statistics.vmStart); // update strings for error messages showArch = options.printArchPrefix(); @@ -579,50 +1316,79 @@ int main(int argc, const char* argv[]) archInferred = (options.architecture() == 0); // open and parse input files + statistics.startInputFileProcessing = mach_absolute_time(); ld::tool::InputFiles inputFiles(options, &archName); // load and resolve all references - InternalState state(options); + statistics.startResolver = mach_absolute_time(); ld::tool::Resolver resolver(options, inputFiles, state); resolver.resolve(); - + // add dylibs used + statistics.startDylibs = mach_absolute_time(); inputFiles.dylibs(state); // do initial section sorting so passes have rough idea of the layout state.sortSections(); // run passes + statistics.startPasses = mach_absolute_time(); ld::passes::objc::doPass(options, state); ld::passes::stubs::doPass(options, state); ld::passes::huge::doPass(options, state); ld::passes::got::doPass(options, state); ld::passes::tlvp::doPass(options, state); ld::passes::dylibs::doPass(options, state); // must be after stubs and GOT passes - ld::passes::order_file::doPass(options, state); - ld::passes::branch_shim::doPass(options, state); // must be after stubs - ld::passes::branch_island::doPass(options, state); // must be after stubs and order_file pass + ld::passes::order::doPass(options, state); + state.markAtomsOrdered(); + ld::passes::dedup::doPass(options, state); + ld::passes::branch_shim::doPass(options, state); // must be after stubs + ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); - ld::passes::compact_unwind::doPass(options, state); // must be after order-file pass - + ld::passes::compact_unwind::doPass(options, state); // must be after order pass + ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib + // sort final sections state.sortSections(); // write output file + statistics.startOutput = mach_absolute_time(); ld::tool::OutputFile out(options); out.write(state); + statistics.startDone = mach_absolute_time(); // print statistics //mach_o::relocatable::printCounts(); if ( options.printStatistics() ) { - getVMInfo(vmEnd); - fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", vmEnd.pageins-vmStart.pageins, - vmEnd.pageouts-vmStart.pageouts, vmEnd.faults-vmStart.faults); - + getVMInfo(statistics.vmEnd); + uint64_t totalTime = statistics.startDone - statistics.startTool; + printTime("ld total time", totalTime, totalTime); + printTime(" option parsing time", statistics.startInputFileProcessing - statistics.startTool, totalTime); + printTime(" object file processing", statistics.startResolver - statistics.startInputFileProcessing,totalTime); + printTime(" resolve symbols", statistics.startDylibs - statistics.startResolver, totalTime); + printTime(" build atom list", statistics.startPasses - statistics.startDylibs, totalTime); + printTime(" passess", statistics.startOutput - statistics.startPasses, totalTime); + printTime(" write output", statistics.startDone - statistics.startOutput, totalTime); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", + statistics.vmEnd.pageins-statistics.vmStart.pageins, + statistics.vmEnd.pageouts-statistics.vmStart.pageouts, + statistics.vmEnd.faults-statistics.vmStart.faults); + char temp[40]; + fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", inputFiles._totalObjectLoaded, commatize(inputFiles._totalObjectSize, temp)); + fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", inputFiles._totalArchivesLoaded, commatize(inputFiles._totalArchiveSize, temp)); + fprintf(stderr, "processed %3u dylib files\n", inputFiles._totalDylibsLoaded); + fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(out.fileSize(), temp)); + } + // Would like linker warning to be build error. + if ( options.errorBecauseOfWarnings() ) { + fprintf(stderr, "ld: fatal warning(s) induced error (-fatal_warnings)\n"); + return 1; } } catch (const char* msg) { - if ( archInferred ) + if ( strstr(msg, "malformed") != NULL ) + fprintf(stderr, "ld: %s\n", msg); + else if ( archInferred ) fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); else if ( showArch ) fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); @@ -639,7 +1405,11 @@ int main(int argc, const char* argv[]) // implement assert() function to print out a backtrace before aborting void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) { - fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + Snapshot *snapshot = Snapshot::globalSnapshot; + + snapshot->setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + snapshot->createSnapshot(); + snapshot->recordAssertionMessage("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); void* callStack[128]; int depth = ::backtrace(callStack, 128); @@ -657,7 +1427,10 @@ void __assert_rtn(const char* func, const char* file, int line, const char* fail } long offset = (uintptr_t)callStack[i] - (uintptr_t)info.dli_saddr; fprintf(stderr, "%d %p %s + %ld\n", i, callStack[i], symboName, offset); + snapshot->recordAssertionMessage("%d %p %s + %ld\n", i, callStack[i], symboName, offset); } + fprintf(stderr, "A linker snapshot was created at:\n\t%s\n", snapshot->rootDir()); + fprintf(stderr, "ld: Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); exit(1); } #endif