Quick: PC-relative loads from dex cache arrays on x86.
Rewrite all PC-relative addressing on x86 and implement
PC-relative loads from dex cache arrays. Don't adjust the
base to point to the start of the method, let it point to
the anchor, i.e. the target of the "call +0" insn.
Change-Id: Ic22544a8bc0c5e49eb00a75154dc8f3ead816989
diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/compiler/linker/x86/relative_patcher_x86.cc
index 246cf11..315585d 100644
--- a/compiler/linker/x86/relative_patcher_x86.cc
+++ b/compiler/linker/x86/relative_patcher_x86.cc
@@ -16,14 +16,43 @@
#include "linker/x86/relative_patcher_x86.h"
+#include "compiled_method.h"
+
namespace art {
namespace linker {
-void X86RelativePatcher::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 X86RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ uint32_t anchor_literal_offset = patch.PcInsnOffset();
+ uint32_t literal_offset = patch.LiteralOffset();
+
+ // Check that the anchor points to pop in a "call +0; pop <reg>" sequence.
+ DCHECK_GE(anchor_literal_offset, 5u);
+ DCHECK_LT(anchor_literal_offset, code->size());
+ DCHECK_EQ((*code)[anchor_literal_offset - 5u], 0xe8u);
+ DCHECK_EQ((*code)[anchor_literal_offset - 4u], 0x00u);
+ DCHECK_EQ((*code)[anchor_literal_offset - 3u], 0x00u);
+ DCHECK_EQ((*code)[anchor_literal_offset - 2u], 0x00u);
+ DCHECK_EQ((*code)[anchor_literal_offset - 1u], 0x00u);
+ DCHECK_EQ((*code)[anchor_literal_offset] & 0xf8u, 0x58u);
+
+ // Check that the patched data contains kDummy32BitOffset.
+ constexpr int kDummy32BitOffset = 256; // Must match X86Mir2Lir::kDummy32BitOffset.
+ DCHECK_LE(literal_offset, code->size());
+ DCHECK_EQ((*code)[literal_offset + 0u], static_cast<uint8_t>(kDummy32BitOffset >> 0));
+ DCHECK_EQ((*code)[literal_offset + 1u], static_cast<uint8_t>(kDummy32BitOffset >> 8));
+ DCHECK_EQ((*code)[literal_offset + 2u], static_cast<uint8_t>(kDummy32BitOffset >> 16));
+ DCHECK_EQ((*code)[literal_offset + 3u], static_cast<uint8_t>(kDummy32BitOffset >> 24));
+
+ // Apply patch.
+ uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
+ uint32_t diff = target_offset - anchor_offset;
+ (*code)[literal_offset + 0u] = static_cast<uint8_t>(diff >> 0);
+ (*code)[literal_offset + 1u] = static_cast<uint8_t>(diff >> 8);
+ (*code)[literal_offset + 2u] = static_cast<uint8_t>(diff >> 16);
+ (*code)[literal_offset + 3u] = static_cast<uint8_t>(diff >> 24);
}
} // namespace linker
diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc
index 15ac47e..7acc330 100644
--- a/compiler/linker/x86/relative_patcher_x86_test.cc
+++ b/compiler/linker/x86/relative_patcher_x86_test.cc
@@ -101,5 +101,35 @@
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
+TEST_F(X86RelativePatcherTest, DexCacheReference) {
+ dex_cache_arrays_begin_ = 0x12345678;
+ constexpr size_t kElementOffset = 0x1234;
+ static const uint8_t raw_code[] = {
+ 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
+ 0x5b, // pop ebx
+ 0x8b, 0x83, 0x00, 0x01, 0x00, 0x00, // mov eax, [ebx + 256 (kDummy32BitValue)]
+ };
+ constexpr uint32_t anchor_offset = 5u; // After call +0.
+ ArrayRef<const uint8_t> code(raw_code);
+ LinkerPatch patches[] = {
+ LinkerPatch::DexCacheArrayPatch(code.size() - 4u, nullptr, anchor_offset, kElementOffset),
+ };
+ AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
+ ASSERT_TRUE(result.first);
+ uint32_t diff =
+ dex_cache_arrays_begin_ + kElementOffset - (result.second + anchor_offset);
+ static const uint8_t expected_code[] = {
+ 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
+ 0x5b, // pop ebx
+ 0x8b, 0x83, // mov eax, [ebx + diff]
+ static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8),
+ static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24)
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
} // namespace linker
} // namespace art