PC-relative loads from dex cache arrays for arm.
Change-Id: Ic25df4b51a901ff1d2ca356b5eec71d4acc5d9b7
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 4267743..b17cbca 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -48,22 +48,30 @@
uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
value |= 0xf000d000; // BL
- uint8_t* addr = &(*code)[literal_offset];
// Check that we're just overwriting an existing BL.
- DCHECK_EQ(addr[1] & 0xf8, 0xf0);
- DCHECK_EQ(addr[3] & 0xd0, 0xd0);
+ DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000);
// Write the new BL.
- addr[0] = (value >> 16) & 0xff;
- addr[1] = (value >> 24) & 0xff;
- addr[2] = (value >> 0) & 0xff;
- addr[3] = (value >> 8) & 0xff;
+ SetInsn32(code, literal_offset, value);
}
-void Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "Unexpected relative dex cache array patch.";
+void Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ uint32_t literal_offset = patch.LiteralOffset();
+ uint32_t pc_literal_offset = patch.PcInsnOffset();
+ uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */;
+ uint32_t diff = target_offset - pc_base;
+
+ uint32_t insn = GetInsn32(code, literal_offset);
+ DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u); // MOVW/MOVT, unpatched (imm16 == 0).
+ uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu);
+ uint32_t imm4 = (diff16 >> 12) & 0xfu;
+ uint32_t imm = (diff16 >> 11) & 0x1u;
+ uint32_t imm3 = (diff16 >> 8) & 0x7u;
+ uint32_t imm8 = diff16 & 0xffu;
+ insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8;
+ SetInsn32(code, literal_offset, insn);
}
std::vector<uint8_t> Thumb2RelativePatcher::CompileThunkCode() {
@@ -80,5 +88,31 @@
return thunk_code;
}
+void Thumb2RelativePatcher::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
+ DCHECK_LE(offset + 4u, code->size());
+ DCHECK_EQ(offset & 1u, 0u);
+ uint8_t* addr = &(*code)[offset];
+ addr[0] = (value >> 16) & 0xff;
+ addr[1] = (value >> 24) & 0xff;
+ addr[2] = (value >> 0) & 0xff;
+ addr[3] = (value >> 8) & 0xff;
+}
+
+uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset) {
+ DCHECK_LE(offset + 4u, code.size());
+ DCHECK_EQ(offset & 1u, 0u);
+ const uint8_t* addr = &code[offset];
+ return
+ (static_cast<uint32_t>(addr[0]) << 16) +
+ (static_cast<uint32_t>(addr[1]) << 24) +
+ (static_cast<uint32_t>(addr[2]) << 0)+
+ (static_cast<uint32_t>(addr[3]) << 8);
+}
+
+template <typename Alloc>
+uint32_t Thumb2RelativePatcher::GetInsn32(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
+ return GetInsn32(ArrayRef<const uint8_t>(*code), offset);
+}
+
} // namespace linker
} // namespace art
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 5611303..2d474c2 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -34,6 +34,12 @@
private:
static std::vector<uint8_t> CompileThunkCode();
+ void SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value);
+ static uint32_t GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset);
+
+ template <typename Alloc>
+ static uint32_t GetInsn32(std::vector<uint8_t, Alloc>* code, uint32_t offset);
+
// PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
static constexpr int32_t kPcDisplacement = 4;
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index 3b397cc..a057a4c 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -121,6 +121,48 @@
result.push_back(static_cast<uint8_t>(bl >> 8));
return result;
}
+
+ void TestDexCachereference(uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ dex_cache_arrays_begin_ = dex_cache_arrays_begin;
+ static const uint8_t raw_code[] = {
+ 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder)
+ 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder)
+ 0x78, 0x44, // ADD r0, pc
+ };
+ constexpr uint32_t pc_insn_offset = 8u;
+ const ArrayRef<const uint8_t> code(raw_code);
+ LinkerPatch patches[] = {
+ LinkerPatch::DexCacheArrayPatch(0u, nullptr, pc_insn_offset, element_offset),
+ LinkerPatch::DexCacheArrayPatch(4u, nullptr, pc_insn_offset, element_offset),
+ };
+ AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t pc_base_offset = method1_offset + pc_insn_offset + 4u /* PC adjustment */;
+ uint32_t diff = dex_cache_arrays_begin_ + element_offset - pc_base_offset;
+ // Distribute the bits of the diff between the MOVW and MOVT:
+ uint32_t diffw = diff & 0xffffu;
+ uint32_t difft = diff >> 16;
+ uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder),
+ ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
+ ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
+ ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
+ ((diffw & 0x00ffu)); // keep imm8 at bits 0-7.
+ uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder),
+ ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
+ ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
+ ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
+ ((difft & 0x00ffu)); // keep imm8 at bits 0-7.
+ const uint8_t expected_code[] = {
+ static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
+ static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
+ static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
+ static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
+ 0x78, 0x44,
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+ }
};
const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
@@ -285,5 +327,25 @@
EXPECT_TRUE(CheckThunk(thunk_offset));
}
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm8) {
+ TestDexCachereference(0x00ff0000u, 0x00fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm3) {
+ TestDexCachereference(0x02ff0000u, 0x05fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm) {
+ TestDexCachereference(0x08ff0000u, 0x08fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceimm4) {
+ TestDexCachereference(0xd0ff0000u, 0x60fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
} // namespace linker
} // namespace art