]> git.saurik.com Git - apple/ld64.git/blobdiff - src/ld/ld.cpp
ld64-302.3.tar.gz
[apple/ld64.git] / src / ld / ld.cpp
index d9d66a679ab86b4e0ea3df0ec2c3e08b88a37cda..a8d2276651f078ca402709b4a81c7f94696599e3 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
  *
 /* -*- 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@
  *
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -54,8 +54,7 @@ extern "C" double log2 ( double );
 #include <vector>
 #include <list>
 #include <algorithm>
 #include <vector>
 #include <list>
 #include <algorithm>
-#include <ext/hash_map>
-#include <ext/hash_set>
+#include <unordered_map>
 #include <cxxabi.h>
 
 #include "Options.h"
 #include <cxxabi.h>
 
 #include "Options.h"
@@ -67,6 +66,7 @@ extern "C" double log2 ( double );
 #include "InputFiles.h"
 #include "Resolver.h"
 #include "OutputFile.h"
 #include "InputFiles.h"
 #include "Resolver.h"
 #include "OutputFile.h"
+#include "Snapshot.h"
 
 #include "passes/stubs/make_stubs.h"
 #include "passes/dtrace_dof.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/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/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"
 
 #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"
 
 
 #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:
 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&);
        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                                                                    sortSections();
+       void                                                                    markAtomsOrdered() { _atomsOrderedInSections = true; }
+       bool                                                                    hasReferenceToWeakExternal(const ld::Atom& atom);
+
        virtual                                                                 ~InternalState() {}
 private:
        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:
 
        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 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;
        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;
 
                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_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;
        
        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;
        };
        struct SectionEquals {
                bool operator()(const ld::Section* left, const ld::Section* right) const;
        };
-       typedef __gnu_cxx::hash_map<const ld::Section*, FinalSection*, SectionHash, SectionEquals> SectionInToOut;
+       typedef std::unordered_map<const ld::Section*, FinalSection*, SectionHash, SectionEquals> SectionInToOut;
        
 
        SectionInToOut                  _sectionInToFinalMap;
        const Options&                  _options;
        
 
        SectionInToOut                  _sectionInToFinalMap;
        const Options&                  _options;
+       bool                                    _atomsOrderedInSections;
+       std::unordered_map<const ld::Atom*, const char*> _pendingSegMove;
 };
 
 ld::Section    InternalState::FinalSection::_s_DATA_data( "__DATA", "__data",  ld::Section::typeUnclassified);
 };
 
 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_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<const char*> InternalState::FinalSection::_s_segmentsSeen;
 
 
 size_t InternalState::SectionHash::operator()(const ld::Section* sect) const
 {
        size_t hash = 0;        
 std::vector<const char*> InternalState::FinalSection::_s_segmentsSeen;
 
 
 size_t InternalState::SectionHash::operator()(const ld::Section* sect) const
 {
        size_t hash = 0;        
-       __gnu_cxx::hash<const char*> temp;
+       ld::CStringHash temp;
        hash += temp.operator()(sect->segmentName());
        hash += temp.operator()(sect->sectionName());
        return hash;
        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), 
        : 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);
 }
 
        //              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:
 {
        // 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 )
                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;
                        }
                                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 ) {
                        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:
                        }
                        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:
                        break;
                        // FIX ME: more 
                default:
@@ -216,40 +270,67 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section&
        return sect;
 }
 
        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
 {
        // 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;
 }
 
                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<const char*>& 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<const char*>& 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 )
        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());
        }
        _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;
 {
        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;
                return 1;
        if ( sect.type() == ld::Section::typeLastSection )
                return INT_MAX;
+       const std::vector<const char*>* sectionList = options.sectionOrder(sect.segmentName());
+       if ( ((options.outputKind() == Options::kPreload) || (options.outputKind() == Options::kDyld)) && (sectionList != NULL) ) {
+               uint32_t count = 10;
+               for (std::vector<const char*>::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:
        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;
                                        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:
                        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:
                        case ld::Section::typeUnwindInfo:
-                               return INT_MAX-2;
+                               return INT_MAX-3;
                        case ld::Section::typeCFI:
                        case ld::Section::typeCFI:
-                               return INT_MAX-1;
+                               return INT_MAX-2;
                        case ld::Section::typeStubClose:
                                return INT_MAX;
                        default:
                                return sectionsSeen+20;
                }
        }
                        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;
                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:
                        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:
                        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
                        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; // <rdar://problem/25448494> zero fill need to be last and in "seen" order
                        default:
                        default:
+                               // <rdar://problem/14348664> __DATA,__const section should be near __mod_init_func not __data
+                               if ( strcmp(sect.sectionName(), "__const") == 0 )
+                                       return 14;
+                               // <rdar://problem/17125893> Linker should put __cfstring near __const
+                               if ( strcmp(sect.sectionName(), "__cfstring") == 0 )
+                                       return 15;
                                // <rdar://problem/7435296> Reorder sections to reduce page faults in object files
                                // <rdar://problem/7435296> 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;
                                        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;
                                        return 23;
-                               else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 ) 
                                        return 24;
                                        return 24;
-                               else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) 
                                        return 25;
                                        return 25;
-                               else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) 
                                        return 26;
                                        return 26;
-                               else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) 
                                        return 27;
                                        return 27;
-                               else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) 
                                        return 28;
                                        return 28;
-                               else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) 
                                        return 29;
                                        return 29;
-                               else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) 
                                        return 30;
                                        return 30;
-                               else if ( strcmp(sect.sectionName(), "__objc_data") == 0 ) 
+                               else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) 
                                        return 31;
                                        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 )
                                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;
 }
 
        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);
        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;
                if ( fit->firstInCluster() ) {
                        assert(lastWasClusterEnd);
                        curClusterOffsetInAtom = fit->offsetInAtom;
@@ -428,38 +532,240 @@ static void validateFixups(const ld::Atom& atom)
 }
 #endif
 
 }
 #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;
 
 
-       // <rdar://problem/8612550> 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
 #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;
 }
 
        return fs;
 }
 
+
+
+ld::Internal::FinalSection* InternalState::getFinalSection(const char* seg, const char* sect, ld::Section::Type type)
+{      
+       for (std::vector<ld::Internal::FinalSection*>::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;
 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
        }
 
        // otherwise, create a new final section
-       bool objFile = false;
        switch ( _options.outputKind() ) {
                case Options::kStaticExecutable:
                case Options::kDynamicExecutable:
        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
                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;
                                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:
                        }
                        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;
                        }
                        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, 
                        break;
        }
 
        InternalState::FinalSection* result = new InternalState::FinalSection(*baseForFinalSection, 
-                                                                                                                                       _sectionInToFinalMap.size(), objFile);
+                                                                                                                                       _sectionInToFinalMap.size(), _options);
        _sectionInToFinalMap[baseForFinalSection] = result;
        _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;
 }
        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<ld::Internal::FinalSection*>::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<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+                               const ld::Atom* atom = *ait;
+                               (const_cast<ld::Atom*>(atom))->setSectionOffset(atom->objectAddress());
+                       }
+               }
+               else {
+                       uint16_t maxAlignment = 0;
+                       uint64_t offset = 0;
+                       for (std::vector<const ld::Atom*>::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<ld::Atom*>(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;
+               }
+       }
+
+       // <rdar://problem/24221680> 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<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::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);
+
+               // <rdar://problem/21994854> 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<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::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;
+
+                       // <rdar://problem/10445047> 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<ld::Internal::FinalSection*>::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);
 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[])
 {
 int main(int argc, const char* argv[])
 {
-#if DEBUG
-       usleep(1000000);
-#endif
        const char* archName = NULL;
        bool showArch = false;
        bool archInferred = false;
        try {
        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);
                // 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() )
                if ( options.printStatistics() )
-                       getVMInfo(vmStart);
+                       getVMInfo(statistics.vmStart);
 
                // update strings for error messages
                showArch = options.printArchPrefix();
 
                // 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
                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
                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();
                ld::tool::Resolver resolver(options, inputFiles, state);
                resolver.resolve();
-                               
+        
                // add dylibs used
                // 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
                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::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::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
                // sort final sections
                state.sortSections();
 
                // write output file
+               statistics.startOutput = mach_absolute_time();
                ld::tool::OutputFile out(options);
                out.write(state);
                ld::tool::OutputFile out(options);
                out.write(state);
+               statistics.startDone = mach_absolute_time();
                
                // print statistics
                //mach_o::relocatable::printCounts();
                if ( options.printStatistics() ) {
                
                // 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));
+               }
+               // <rdar://problem/6780050> 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) {
                }
        }
        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);
                        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)
 {
 // 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);
 
        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);
                }
                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
        exit(1);
 }
 #endif