]> git.saurik.com Git - apple/ld64.git/blobdiff - src/ld/passes/branch_island.cpp
ld64-123.2.tar.gz
[apple/ld64.git] / src / ld / passes / branch_island.cpp
diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp
new file mode 100644 (file)
index 0000000..d42fa54
--- /dev/null
@@ -0,0 +1,623 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdint.h>
+#include <math.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <libkern/OSByteOrder.h>
+
+#include <vector>
+#include <map>
+
+#include "MachOFileAbstraction.hpp"
+#include "ld.hpp"
+#include "branch_island.h"
+
+namespace ld {
+namespace passes {
+namespace branch_island {
+
+
+
+
+struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; };
+class TargetAndOffsetComparor
+{
+public:
+       bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const
+       {
+               if ( left.atom != right.atom )
+                       return ( left.atom < right.atom );
+               return ( left.offset < right.offset );
+       }
+};
+
+
+static bool _s_log = false;
+static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode);
+
+class PPCBranchIslandAtom : public ld::Atom {
+public:
+                                                                                       PPCBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget)
+                               : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                       ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, 
+                                                       ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), 
+                               _name(nm),
+                               _target(target),
+                               _finalTarget(finalTarget) { }
+
+       virtual const ld::File*                                 file() const                                    { return NULL; }
+       virtual bool                                                    translationUnitSource(const char** dir, const char**) const 
+                                                                                                                                                       { return false; }
+       virtual const char*                                             name() const                                    { return _name; }
+       virtual uint64_t                                                size() const                                    { return 4; }
+       virtual uint64_t                                                objectAddress() const                   { return 0; }
+       virtual void                                                    copyRawContent(uint8_t buffer[]) const {
+               int64_t displacement = _target->finalAddress() - this->finalAddress();
+               const int64_t bl_sixteenMegLimit = 0x00FFFFFF;
+               if ( _target->contentType() == ld::Atom::typeBranchIsland ) {
+                       // try optimizing away intermediate islands
+                       int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress();
+                       if ( (skipToFinalDisplacement > bl_sixteenMegLimit) && (skipToFinalDisplacement < (-bl_sixteenMegLimit)) ) {
+                               displacement = skipToFinalDisplacement;
+                       }
+               }
+               int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC);
+               OSWriteBigInt32(buffer, 0, branchInstruction);
+       }
+       virtual void                                                    setScope(Scope)                                 { }
+
+private:
+       const char*                                                             _name;
+       const ld::Atom*                                                 _target;
+       TargetAndOffset                                                 _finalTarget;
+};
+
+
+class ARMtoARMBranchIslandAtom : public ld::Atom {
+public:
+                                                                                       ARMtoARMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget)
+                               : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                       ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, 
+                                                       ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), 
+                               _name(nm),
+                               _target(target),
+                               _finalTarget(finalTarget) { }
+
+       virtual const ld::File*                                 file() const                                    { return NULL; }
+       virtual bool                                                    translationUnitSource(const char** dir, const char**) const 
+                                                                                                                                                       { return false; }
+       virtual const char*                                             name() const                                    { return _name; }
+       virtual uint64_t                                                size() const                                    { return 4; }
+       virtual uint64_t                                                objectAddress() const                   { return 0; }
+       virtual void                                                    copyRawContent(uint8_t buffer[]) const {
+               int64_t displacement = _target->finalAddress() - this->finalAddress() - 8;
+               if ( _target->contentType() == ld::Atom::typeBranchIsland ) {
+                       // an ARM branch can branch farther than a thumb branch.  The branch
+                       // island generation was conservative and put islands every thumb
+                       // branch distance apart.  Check to see if this is a an island
+                       // hopping branch that could be optimized to go directly to target.
+                       int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 8;
+                       if ( (skipToFinalDisplacement < 33554428LL) && (skipToFinalDisplacement > (-33554432LL)) ) {
+                               // can skip branch island and jump straight to target
+                               if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", 
+                                                                                       _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress());
+                               displacement = skipToFinalDisplacement;
+                       }
+                       else {
+                               // ultimate target is too far, jump to island
+                               if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", 
+                                                                                       _target->name(), _finalTarget.atom->finalAddress());
+                       }
+               }
+               uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF;
+               int32_t branchInstruction = 0xEA000000 | imm24;
+               OSWriteLittleInt32(buffer, 0, branchInstruction);
+       }
+       virtual void                                                    setScope(Scope)                                 { }
+
+private:
+       const char*                                                             _name;
+       const ld::Atom*                                                 _target;
+       TargetAndOffset                                                 _finalTarget;
+};
+
+
+
+class ARMtoThumb1BranchIslandAtom : public ld::Atom {
+public:
+                                                                                       ARMtoThumb1BranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget)
+                               : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                       ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, 
+                                                       ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), 
+                               _name(nm),
+                               _target(target),
+                               _finalTarget(finalTarget) { }
+
+       virtual const ld::File*                                 file() const                                    { return NULL; }
+       virtual bool                                                    translationUnitSource(const char** dir, const char**) const 
+                                                                                                                                                       { return false; }
+       virtual const char*                                             name() const                                    { return _name; }
+       virtual uint64_t                                                size() const                                    { return 16; }
+       virtual uint64_t                                                objectAddress() const                   { return 0; }
+       virtual void                                                    copyRawContent(uint8_t buffer[]) const {
+               // There is no large displacement thumb1 branch instruction.
+               // Instead use ARM instructions that can jump to thumb.
+               // we use a 32-bit displacement, so we can directly jump to target which means no island hopping
+               int64_t displacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - (this->finalAddress() + 12);
+               if ( _finalTarget.atom->isThumb() )
+                       displacement |= 1;
+               if (_s_log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", 
+                                                                               _target->name(), _finalTarget.atom->finalAddress());
+               OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); //      ldr  ip, pc + 4
+               OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); //      add      ip, pc, ip
+               OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); //      bx       ip
+               OSWriteLittleInt32(&buffer[12], 0, displacement);       //      .long target-this               
+       }
+       virtual void                                                    setScope(Scope)                                 { }
+
+private:
+       const char*                                                             _name;
+       const ld::Atom*                                                 _target;
+       TargetAndOffset                                                 _finalTarget;
+};
+
+
+
+class Thumb2toThumbBranchIslandAtom : public ld::Atom {
+public:
+                                                                                       Thumb2toThumbBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget)
+                               : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                       ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, 
+                                                       ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), 
+                               _name(nm),
+                               _target(target),
+                               _finalTarget(finalTarget) { }
+
+       virtual const ld::File*                                 file() const                                    { return NULL; }
+       virtual bool                                                    translationUnitSource(const char** dir, const char**) const 
+                                                                                                                                                       { return false; }
+       virtual const char*                                             name() const                                    { return _name; }
+       virtual uint64_t                                                size() const                                    { return 4; }
+       virtual uint64_t                                                objectAddress() const                   { return 0; }
+       virtual void                                                    copyRawContent(uint8_t buffer[]) const {
+               int64_t displacement = _target->finalAddress() - this->finalAddress() - 4;
+               if ( _target->contentType() == ld::Atom::typeBranchIsland ) {
+                       // an ARM branch can branch farther than a thumb branch.  The branch
+                       // island generation was conservative and put islands every thumb
+                       // branch distance apart.  Check to see if this is a an island
+                       // hopping branch that could be optimized to go directly to target.
+                       int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 4;
+                       if ( (skipToFinalDisplacement < 16777214) && (skipToFinalDisplacement > (-16777216LL)) ) {
+                               // can skip branch island and jump straight to target
+                               if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", 
+                                                                                       _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress());
+                               displacement = skipToFinalDisplacement;
+                       }
+                       else {
+                               // ultimate target is too far for thumb2 branch, jump to island
+                               if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", 
+                                                                                       _target->name(), _finalTarget.atom->finalAddress());
+                       }
+               }
+               // The instruction is really two instructions:
+               // The lower 16 bits are the first instruction, which contains the high
+               //   11 bits of the displacement.
+               // The upper 16 bits are the second instruction, which contains the low
+               //   11 bits of the displacement, as well as differentiating bl and blx.
+               uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
+               uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
+               uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
+               uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
+               uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
+               uint32_t j1 = (i1 == s);
+               uint32_t j2 = (i2 == s);
+               uint32_t opcode = 0x9000F000;
+               uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
+               uint32_t firstDisp = (s << 10) | imm10;
+               uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp;
+               //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n",
+               //      s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName());
+               OSWriteLittleInt32(buffer, 0, newInstruction);
+       }
+       virtual void                                                    setScope(Scope)                                 { }
+
+private:
+       const char*                                                             _name;
+       const ld::Atom*                                                 _target;
+       TargetAndOffset                                                 _finalTarget;
+};
+
+
+class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom {
+public:
+                                                                                       NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget)
+                               : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                       ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, 
+                                                       ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), 
+                               _name(nm),
+                               _target(target),
+                               _finalTarget(finalTarget) { }
+
+       virtual const ld::File*                                 file() const                                    { return NULL; }
+       virtual bool                                                    translationUnitSource(const char** dir, const char**) const 
+                                                                                                                                                       { return false; }
+       virtual const char*                                             name() const                                    { return _name; }
+       virtual uint64_t                                                size() const                                    { return 8; }
+       virtual uint64_t                                                objectAddress() const                   { return 0; }
+       virtual void                                                    copyRawContent(uint8_t buffer[]) const {
+               // There is no large displacement thumb1 branch instruction.
+               // Instead use ARM instructions that can jump to thumb.
+               // we use a 32-bit displacement, so we can directly jump to final target which means no island hopping
+               uint32_t targetAddr = _finalTarget.atom->finalAddress();
+               if ( _finalTarget.atom->isThumb() )
+                       targetAddr |= 1;
+               if (_s_log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n",
+                                                                       _target->name(), _finalTarget.atom->finalAddress());
+               OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004);  //      ldr     pc, [pc, #-4]
+               OSWriteLittleInt32(&buffer[4], 0, targetAddr);  //      .long target-this               
+       }
+       virtual void                                                    setScope(Scope)                                 { }
+
+private:
+       const char*                                                             _name;
+       const ld::Atom*                                                 _target;
+       TargetAndOffset                                                 _finalTarget;
+};
+
+
+static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget)
+{
+       char* name;
+       if ( finalTarget.offset == 0 ) {
+               if ( islandRegion == 0 )
+                       asprintf(&name, "%s.island", finalTarget.atom->name());
+               else
+                       asprintf(&name, "%s.island.%d", finalTarget.atom->name(), islandRegion+1);
+       }
+       else {
+               asprintf(&name, "%s_plus_%d.island.%d", finalTarget.atom->name(), finalTarget.offset, islandRegion);
+       }
+
+       switch ( kind ) {
+               case ld::Fixup::kindStorePPCBranch24:
+               case ld::Fixup::kindStoreTargetAddressPPCBranch24:
+                       return new PPCBranchIslandAtom(name, nextTarget, finalTarget);
+                       break;
+               case ld::Fixup::kindStoreARMBranch24:
+               case ld::Fixup::kindStoreThumbBranch22:
+               case ld::Fixup::kindStoreTargetAddressARMBranch24:
+               case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+                       if ( finalTarget.atom->isThumb() ) {
+                               if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) {
+                                       return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget);
+                               }
+                               else if ( opts.outputSlidable() ) {
+                                       return new ARMtoThumb1BranchIslandAtom(name, nextTarget, finalTarget);
+                               }
+                               else {
+                                       return new NoPicARMtoThumbMBranchIslandAtom(name, nextTarget, finalTarget);
+                               }
+                       }
+                       else {
+                               return new ARMtoARMBranchIslandAtom(name, nextTarget, finalTarget);
+                       }
+                       break;
+               default:
+                       assert(0 && "unexpected branch kind");
+                       break;
+       }
+       return NULL;
+}
+
+
+static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool seenThumbBranch)
+{
+       switch ( opts.architecture() ) {
+               case CPU_TYPE_POWERPC:
+               case CPU_TYPE_POWERPC64:
+                       return 16000000;
+                       break;
+               case CPU_TYPE_ARM:
+                       if ( ! seenThumbBranch )
+                               return 32000000;  // ARM can branch +/- 32MB
+                       else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) 
+                               return 16000000;  // thumb2 can branch +/- 16MB
+                       else
+                               return  4000000;  // thumb1 can branch +/- 4MB
+                       break;
+       }
+       assert(0 && "unexpected architecture");
+       return 0x100000000LL;
+}
+
+
+static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBranch)
+{
+       switch ( opts.architecture() ) {
+               case CPU_TYPE_POWERPC:
+               case CPU_TYPE_POWERPC64:
+                               return 14*1024*1024;
+                       break;
+               case CPU_TYPE_ARM:
+                       if ( ! seenThumbBranch )
+                               return 30*1024*1024;    // 2MB of branch islands per 32MB
+                       else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) 
+                               return 14*1024*1024;    // 2MB of branch islands per 16MB
+                       else
+                               return 3500000;                 // 0.5MB of branch islands per 4MB
+                       break;
+       }
+       assert(0 && "unexpected architecture");
+       return 0x100000000LL;
+}
+
+
+//
+// PowerPC can do PC relative branches as far as +/-16MB.
+// If a branch target is >16MB then we insert one or more
+// "branch islands" between the branch and its target that
+// allows island hopping to the target.
+//
+// Branch Island Algorithm
+//
+// If the __TEXT segment < 16MB, then no branch islands needed
+// Otherwise, every 14MB into the __TEXT segment a region is
+// added which can contain branch islands.  Every out-of-range
+// bl instruction is checked.  If it crosses a region, an island
+// is added to that region with the same target and the bl is
+// adjusted to target the island instead.
+//
+// In theory, if too many islands are added to one region, it
+// could grow the __TEXT enough that other previously in-range
+// bl branches could be pushed out of range.  We reduce the
+// probability this could happen by placing the ranges every
+// 14MB which means the region would have to be 2MB (512,000 islands)
+// before any branches could be pushed out of range.
+//
+
+void doPass(const Options& opts, ld::Internal& state)
+{      
+       // only make branch islands in final linked images
+       if ( opts.outputKind() == Options::kObjectFile )
+               return;
+
+       // only PowerPC and ARM need branch islands
+       switch ( opts.architecture() ) {
+               case CPU_TYPE_POWERPC:
+               case CPU_TYPE_POWERPC64:
+               case CPU_TYPE_ARM:
+                       break;
+               default:
+                       return;
+       }
+       
+       // scan to find __text section
+       ld::Internal::FinalSection* textSection = NULL;
+       for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+               ld::Internal::FinalSection* sect = *sit;
+               if ( strcmp(sect->sectionName(), "__text") == 0 )
+                       textSection = sect;
+       }
+       if ( textSection == NULL )
+               return;
+       
+       // assign section offsets to each atom in __text section, watch for thumb branches, and find total size
+       const bool isARM = (opts.architecture() == CPU_TYPE_ARM);
+       bool hasThumbBranches = false;
+       uint64_t offset = 0;
+       for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin();  ait != textSection->atoms.end(); ++ait) {
+               const ld::Atom* atom = *ait;
+               // check for thumb branches
+               if ( isARM && ~hasThumbBranches ) {
+                       for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+                               switch ( fit->kind ) {
+                                       case ld::Fixup::kindStoreThumbBranch22:
+                                       case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+                                               hasThumbBranches = true;
+                                               break;
+                                       default:
+                                               break;
+                               }
+                       }
+               }
+               // align atom
+               ld::Atom::Alignment atomAlign = atom->alignment();
+               uint64_t atomAlignP2 = (1 << atomAlign.powerOf2);
+               uint64_t currentModulus = (offset % atomAlignP2);
+               if ( currentModulus != atomAlign.modulus ) {
+                       if ( atomAlign.modulus > currentModulus )
+                               offset += atomAlign.modulus-currentModulus;
+                       else
+                               offset += atomAlign.modulus+atomAlignP2-currentModulus;         
+               }
+               (const_cast<ld::Atom*>(atom))->setSectionOffset(offset);
+               offset += atom->size();
+       }
+       uint64_t totalTextSize = offset;
+       if ( totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches) )
+               return;
+       if (_s_log) fprintf(stderr, "ld:  __text section size=%llu, might need branch islands\n", totalTextSize);
+       
+       // figure out how many regions of branch islands will be needed
+       const uint32_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section
+       const int kIslandRegionsCount = totalTextSize / kBetweenRegions;
+       typedef std::map<TargetAndOffset,const ld::Atom*, TargetAndOffsetComparor> AtomToIsland;
+    AtomToIsland* regionsMap[kIslandRegionsCount];
+       std::vector<const ld::Atom*>* regionsIslands[kIslandRegionsCount];
+       for(int i=0; i < kIslandRegionsCount; ++i) {
+               regionsMap[i] = new AtomToIsland();
+               regionsIslands[i] = new std::vector<const ld::Atom*>();
+       }
+       unsigned int islandCount = 0;
+       if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount);
+       
+       // create islands for branches in __text that are out of range
+       for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) {
+               const ld::Atom* atom = *ait;
+               const ld::Atom* target = NULL;
+               uint64_t addend = 0;
+               ld::Fixup* fixupWithTarget = NULL;
+               for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+                       if ( fit->firstInCluster() ) {
+                               target = NULL;
+                               fixupWithTarget = NULL;
+                               addend = 0;
+                       }
+                       switch ( fit->binding ) {
+                               case ld::Fixup::bindingNone:
+                               case ld::Fixup::bindingByNameUnbound:
+                                       break;
+                               case ld::Fixup::bindingByContentBound:
+                               case ld::Fixup::bindingDirectlyBound:
+                                       target = fit->u.target;
+                                       fixupWithTarget = fit;
+                                       break;
+                               case ld::Fixup::bindingsIndirectlyBound:
+                                       target = state.indirectBindingTable[fit->u.bindingIndex];
+                                       fixupWithTarget = fit;
+                                       break;
+                       }
+                       bool haveBranch = false;
+                       switch (fit->kind) {
+                               case ld::Fixup::kindAddAddend:
+                                       addend = fit->u.addend;
+                                       break;
+                               case ld::Fixup::kindStorePPCBranch24:
+                               case ld::Fixup::kindStoreTargetAddressPPCBranch24:
+                               case ld::Fixup::kindStoreARMBranch24:
+                               case ld::Fixup::kindStoreThumbBranch22:
+                               case ld::Fixup::kindStoreTargetAddressARMBranch24:
+                               case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+                                       haveBranch = true;
+                                       break;
+                default:
+                    break;   
+                       }
+                       if ( haveBranch ) {
+                               int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom;
+                               int64_t dstAddr = target->sectionOffset() + addend;
+                               if ( target->section().type() == ld::Section::typeStub )
+                                       dstAddr = totalTextSize;
+                               int64_t displacement = dstAddr - srcAddr;
+                               TargetAndOffset finalTargetAndOffset = { target, addend };
+                               const int64_t kBranchLimit = kBetweenRegions;
+                               if ( displacement > kBranchLimit ) {
+                                       // create forward branch chain
+                                       const ld::Atom* nextTarget = target;
+                                       for (int i=kIslandRegionsCount-1; i >=0 ; --i) {
+                                               AtomToIsland* region = regionsMap[i];
+                                               int64_t islandRegionAddr = kBetweenRegions * (i+1);
+                                               if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { 
+                                                       AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
+                                                       if ( pos == region->end() ) {
+                                                               ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset);
+                                                               (*region)[finalTargetAndOffset] = island;
+                                                               if (_s_log) fprintf(stderr, "added island %s to region %d for %s\n", island->name(), i, atom->name());
+                                                               regionsIslands[i]->push_back(island);
+                                                               ++islandCount;
+                                                               nextTarget = island;
+                                                       }
+                                                       else {
+                                                               nextTarget = pos->second;
+                                                       }
+                                               }
+                                       }
+                                       if (_s_log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->name(), target->name(), atom->name());
+                                       fixupWithTarget->u.target = nextTarget;
+                                       fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound;
+                               }
+                               else if ( displacement < (-kBranchLimit) ) {
+                                       // create back branching chain
+                                       const ld::Atom* prevTarget = target;
+                                       for (int i=0; i < kIslandRegionsCount ; ++i) {
+                                               AtomToIsland* region = regionsMap[i];
+                                               int64_t islandRegionAddr = kBetweenRegions * (i+1);
+                                               if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) {
+                                                       AtomToIsland::iterator pos = region->find(finalTargetAndOffset);
+                                                       if ( pos == region->end() ) {
+                                                               ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset);
+                                                               (*region)[finalTargetAndOffset] = island;
+                                                               if (_s_log) fprintf(stderr, "added back island %s to region %d for %s\n", island->name(), i, atom->name());
+                                                               regionsIslands[i]->push_back(island);
+                                                               ++islandCount;
+                                                               prevTarget = island;
+                                                       }
+                                                       else {
+                                                               prevTarget = pos->second;
+                                                       }
+                                               }
+                                       }
+                                       if (_s_log) fprintf(stderr, "using back island %s for %s\n", prevTarget->name(), atom->name());
+                                       fixupWithTarget->u.target = prevTarget;
+                                       fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound;
+                               }
+                       }
+               }
+       }
+
+
+       // insert islands into __text section and adjust section offsets
+       if ( islandCount > 0 ) {
+               if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount);
+               std::vector<const ld::Atom*> newAtomList;
+               newAtomList.reserve(textSection->atoms.size()+islandCount);
+               uint64_t islandRegionAddr = kBetweenRegions;;
+               int regionIndex = 0;
+               for (std::vector<const ld::Atom*>::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) {
+                       const ld::Atom* atom = *it;
+                       if ( (atom->sectionOffset()+atom->size()) > islandRegionAddr ) {
+                               std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex];
+                               for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
+                                       const ld::Atom* islandAtom = *rit;
+                                       newAtomList.push_back(islandAtom);
+                                       if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name());
+                               }
+                               ++regionIndex;
+                               islandRegionAddr += kBetweenRegions;
+                       }
+                       newAtomList.push_back(atom);
+               }
+               // put any remaining islands at end of __text section
+               if ( regionIndex < kIslandRegionsCount ) {
+                       std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex];
+                       for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) {
+                               const ld::Atom* islandAtom = *rit;
+                               newAtomList.push_back(islandAtom);
+                               if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name());
+                       }
+               }
+               // swap in new list of atoms for __text section
+               textSection->atoms.clear();
+               textSection->atoms = newAtomList;
+       }
+
+}
+
+
+} // namespace branch_island
+} // namespace passes 
+} // namespace ld