ART: Allow method references across oat files for multi-image, 2nd attempt.
These were disabled because we didn't have sufficient
information about the multi-image layout when processing
link-time patches in OatWriter. This CL refactors the
ELF file creation so that the information is available.
Also clean up ImageWriter to use oat file indexes instead
of filenames and avoid reopening the oat file to retrieve
the checksum.
Change-Id: Icc7b528deca29da1e473c8f079521a36d6c4892f
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index 73b0fac..682b008 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -40,6 +40,11 @@
MethodReference(nullptr, 0u),
aligned_offset);
if (needs_thunk) {
+ // All remaining patches will be handled by this thunk.
+ DCHECK(!unprocessed_patches_.empty());
+ DCHECK_LE(aligned_offset - unprocessed_patches_.front().second, max_positive_displacement_);
+ unprocessed_patches_.clear();
+
thunk_locations_.push_back(aligned_offset);
offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
}
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
index f80dd96..25fd35e 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.h
+++ b/compiler/linker/arm/relative_patcher_arm_base.h
@@ -27,18 +27,23 @@
class ArmBaseRelativePatcher : public RelativePatcher {
public:
- uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method,
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
protected:
ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
- InstructionSet instruction_set, std::vector<uint8_t> thunk_code,
- uint32_t max_positive_displacement, uint32_t max_negative_displacement);
+ InstructionSet instruction_set,
+ std::vector<uint8_t> thunk_code,
+ uint32_t max_positive_displacement,
+ uint32_t max_negative_displacement);
- uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method,
- MethodReference method_ref, uint32_t max_extra_space);
+ uint32_t ReserveSpaceInternal(uint32_t offset,
+ const CompiledMethod* compiled_method,
+ MethodReference method_ref,
+ uint32_t max_extra_space);
uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset);
private:
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 5f4f760..c090dff 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -28,8 +28,10 @@
kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
}
-void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) {
+void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(literal_offset + 4u, code->size());
DCHECK_EQ(literal_offset & 1u, 0u);
DCHECK_EQ(patch_offset & 1u, 0u);
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 006d6fb..0d903c0 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -26,10 +26,14 @@
public:
explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider);
- void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
private:
static std::vector<uint8_t> CompileThunkCode();
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 3d4c218..a81c85c 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -131,8 +131,10 @@
return ArmBaseRelativePatcher::WriteThunks(out, offset);
}
-void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) {
+void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset, uint32_t
+ target_offset) {
DCHECK_LE(literal_offset + 4u, code->size());
DCHECK_EQ(literal_offset & 3u, 0u);
DCHECK_EQ(patch_offset & 3u, 0u);
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
index 2d07e75..f9b76e6 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ b/compiler/linker/arm64/relative_patcher_arm64.h
@@ -28,14 +28,19 @@
Arm64RelativePatcher(RelativePatcherTargetProvider* provider,
const Arm64InstructionSetFeatures* features);
- uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method,
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
private:
static std::vector<uint8_t> CompileThunkCode();
diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc
new file mode 100644
index 0000000..e9e242b
--- /dev/null
+++ b/compiler/linker/multi_oat_relative_patcher.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "multi_oat_relative_patcher.h"
+
+#include "globals.h"
+#include "base/bit_utils.h"
+#include "base/logging.h"
+
+namespace art {
+namespace linker {
+
+MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set,
+ const InstructionSetFeatures* features)
+ : method_offset_map_(),
+ relative_patcher_(
+ linker::RelativePatcher::Create(instruction_set, features, &method_offset_map_)),
+ adjustment_(0u),
+ instruction_set_(instruction_set),
+ start_size_code_alignment_(0u),
+ start_size_relative_call_thunks_(0u),
+ start_size_misc_thunks_(0u) {
+}
+
+void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) {
+ DCHECK_ALIGNED(adjustment, kPageSize);
+ adjustment_ = adjustment;
+
+ start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize();
+ start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize();
+ start_size_misc_thunks_ = relative_patcher_->MiscThunksSize();
+}
+
+uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const {
+ DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_);
+ return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_;
+}
+
+uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const {
+ DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_);
+ return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_;
+}
+
+uint32_t MultiOatRelativePatcher::MiscThunksSize() const {
+ DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_);
+ return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_;
+}
+
+std::pair<bool, uint32_t> MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset(
+ MethodReference ref) {
+ auto it = map.find(ref);
+ if (it == map.end()) {
+ return std::pair<bool, uint32_t>(false, 0u);
+ } else {
+ return std::pair<bool, uint32_t>(true, it->second);
+ }
+}
+} // namespace linker
+} // namespace art
diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
new file mode 100644
index 0000000..1727d52
--- /dev/null
+++ b/compiler/linker/multi_oat_relative_patcher.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
+#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
+
+#include "arch/instruction_set.h"
+#include "method_reference.h"
+#include "relative_patcher.h"
+#include "safe_map.h"
+
+namespace art {
+
+class CompiledMethod;
+class LinkerPatch;
+class InstructionSetFeatures;
+
+namespace linker {
+
+// MultiOatRelativePatcher is a helper class for handling patching across
+// any number of oat files. It provides storage for method code offsets
+// and wraps RelativePatcher calls, adjusting relative offsets according
+// to the value set by SetAdjustment().
+class MultiOatRelativePatcher FINAL {
+ public:
+ using const_iterator =
+ SafeMap<MethodReference, uint32_t, MethodReferenceComparator>::const_iterator;
+
+ MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features);
+
+ // Mark the start of a new oat file (for statistics retrieval) and set the
+ // adjustment for a new oat file to apply to all relative offsets that are
+ // passed to the MultiOatRelativePatcher.
+ //
+ // The adjustment should be the global offset of the base from which relative
+ // offsets are calculated, such as the start of .rodata for the current oat file.
+ // It must must never point directly to a method's code to avoid relative offsets
+ // with value 0 because this value is used as a missing offset indication in
+ // GetOffset() and an error indication in WriteThunks(). Additionally, it must be
+ // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP.
+ void StartOatFile(uint32_t adjustment);
+
+ // Get relative offset. Returns 0 when the offset has not been set yet.
+ uint32_t GetOffset(MethodReference method_ref) {
+ auto it = method_offset_map_.map.find(method_ref);
+ return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u;
+ }
+
+ // Set the offset.
+ void SetOffset(MethodReference method_ref, uint32_t offset) {
+ method_offset_map_.map.Put(method_ref, offset + adjustment_);
+ }
+
+ // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment.
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
+ MethodReference method_ref) {
+ offset += adjustment_;
+ offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref);
+ offset -= adjustment_;
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment.
+ uint32_t ReserveSpaceEnd(uint32_t offset) {
+ offset += adjustment_;
+ offset = relative_patcher_->ReserveSpaceEnd(offset);
+ offset -= adjustment_;
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment.
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) {
+ offset += adjustment_;
+ offset = relative_patcher_->WriteThunks(out, offset);
+ if (offset != 0u) { // 0u indicates write error.
+ offset -= adjustment_;
+ }
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment.
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ patch_offset += adjustment_;
+ target_offset += adjustment_;
+ relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset);
+ }
+
+ // Wrapper around RelativePatcher::PatchDexCacheReference(), doing offset adjustment.
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ patch_offset += adjustment_;
+ target_offset += adjustment_;
+ relative_patcher_->PatchDexCacheReference(code, patch, patch_offset, target_offset);
+ }
+
+ // Wrappers around RelativePatcher for statistics retrieval.
+ uint32_t CodeAlignmentSize() const;
+ uint32_t RelativeCallThunksSize() const;
+ uint32_t MiscThunksSize() const;
+
+ private:
+ // Map method reference to assigned offset.
+ // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
+ class MethodOffsetMap : public linker::RelativePatcherTargetProvider {
+ public:
+ std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE;
+ SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map;
+ };
+
+ MethodOffsetMap method_offset_map_;
+ std::unique_ptr<RelativePatcher> relative_patcher_;
+ uint32_t adjustment_;
+ InstructionSet instruction_set_;
+
+ uint32_t start_size_code_alignment_;
+ uint32_t start_size_relative_call_thunks_;
+ uint32_t start_size_misc_thunks_;
+
+ friend class MultiOatRelativePatcherTest;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
new file mode 100644
index 0000000..792cdfe
--- /dev/null
+++ b/compiler/linker/multi_oat_relative_patcher_test.cc
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiled_method.h"
+#include "gtest/gtest.h"
+#include "multi_oat_relative_patcher.h"
+#include "vector_output_stream.h"
+
+namespace art {
+namespace linker {
+
+static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
+
+static bool EqualRef(MethodReference lhs, MethodReference rhs) {
+ return lhs.dex_file == rhs.dex_file && lhs.dex_method_index == rhs.dex_method_index;
+}
+
+class MultiOatRelativePatcherTest : public testing::Test {
+ protected:
+ class MockPatcher : public RelativePatcher {
+ public:
+ MockPatcher() { }
+
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
+ MethodReference method_ref) OVERRIDE {
+ last_reserve_offset_ = offset;
+ last_reserve_method_ = method_ref;
+ offset += next_reserve_adjustment_;
+ next_reserve_adjustment_ = 0u;
+ return offset;
+ }
+
+ uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE {
+ last_reserve_offset_ = offset;
+ last_reserve_method_ = kNullMethodRef;
+ offset += next_reserve_adjustment_;
+ next_reserve_adjustment_ = 0u;
+ return offset;
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ last_write_offset_ = offset;
+ if (next_write_alignment_ != 0u) {
+ offset += next_write_alignment_;
+ bool success = WriteCodeAlignment(out, next_write_alignment_);
+ CHECK(success);
+ next_write_alignment_ = 0u;
+ }
+ if (next_write_call_thunk_ != 0u) {
+ offset += next_write_call_thunk_;
+ std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
+ bool success = WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk));
+ CHECK(success);
+ next_write_call_thunk_ = 0u;
+ }
+ if (next_write_misc_thunk_ != 0u) {
+ offset += next_write_misc_thunk_;
+ std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm');
+ bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk));
+ CHECK(success);
+ next_write_misc_thunk_ = 0u;
+ }
+ return offset;
+ }
+
+ void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ last_literal_offset_ = literal_offset;
+ last_patch_offset_ = patch_offset;
+ last_target_offset_ = target_offset;
+ }
+
+ void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ last_literal_offset_ = patch.LiteralOffset();
+ last_patch_offset_ = patch_offset;
+ last_target_offset_ = target_offset;
+ }
+
+ uint32_t last_reserve_offset_ = 0u;
+ MethodReference last_reserve_method_ = kNullMethodRef;
+ uint32_t next_reserve_adjustment_ = 0u;
+
+ uint32_t last_write_offset_ = 0u;
+ uint32_t next_write_alignment_ = 0u;
+ uint32_t next_write_call_thunk_ = 0u;
+ uint32_t next_write_misc_thunk_ = 0u;
+
+ uint32_t last_literal_offset_ = 0u;
+ uint32_t last_patch_offset_ = 0u;
+ uint32_t last_target_offset_ = 0u;
+ };
+
+ MultiOatRelativePatcherTest()
+ : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
+ patcher_(kRuntimeISA, instruction_set_features_.get()) {
+ std::unique_ptr<MockPatcher> mock(new MockPatcher());
+ mock_ = mock.get();
+ patcher_.relative_patcher_ = std::move(mock);
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+ MultiOatRelativePatcher patcher_;
+ MockPatcher* mock_;
+};
+
+TEST_F(MultiOatRelativePatcherTest, Offsets) {
+ const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
+ MethodReference ref1(dex_file, 1u);
+ MethodReference ref2(dex_file, 2u);
+ EXPECT_EQ(0u, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+ EXPECT_EQ(0u, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t off1 = 0x1234;
+ patcher_.SetOffset(ref1, off1);
+ EXPECT_EQ(off1, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment2 = 0x30000;
+ patcher_.StartOatFile(adjustment2);
+ EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t off2 = 0x4321;
+ patcher_.SetOffset(ref2, off2);
+ EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
+ EXPECT_EQ(off2, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment3 = 0x78000;
+ patcher_.StartOatFile(adjustment3);
+ EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
+ EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
+}
+
+TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
+ const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
+ MethodReference ref1(dex_file, 1u);
+ MethodReference ref2(dex_file, 2u);
+ MethodReference ref3(dex_file, 3u);
+ const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_offset = 0x100;
+ uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
+ ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(ref1, mock_->last_reserve_method_));
+ ASSERT_EQ(method1_offset, method1_offset_check);
+
+ uint32_t method2_offset = 0x1230;
+ uint32_t method2_reserve_adjustment = 0x10;
+ mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
+ uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
+ ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(ref2, mock_->last_reserve_method_));
+ ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
+
+ uint32_t end1_offset = 0x4320;
+ uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
+ ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_));
+ ASSERT_EQ(end1_offset, end1_offset_check);
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_offset = 0xf00;
+ uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
+ ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(ref3, mock_->last_reserve_method_));
+ ASSERT_EQ(method3_offset, method3_offset_check);
+
+ uint32_t end2_offset = 0x2400;
+ uint32_t end2_reserve_adjustment = 0x20;
+ mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
+ uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
+ ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_));
+ ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
+}
+
+TEST_F(MultiOatRelativePatcherTest, Write) {
+ std::vector<uint8_t> output;
+ VectorOutputStream vos("output", &output);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_offset = 0x100;
+ uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
+ ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method1_offset, method1_offset_check);
+ vos.WriteFully("1", 1); // Mark method1.
+
+ uint32_t method2_offset = 0x1230;
+ uint32_t method2_alignment_size = 1;
+ uint32_t method2_call_thunk_size = 2;
+ mock_->next_write_alignment_ = method2_alignment_size;
+ mock_->next_write_call_thunk_ = method2_call_thunk_size;
+ uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
+ ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
+ method2_offset_adjusted);
+ vos.WriteFully("2", 1); // Mark method2.
+
+ EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
+ EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_offset = 0xf00;
+ uint32_t method3_alignment_size = 2;
+ uint32_t method3_misc_thunk_size = 1;
+ mock_->next_write_alignment_ = method3_alignment_size;
+ mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
+ uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
+ ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
+ method3_offset_adjusted);
+ vos.WriteFully("3", 1); // Mark method3.
+
+ EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
+ EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
+
+ uint8_t expected_output[] = {
+ '1',
+ 0, 'c', 'c', '2',
+ 0, 0, 'm', '3',
+ };
+ ASSERT_EQ(arraysize(expected_output), output.size());
+ for (size_t i = 0; i != arraysize(expected_output); ++i) {
+ ASSERT_EQ(expected_output[i], output[i]) << i;
+ }
+}
+
+TEST_F(MultiOatRelativePatcherTest, Patch) {
+ std::vector<uint8_t> code(16);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_literal_offset = 4u;
+ uint32_t method1_patch_offset = 0x1234u;
+ uint32_t method1_target_offset = 0x8888u;
+ patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
+ DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
+ DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
+
+ uint32_t method2_literal_offset = 12u;
+ uint32_t method2_patch_offset = 0x7654u;
+ uint32_t method2_target_offset = 0xccccu;
+ LinkerPatch method2_patch =
+ LinkerPatch::DexCacheArrayPatch(method2_literal_offset, nullptr, 0u, 1234u);
+ patcher_.PatchDexCacheReference(
+ &code, method2_patch, method2_patch_offset, method2_target_offset);
+ DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
+ DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_literal_offset = 8u;
+ uint32_t method3_patch_offset = 0x108u;
+ uint32_t method3_target_offset = 0x200u;
+ patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
+ DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
+ DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
+}
+
+} // namespace linker
+} // namespace art
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index 82702dc..6727c17 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -34,7 +34,8 @@
namespace linker {
std::unique_ptr<RelativePatcher> RelativePatcher::Create(
- InstructionSet instruction_set, const InstructionSetFeatures* features,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
RelativePatcherTargetProvider* provider) {
class RelativePatcherNone FINAL : public RelativePatcher {
public:
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index 8a9f3f8..ba37451 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -83,23 +83,31 @@
}
// Reserve space for thunks if needed before a method, return adjusted offset.
- virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method,
+ virtual uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
MethodReference method_ref) = 0;
// Reserve space for thunks if needed after the last method, return adjusted offset.
+ // The caller may use this method to preemptively force thunk space reservation and
+ // then resume reservation for more methods. This is useful when there is a gap in
+ // the .text segment, for example when going to the next oat file for multi-image.
virtual uint32_t ReserveSpaceEnd(uint32_t offset) = 0;
- // Write relative call thunks if needed, return adjusted offset.
+ // Write relative call thunks if needed, return adjusted offset. Returns 0 on write failure.
virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0;
// Patch method code. The input displacement is relative to the patched location,
// the patcher may need to adjust it if the correct base is different.
- virtual void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) = 0;
+ virtual void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) = 0;
// Patch a reference to a dex cache location.
- virtual void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) = 0;
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) = 0;
protected:
RelativePatcher()
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index bf8e786..704135a 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -44,10 +44,22 @@
: compiler_options_(),
verification_results_(&compiler_options_),
inliner_map_(),
- driver_(&compiler_options_, &verification_results_, &inliner_map_,
- Compiler::kQuick, instruction_set, nullptr,
- false, nullptr, nullptr, nullptr, 1u,
- false, false, nullptr, -1, nullptr, nullptr),
+ driver_(&compiler_options_,
+ &verification_results_,
+ &inliner_map_,
+ Compiler::kQuick,
+ instruction_set,
+ /* instruction_set_features*/ nullptr,
+ /* boot_image */ false,
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 1u,
+ /* dump_stats */ false,
+ /* dump_passes */ false,
+ /* timer */ nullptr,
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr),
error_msg_(),
instruction_set_(instruction_set),
features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
@@ -138,8 +150,10 @@
offset + patch.LiteralOffset(), target_offset);
} else if (patch.Type() == kLinkerPatchDexCacheArray) {
uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset();
- patcher_->PatchDexCacheReference(&patched_code_, patch,
- offset + patch.LiteralOffset(), target_offset);
+ patcher_->PatchDexCacheReference(&patched_code_,
+ patch,
+ offset + patch.LiteralOffset(),
+ target_offset);
} else {
LOG(FATAL) << "Bad patch type.";
}
diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h
index 0c881f0..ddc244c 100644
--- a/compiler/linker/x86/relative_patcher_x86.h
+++ b/compiler/linker/x86/relative_patcher_x86.h
@@ -26,8 +26,10 @@
public:
X86RelativePatcher() { }
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
};
} // namespace linker
diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/compiler/linker/x86/relative_patcher_x86_base.cc
index bc285a7..bf3a648 100644
--- a/compiler/linker/x86/relative_patcher_x86_base.cc
+++ b/compiler/linker/x86/relative_patcher_x86_base.cc
@@ -34,8 +34,10 @@
return offset; // No thunks added; no limit on relative call distance.
}
-void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) {
+void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(literal_offset + 4u, code->size());
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/compiler/linker/x86/relative_patcher_x86_base.h
index 9200709..ca83a72 100644
--- a/compiler/linker/x86/relative_patcher_x86_base.h
+++ b/compiler/linker/x86/relative_patcher_x86_base.h
@@ -29,8 +29,10 @@
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
protected:
X86BaseRelativePatcher() { }
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc
index 598f3ac..e571f50 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc
@@ -23,7 +23,8 @@
void X86_64RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) {
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(patch.LiteralOffset() + 4u, code->size());
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h
index af687b4..feecb3a 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.h
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.h
@@ -26,8 +26,10 @@
public:
X86_64RelativePatcher() { }
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
};
} // namespace linker