vassert(is_imm32_to_iregNo_EXACTLY2(
p, /*r*/12, (UInt)Ptr_to_ULong(disp_cp_chain_me_EXPECTED)));
vassert(p[2] == 0xE12FFF3C);
- /* And what we want to change it to is:
- movw r12, lo16(place_to_jump_to)
- movt r12, hi16(place_to_jump_to)
- bx r12
- viz
- <8 bytes generated by imm32_to_iregNo_EXACTLY2>
- E1 2F FF 1C
- The replacement has the same length as the original.
+ /* And what we want to change it to is either:
+ (general case)
+ movw r12, lo16(place_to_jump_to)
+ movt r12, hi16(place_to_jump_to)
+ bx r12
+ viz
+ <8 bytes generated by imm32_to_iregNo_EXACTLY2>
+ E1 2F FF 1C
+ ---OR---
+ in the case where the displacement falls within 26 bits
+ b disp24; undef; undef
+ viz
+ EA <3 bytes == disp24>
+ FF 00 00 00
+ FF 00 00 00
+
+ In both cases the replacement has the same length as the original.
+ To remain sane & verifiable,
+ (1) limit the displacement for the short form to
+ (say) +/- 30 million, so as to avoid wraparound
+ off-by-ones
+ (2) even if the short form is applicable, once every (say)
+ 1024 times use the long form anyway, so as to maintain
+ verifiability
*/
- (void)imm32_to_iregNo_EXACTLY2(
- p, /*r*/12, (UInt)Ptr_to_ULong(place_to_jump_to));
- p[2] = 0xE12FFF1C;
+
+ /* This is the delta we need to put into a B insn. It's relative
+ to the start of the next-but-one insn, hence the -8. */
+ Long delta = (Long)((UChar*)place_to_jump_to - (UChar*)p) - (Long)8;
+ Bool shortOK = delta >= -30*1000*1000 && delta < 30*1000*1000;
+ vassert(0 == (delta & (Long)3));
+
+ static UInt shortCTR = 0; /* DO NOT MAKE NON-STATIC */
+ if (shortOK) {
+ shortCTR++; // thread safety bleh
+ if (0 == (shortCTR & 0x3FF)) {
+ shortOK = False;
+ if (0)
+ vex_printf("QQQ chainXDirect_ARM: shortCTR = %u, "
+ "using long form\n", shortCTR);
+ }
+ }
+
+ /* And make the modifications. */
+ if (shortOK) {
+ Int simm24 = (Int)(delta >> 2);
+ vassert(simm24 == ((simm24 << 8) >> 8));
+ p[0] = 0xEA000000 | (simm24 & 0x00FFFFFF);
+ p[1] = 0xFF000000;
+ p[2] = 0xFF000000;
+ } else {
+ (void)imm32_to_iregNo_EXACTLY2(
+ p, /*r*/12, (UInt)Ptr_to_ULong(place_to_jump_to));
+ p[2] = 0xE12FFF1C;
+ }
+
VexInvalRange vir = {(HWord)p, 12};
return vir;
}
void* disp_cp_chain_me )
{
/* What we're expecting to see is:
- movw r12, lo16(place_to_jump_to_EXPECTED)
- movt r12, lo16(place_to_jump_to_EXPECTED)
- bx r12
- viz
- <8 bytes generated by imm32_to_iregNo_EXACTLY2>
- E1 2F FF 1C
+ (general case)
+ movw r12, lo16(place_to_jump_to_EXPECTED)
+ movt r12, lo16(place_to_jump_to_EXPECTED)
+ bx r12
+ viz
+ <8 bytes generated by imm32_to_iregNo_EXACTLY2>
+ E1 2F FF 1C
+ ---OR---
+ in the case where the displacement falls within 26 bits
+ b disp24; undef; undef
+ viz
+ EA <3 bytes == disp24>
+ FF 00 00 00
+ FF 00 00 00
*/
UInt* p = (UInt*)place_to_unchain;
vassert(0 == (3 & (HWord)p));
- vassert(is_imm32_to_iregNo_EXACTLY2(
- p, /*r*/12, (UInt)Ptr_to_ULong(place_to_jump_to_EXPECTED)));
- vassert(p[2] == 0xE12FFF1C);
+
+ Bool valid = False;
+ if (is_imm32_to_iregNo_EXACTLY2(
+ p, /*r*/12, (UInt)Ptr_to_ULong(place_to_jump_to_EXPECTED))
+ && p[2] == 0xE12FFF1C) {
+ valid = True; /* it's the long form */
+ if (0)
+ vex_printf("QQQ unchainXDirect_ARM: found long form\n");
+ } else
+ if ((p[0] >> 24) == 0xEA && p[1] == 0xFF000000 && p[2] == 0xFF000000) {
+ /* It's the short form. Check the displacement is right. */
+ Int simm24 = p[0] & 0x00FFFFFF;
+ simm24 <<= 8; simm24 >>= 8;
+ if ((UChar*)p + (simm24 << 2) + 8 == (UChar*)place_to_jump_to_EXPECTED) {
+ valid = True;
+ if (0)
+ vex_printf("QQQ unchainXDirect_ARM: found short form\n");
+ }
+ }
+ vassert(valid);
+
/* And what we want to change it to is:
movw r12, lo16(disp_cp_chain_me)
movt r12, hi16(disp_cp_chain_me)