Ejemplo n.º 1
0
//
// 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());
	}
}