+ }
+ else {
+ if ( !thumbTarget )
+ throwf("don't know how to convert branch instruction %x referencing %s to bx",
+ instruction, referenceTargetAtomName(state, fit));
+ instruction = 0x9000F000; // keep b
+ }
+ uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
+ uint32_t firstDisp = (s << 10) | imm10;
+ newInstruction = instruction | (nextDisp << 16) | firstDisp;
+ //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, instruction=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n",
+ // s, j1, j2, imm10, imm11, instruction, firstDisp, nextDisp, newInstruction, delta, atom->name(), toTarget->name());
+ set32LE(fixUpLocation, newInstruction);
+ }
+ else {
+ // 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 firstDisp = (uint32_t)(delta >> 12) & 0x7FF;
+ uint32_t nextDisp = (uint32_t)(delta >> 1) & 0x7FF;
+ if ( is_bl && !thumbTarget ) {
+ instruction = 0xE800F000;
+ }
+ else if ( is_blx && thumbTarget ) {
+ instruction = 0xF800F000;
+ }
+ else if ( is_b ) {
+ instruction = 0x9000F000; // keep b
+ if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) {
+ throwf("armv6 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s",
+ referenceTargetAtomName(state, fit), atom->name());
+ }
+ }
+ else {
+ instruction = instruction & 0xF800F800;
+ }
+ newInstruction = instruction | (nextDisp << 16) | firstDisp;
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreARMLow16:
+ {
+ uint32_t imm4 = (accumulator & 0x0000F000) >> 12;
+ uint32_t imm12 = accumulator & 0x00000FFF;
+ instruction = get32LE(fixUpLocation);
+ newInstruction = (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreARMHigh16:
+ {
+ uint32_t imm4 = (accumulator & 0xF0000000) >> 28;
+ uint32_t imm12 = (accumulator & 0x0FFF0000) >> 16;
+ instruction = get32LE(fixUpLocation);
+ newInstruction = (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreThumbLow16:
+ {
+ uint32_t imm4 = (accumulator & 0x0000F000) >> 12;
+ uint32_t i = (accumulator & 0x00000800) >> 11;
+ uint32_t imm3 = (accumulator & 0x00000700) >> 8;
+ uint32_t imm8 = accumulator & 0x000000FF;
+ instruction = get32LE(fixUpLocation);
+ newInstruction = (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreThumbHigh16:
+ {
+ uint32_t imm4 = (accumulator & 0xF0000000) >> 28;
+ uint32_t i = (accumulator & 0x08000000) >> 27;
+ uint32_t imm3 = (accumulator & 0x07000000) >> 24;
+ uint32_t imm8 = (accumulator & 0x00FF0000) >> 16;
+ instruction = get32LE(fixUpLocation);
+ newInstruction = (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreTargetAddressARM64Branch26:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindStoreARM64Branch26 case
+ case ld::Fixup::kindStoreARM64Branch26:
+ if ( fit->contentAddendOnly )
+ delta = accumulator;
+ else
+ delta = accumulator - (atom->finalAddress() + fit->offsetInAtom);
+ rangeCheckARM64Branch26(delta, state, atom, fit);
+ instruction = get32LE(fixUpLocation);
+ newInstruction = (instruction & 0xFC000000) | ((uint32_t)(delta >> 2) & 0x03FFFFFF);
+ set32LE(fixUpLocation, newInstruction);
+ break;
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64Page21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindStoreARM64Branch26 case
+ case ld::Fixup::kindStoreARM64GOTLeaPage21:
+ case ld::Fixup::kindStoreARM64GOTLoadPage21:
+ case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21:
+ case ld::Fixup::kindStoreARM64Page21:
+ {
+ // the ADRP instruction adds the imm << 12 to the page that the pc is on
+ if ( fit->contentAddendOnly )
+ delta = 0;
+ else
+ delta = (accumulator & (-4096)) - ((atom->finalAddress() + fit->offsetInAtom) & (-4096));
+ rangeCheckARM64Page21(delta, state, atom, fit);
+ instruction = get32LE(fixUpLocation);
+ uint32_t immhi = (delta >> 9) & (0x00FFFFE0);
+ uint32_t immlo = (delta << 17) & (0x60000000);
+ newInstruction = (instruction & 0x9F00001F) | immlo | immhi;
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindAddressARM64PageOff12 case
+ case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
+ case ld::Fixup::kindStoreARM64GOTLoadPageOff12:
+ case ld::Fixup::kindStoreARM64PageOff12:
+ {
+ uint32_t offset = accumulator & 0x00000FFF;
+ instruction = get32LE(fixUpLocation);
+ // LDR/STR instruction have implicit scale factor, need to compensate for that
+ if ( instruction & 0x08000000 ) {
+ uint32_t implictShift = ((instruction >> 30) & 0x3);
+ switch ( implictShift ) {
+ case 0:
+ if ( (instruction & 0x04800000) == 0x04800000 ) {
+ // vector and byte LDR/STR have same "size" bits, need to check other bits to differenciate
+ implictShift = 4;
+ if ( (offset & 0xF) != 0 ) {
+ throwf("128-bit LDR/STR not 16-byte aligned: from %s (0x%08llX) to %s (0x%08llX)",
+ atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit),
+ addressOf(state, fit, &toTarget));
+ }
+ }
+ break;
+ case 1:
+ if ( (offset & 0x1) != 0 ) {
+ throwf("16-bit LDR/STR not 2-byte aligned: from %s (0x%08llX) to %s (0x%08llX)",
+ atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit),
+ addressOf(state, fit, &toTarget));
+ }
+ break;
+ case 2:
+ if ( (offset & 0x3) != 0 ) {
+ throwf("32-bit LDR/STR not 4-byte aligned: from %s (0x%08llX) to %s (0x%08llX)",
+ atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit),
+ addressOf(state, fit, &toTarget));
+ }
+ break;
+ case 3:
+ if ( (offset & 0x7) != 0 ) {
+ throwf("64-bit LDR/STR not 8-byte aligned: from %s (0x%08llX) to %s (0x%08llX)",
+ atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit),
+ addressOf(state, fit, &toTarget));
+ }
+ break;
+ }
+ // compensate for implicit scale
+ offset >>= implictShift;
+ }
+ if ( fit->contentAddendOnly )
+ offset = 0;
+ uint32_t imm12 = offset << 10;
+ newInstruction = (instruction & 0xFFC003FF) | imm12;
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindStoreARM64GOTLoadPage21 case
+ case ld::Fixup::kindStoreARM64GOTLeaPageOff12:
+ {
+ // GOT entry was optimized away, change LDR instruction to a ADD
+ instruction = get32LE(fixUpLocation);
+ if ( (instruction & 0xFFC00000) != 0xF9400000 )
+ throwf("GOT load reloc does not point to a LDR instruction in %s", atom->name());
+ uint32_t offset = accumulator & 0x00000FFF;
+ uint32_t imm12 = offset << 10;
+ newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF);
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindStoreARM64TLVPLeaPageOff12 case
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12:
+ {
+ // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD
+ instruction = get32LE(fixUpLocation);
+ if ( (instruction & 0xFFC00000) != 0xF9400000 )
+ throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name());
+ uint32_t offset = accumulator & 0x00000FFF;
+ uint32_t imm12 = offset << 10;
+ newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF);
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreARM64PointerToGOT:
+ set64LE(fixUpLocation, accumulator);
+ break;
+ case ld::Fixup::kindStoreARM64PCRelToGOT:
+ if ( fit->contentAddendOnly )
+ delta = accumulator;
+ else
+ delta = accumulator - (atom->finalAddress() + fit->offsetInAtom);
+ set32LE(fixUpLocation, delta);
+ break;
+#endif
+ }
+ }
+
+#if SUPPORT_ARCH_arm64
+ // after all fixups are done on atom, if there are potential optimizations, do those
+ if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) {
+ // fill in second part of usedByHints map, so we can see the target of fixups that might be optimized
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindLinkerOptimizationHint:
+ case ld::Fixup::kindNoneFollowOn:
+ case ld::Fixup::kindNoneGroupSubordinate:
+ case ld::Fixup::kindNoneGroupSubordinateFDE:
+ case ld::Fixup::kindNoneGroupSubordinateLSDA:
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ break;
+ default:
+ if ( fit->firstInCluster() ) {
+ std::map<uint32_t, const Fixup*>::iterator pos = usedByHints.find(fit->offsetInAtom);
+ if ( pos != usedByHints.end() ) {
+ assert(pos->second == NULL && "two fixups in same hint location");
+ pos->second = fit;
+ //fprintf(stderr, "setting %s usedByHints[0x%04X], kind = %d\n", atom->name(), fit->offsetInAtom, fit->kind);
+ }
+ }
+ }
+ }
+
+ // apply hints pass 1
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint )
+ continue;
+ InstructionInfo infoA;
+ InstructionInfo infoB;
+ InstructionInfo infoC;
+ InstructionInfo infoD;
+ LoadStoreInfo ldrInfoB, ldrInfoC;
+ AddInfo addInfoB;
+ AdrpInfo adrpInfoA;
+ bool usableSegment;
+ bool targetFourByteAligned;
+ bool literalableSize, isADRP, isADD, isLDR, isSTR;
+ //uint8_t loadSize, destReg;
+ //uint32_t scaledOffset;
+ //uint32_t imm12;
+ ld::Fixup::LOH_arm64 alt;
+ alt.addend = fit->u.addend;
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA);
+ if ( alt.info.count > 0 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB);
+ if ( alt.info.count > 1 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC);
+ if ( alt.info.count > 2 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD);
+
+ switch ( alt.info.kind ) {
+ case LOH_ARM64_ADRP_ADRP:
+ // processed in pass 2 beacuse some ADRP may have been removed
+ break;
+ case LOH_ARM64_ADRP_LDR:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg);
+ LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF));
+ literalableSize = ( (ldrInfoB.size != 1) && (ldrInfoB.size != 2) );
+ targetFourByteAligned = ( (infoA.targetAddress & 0x3) == 0 );
+ if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr at 0x%08llX not transformed, isLDR=%d, literalableSize=%d, inRange=%d, usableSegment=%d, scaledOffset=%d\n",
+ infoB.instructionAddress, isLDR, literalableSize, withinOneMeg(infoB.instructionAddress, infoA.targetAddress), usableSegment, ldrInfoB.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD_LDR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ targetFourByteAligned = ( ((infoB.targetAddress+ldrInfoC.offset) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can do T1 transformation to LDR literal
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress+ldrInfoC.offset, infoC.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
+ }
+ }
+ else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can to T4 transformation and turn ADRP/ADD into ADR
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF);
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoB.instructionAddress);
+ }
+ else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transformation by merging ADD into LD
+ // Leave ADRP as-is
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset += addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX could not be transformed, loadSize=%d, literalableSize=%d, inRange=%d, usableSegment=%d, targetFourByteAligned=%d, imm12=%d\n",
+ infoC.instructionAddress, ldrInfoC.size, literalableSize, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, targetFourByteAligned, ldrInfoC.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) {
+ // can do T4 transformation and use ADR
+ set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add at 0x%08llX transformed to ADR\n", infoB.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add at 0x%08llX not transformed, isAdd=%d, inRange=%d, usableSegment=%d\n",
+ infoB.instructionAddress, isADD, withinOneMeg(infoA.targetAddress, infoA.instructionAddress), usableSegment);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_LDR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(ldrInfoC.offset == 0);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ if ( isLDR ) {
+ // target of GOT is external
+ LOH_ASSERT(ldrInfoB.size == 8);
+ LOH_ASSERT(!ldrInfoB.isFloat);
+ LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
+ //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T5 transformed to LDR literal of GOT plus LDR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX no optimization done\n", infoC.instructionAddress);
+ }
+ }
+ else if ( isADD ) {
+ // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target
+ LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) {
+ // can do T1 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
+ }
+ else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress);
+ }
+ }
+ else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) {
+ // can do T2 transform
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.baseReg = adrpInfoA.destReg;
+ ldrInfoC.offset = addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ // T3 transform already done by ld::passes:got:doPass()
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T3 transformed to ADRP/ADD/LDR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX not ADD or LDR\n", infoC.instructionAddress);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD_STR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
+ LOH_ASSERT(isSTR);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can to T4 transformation and turn ADRP/ADD into ADR
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF);
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX T4 transformed to ADR/STR\n", infoB.instructionAddress);
+ }
+ else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transformation by merging ADD into STR
+ // Leave ADRP as-is
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset += addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX T2 transformed to ADRP/STR \n", infoC.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX could not be transformed, loadSize=%d, inRange=%d, usableSegment=%d, imm12=%d\n",
+ infoC.instructionAddress, ldrInfoC.size, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, ldrInfoC.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_STR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
+ LOH_ASSERT(isSTR);
+ LOH_ASSERT(ldrInfoC.offset == 0);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ if ( isLDR ) {
+ // target of GOT is external
+ LOH_ASSERT(ldrInfoB.size == 8);
+ LOH_ASSERT(!ldrInfoB.isFloat);
+ LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T5 transformed to LDR literal of GOT plus STR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX no optimization done\n", infoC.instructionAddress);
+ }
+ }
+ else if ( isADD ) {
+ // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target
+ LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress);
+ }
+ }
+ else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transform
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.baseReg = adrpInfoA.destReg;
+ ldrInfoC.offset = addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ // T3 transform already done by ld::passes:got:doPass()
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T3 transformed to ADRP/ADD/STR\n", infoC.instructionAddress);
+ }
+ }
+ }