// // The tail-call optimzation may result in a function ending in a jump (b) // to another functions. At compile time the compiler does not know // if the target of the jump will be in the same mode (arm vs thumb). // The arm/thumb instruction set has a way to change modes in a bl(x) // insruction, but no instruction to change mode in a jump (b) instruction. // In those rare cases, the linker needs to insert a shim of code to // make the mode switch. // void doPass(const Options& opts, ld::Internal& state) { // only make branch shims in final linked images if ( opts.outputKind() == Options::kObjectFile ) return; // only ARM need branch islands if ( opts.architecture() != CPU_TYPE_ARM ) return; const bool makingKextBundle = (opts.outputKind() == Options::kKextBundle); // scan all sections for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; std::map<const Atom*, const Atom*> atomToThumbMap; std::map<const Atom*, const Atom*> thumbToAtomMap; std::vector<const Atom*> shims; // scan section for branch instructions that need to switch mode for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; const ld::Atom* target = NULL; bool targetIsProxy; for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { switch ( fit->kind ) { case ld::Fixup::kindStoreTargetAddressThumbBranch22: extractTarget(fit, state, &target); targetIsProxy = (target->definition() == ld::Atom::definitionProxy); if ( ! target->isThumb() ) { const uint8_t* fixUpLocation = atom->rawContentPointer(); // <rdar://problem/9544194> don't try to scan atom for branches if atom unwilling to supply raw content if ( fixUpLocation == NULL ) break; fixUpLocation += fit->offsetInAtom; uint32_t instruction = *((uint32_t*)fixUpLocation); bool is_b = ((instruction & 0xD000F800) == 0x9000F000); // need shim for branch from thumb to arm, or for call to function outside kext if ( is_b || (targetIsProxy && makingKextBundle) ) { if ( _s_log ) fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); const Atom* shim = NULL; std::map<const Atom*, const Atom*>::iterator pos = thumbToAtomMap.find(target); if ( pos == thumbToAtomMap.end() ) { if ( opts.archSupportsThumb2() ) { // <rdar://problem/9116044> make long-branch style shims for arm kexts if ( makingKextBundle && opts.allowTextRelocs() ) shim = new NoPICThumb2ToArmShimAtom(target, *sect); else shim = new Thumb2ToArmShimAtom(target, *sect); } else { shim = new Thumb1ToArmShimAtom(target, *sect); } shims.push_back(shim); thumbToAtomMap[target] = shim; } else { shim = pos->second; } fit->binding = ld::Fixup::bindingDirectlyBound; fit->u.target = shim; } } break; case ld::Fixup::kindStoreTargetAddressARMBranch24: extractTarget(fit, state, &target); targetIsProxy = (target->definition() == ld::Atom::definitionProxy); if ( target->isThumb() || (targetIsProxy && makingKextBundle) ) { const uint8_t* fixUpLocation = atom->rawContentPointer(); // <rdar://problem/9544194> don't try to scan atom for branches if atom unwilling to supply raw content if ( fixUpLocation == NULL ) break; fixUpLocation += fit->offsetInAtom; uint32_t instruction = *((uint32_t*)fixUpLocation); bool is_b = ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000); // need shim for branch from arm to thumb, or for call to function outside kext if ( is_b || (targetIsProxy && makingKextBundle) ) { if ( _s_log ) fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); const Atom* shim = NULL; std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target); if ( pos == atomToThumbMap.end() ) { // <rdar://problem/9116044> make long-branch style shims for arm kexts if ( makingKextBundle && opts.allowTextRelocs() ) shim = new NoPICARMtoThumbShimAtom(target, *sect); else shim = new ARMtoThumbShimAtom(target, *sect); shims.push_back(shim); atomToThumbMap[target] = shim; } else { shim = pos->second; } fit->binding = ld::Fixup::bindingDirectlyBound; fit->u.target = shim; } } break; //case ld::Fixup::kindStoreARMBranch24: //case ld::Fixup::kindStoreThumbBranch22: // Note: these fixups will only be seen if the the b/bl is to a symbol plus addend // for now we don't handle making shims. If a shim is needed there will // be an error later. // break; default: break; } } } // append all new shims to end of __text sect->atoms.insert(sect->atoms.end(), shims.begin(), shims.end()); } }